storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/http/listener.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2017 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package http 18 19 import ( 20 "fmt" 21 "io" 22 "net" 23 "os" 24 "sync" 25 "syscall" 26 ) 27 28 type acceptResult struct { 29 conn net.Conn 30 err error 31 } 32 33 // httpListener - HTTP listener capable of handling multiple server addresses. 34 type httpListener struct { 35 mutex sync.Mutex // to guard Close() method. 36 tcpListeners []*net.TCPListener // underlaying TCP listeners. 37 acceptCh chan acceptResult // channel where all TCP listeners write accepted connection. 38 doneCh chan struct{} // done channel for TCP listener goroutines. 39 } 40 41 // isRoutineNetErr returns true if error is due to a network timeout, 42 // connect reset or io.EOF and false otherwise 43 func isRoutineNetErr(err error) bool { 44 if err == nil { 45 return false 46 } 47 if nErr, ok := err.(*net.OpError); ok { 48 // Check if the error is a tcp connection reset 49 if syscallErr, ok := nErr.Err.(*os.SyscallError); ok { 50 if errno, ok := syscallErr.Err.(syscall.Errno); ok { 51 return errno == syscall.ECONNRESET 52 } 53 } 54 // Check if the error is a timeout 55 return nErr.Timeout() 56 } 57 // check for io.EOF and also some times io.EOF is wrapped is another error type. 58 return err == io.EOF || err.Error() == "EOF" 59 } 60 61 // start - starts separate goroutine for each TCP listener. A valid new connection is passed to httpListener.acceptCh. 62 func (listener *httpListener) start() { 63 listener.acceptCh = make(chan acceptResult) 64 listener.doneCh = make(chan struct{}) 65 66 // Closure to send acceptResult to acceptCh. 67 // It returns true if the result is sent else false if returns when doneCh is closed. 68 send := func(result acceptResult, doneCh <-chan struct{}) bool { 69 select { 70 case listener.acceptCh <- result: 71 // Successfully written to acceptCh 72 return true 73 case <-doneCh: 74 // As stop signal is received, close accepted connection. 75 if result.conn != nil { 76 result.conn.Close() 77 } 78 return false 79 } 80 } 81 82 // Closure to handle single connection. 83 handleConn := func(tcpConn *net.TCPConn, doneCh <-chan struct{}) { 84 tcpConn.SetKeepAlive(true) 85 send(acceptResult{tcpConn, nil}, doneCh) 86 } 87 88 // Closure to handle TCPListener until done channel is closed. 89 handleListener := func(tcpListener *net.TCPListener, doneCh <-chan struct{}) { 90 for { 91 tcpConn, err := tcpListener.AcceptTCP() 92 if err != nil { 93 // Returns when send fails. 94 if !send(acceptResult{nil, err}, doneCh) { 95 return 96 } 97 } else { 98 go handleConn(tcpConn, doneCh) 99 } 100 } 101 } 102 103 // Start separate goroutine for each TCP listener to handle connection. 104 for _, tcpListener := range listener.tcpListeners { 105 go handleListener(tcpListener, listener.doneCh) 106 } 107 } 108 109 // Accept - reads from httpListener.acceptCh for one of previously accepted TCP connection and returns the same. 110 func (listener *httpListener) Accept() (conn net.Conn, err error) { 111 result, ok := <-listener.acceptCh 112 if ok { 113 return result.conn, result.err 114 } 115 116 return nil, syscall.EINVAL 117 } 118 119 // Close - closes underneath all TCP listeners. 120 func (listener *httpListener) Close() (err error) { 121 listener.mutex.Lock() 122 defer listener.mutex.Unlock() 123 if listener.doneCh == nil { 124 return syscall.EINVAL 125 } 126 127 for i := range listener.tcpListeners { 128 listener.tcpListeners[i].Close() 129 } 130 close(listener.doneCh) 131 132 listener.doneCh = nil 133 return nil 134 } 135 136 // Addr - net.Listener interface compatible method returns net.Addr. In case of multiple TCP listeners, it returns '0.0.0.0' as IP address. 137 func (listener *httpListener) Addr() (addr net.Addr) { 138 addr = listener.tcpListeners[0].Addr() 139 if len(listener.tcpListeners) == 1 { 140 return addr 141 } 142 143 tcpAddr := addr.(*net.TCPAddr) 144 if ip := net.ParseIP("0.0.0.0"); ip != nil { 145 tcpAddr.IP = ip 146 } 147 148 addr = tcpAddr 149 return addr 150 } 151 152 // Addrs - returns all address information of TCP listeners. 153 func (listener *httpListener) Addrs() (addrs []net.Addr) { 154 for i := range listener.tcpListeners { 155 addrs = append(addrs, listener.tcpListeners[i].Addr()) 156 } 157 158 return addrs 159 } 160 161 // newHTTPListener - creates new httpListener object which is interface compatible to net.Listener. 162 // httpListener is capable to 163 // * listen to multiple addresses 164 // * controls incoming connections only doing HTTP protocol 165 func newHTTPListener(serverAddrs []string) (listener *httpListener, err error) { 166 167 var tcpListeners []*net.TCPListener 168 169 // Close all opened listeners on error 170 defer func() { 171 if err == nil { 172 return 173 } 174 175 for _, tcpListener := range tcpListeners { 176 // Ignore error on close. 177 tcpListener.Close() 178 } 179 }() 180 181 for _, serverAddr := range serverAddrs { 182 var l net.Listener 183 if l, err = listen("tcp", serverAddr); err != nil { 184 if l, err = fallbackListen("tcp", serverAddr); err != nil { 185 return nil, err 186 } 187 } 188 189 tcpListener, ok := l.(*net.TCPListener) 190 if !ok { 191 return nil, fmt.Errorf("unexpected listener type found %v, expected net.TCPListener", l) 192 } 193 194 tcpListeners = append(tcpListeners, tcpListener) 195 } 196 197 listener = &httpListener{ 198 tcpListeners: tcpListeners, 199 } 200 listener.start() 201 202 return listener, nil 203 }