Something went wrong. Try again.
sloproxy
Something went wrong. Try again.
3.3 kB · 125 lines
Go
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126package api
import ( "bufio" "crypto/tls" "errors" "net" "net/http" "strings" "time"
log "github.com/sirupsen/logrus")
func normalizeHTTPServeError(err error) error { if err == nil { return nil } if errors.Is(err, net.ErrClosed) { return nil } if errors.Is(err, http.ErrServerClosed) { return nil } return err}
func normalizeListenerError(err error) error { if err == nil { return nil } if errors.Is(err, net.ErrClosed) { return nil } return err}
func (s *Server) acceptMuxConnections(listener net.Listener, httpListener *muxListener) error { if s == nil || listener == nil { return net.ErrClosed }
for { conn, errAccept := listener.Accept() if errAccept != nil { return errAccept } if conn == nil { continue }
// Dispatch each connection to a goroutine so that slow/idle clients // cannot block the accept loop. Previously, TLS handshake and // reader.Peek(1) were performed inline; an idle TCP connection that // never sent bytes would block Peek indefinitely, preventing all // subsequent connections from being accepted (issue #3267). go s.routeMuxConnection(conn, httpListener) }}
// routeMuxConnection performs per-connection protocol detection and routing.func (s *Server) routeMuxConnection(conn net.Conn, httpListener *muxListener) { // Set a read deadline so that idle connections that never send bytes do not // leak goroutines and file descriptors. The deadline is cleared once the // connection is successfully routed to its handler. const muxSniffDeadline = 10 * time.Second _ = conn.SetReadDeadline(time.Now().Add(muxSniffDeadline))
tlsConn, ok := conn.(*tls.Conn) if ok { if errHandshake := tlsConn.Handshake(); errHandshake != nil { if errClose := conn.Close(); errClose != nil { log.Errorf("failed to close connection after TLS handshake error: %v", errClose) } return } proto := strings.TrimSpace(tlsConn.ConnectionState().NegotiatedProtocol) if proto == "h2" || proto == "http/1.1" { if httpListener == nil { if errClose := conn.Close(); errClose != nil { log.Errorf("failed to close connection: %v", errClose) } return } if errPut := httpListener.Put(tlsConn); errPut != nil { if errClose := conn.Close(); errClose != nil { log.Errorf("failed to close connection after HTTP routing failure: %v", errClose) } } else { _ = conn.SetReadDeadline(time.Time{}) } return } }
reader := bufio.NewReader(conn) prefix, errPeek := reader.Peek(1) if errPeek != nil { if errClose := conn.Close(); errClose != nil { log.Errorf("failed to close connection after protocol peek failure: %v", errClose) } return }
if isRedisRESPPrefix(prefix[0]) { _ = conn.SetReadDeadline(time.Time{}) s.handleRedisConnection(conn, reader) return }
if httpListener == nil { if errClose := conn.Close(); errClose != nil { log.Errorf("failed to close connection without HTTP listener: %v", errClose) } return }
if errPut := httpListener.Put(&bufferedConn{Conn: conn, reader: reader}); errPut != nil { if errClose := conn.Close(); errClose != nil { log.Errorf("failed to close connection after HTTP routing failure: %v", errClose) } } else { _ = conn.SetReadDeadline(time.Time{}) }}