github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/netutil/net.go (about) 1 // Copyright 2014 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package netutil 12 13 import ( 14 "context" 15 "crypto/tls" 16 "fmt" 17 "io" 18 "net" 19 "net/http" 20 "strings" 21 "time" 22 23 "github.com/cockroachdb/cmux" 24 "github.com/cockroachdb/cockroach/pkg/util/log" 25 "github.com/cockroachdb/cockroach/pkg/util/stop" 26 "github.com/cockroachdb/cockroach/pkg/util/syncutil" 27 "github.com/cockroachdb/errors" 28 "golang.org/x/net/http2" 29 "google.golang.org/grpc" 30 ) 31 32 // ListenAndServeGRPC creates a listener and serves the specified grpc Server 33 // on it, closing the listener when signaled by the stopper. 34 func ListenAndServeGRPC( 35 stopper *stop.Stopper, server *grpc.Server, addr net.Addr, 36 ) (net.Listener, error) { 37 ln, err := net.Listen(addr.Network(), addr.String()) 38 if err != nil { 39 return ln, err 40 } 41 42 ctx := context.TODO() 43 44 stopper.RunWorker(ctx, func(context.Context) { 45 <-stopper.ShouldQuiesce() 46 FatalIfUnexpected(ln.Close()) 47 <-stopper.ShouldStop() 48 server.Stop() 49 }) 50 51 stopper.RunWorker(ctx, func(context.Context) { 52 FatalIfUnexpected(server.Serve(ln)) 53 }) 54 return ln, nil 55 } 56 57 var httpLogger = log.NewStdLogger(log.Severity_ERROR, "httpLogger") 58 59 // Server is a thin wrapper around http.Server. See MakeServer for more detail. 60 type Server struct { 61 *http.Server 62 } 63 64 // MakeServer constructs a Server that tracks active connections, 65 // closing them when signaled by stopper. 66 // 67 // It can serve two different purposes simultaneously: 68 // 69 // - to serve as actual HTTP server, using the .Serve(net.Listener) method. 70 // - to serve as plain TCP server, using the .ServeWith(...) method. 71 // 72 // The latter is used e.g. to accept SQL client connections. 73 // 74 // When the HTTP facility is not used, the Go HTTP server object is 75 // still used internally to maintain/register the connections via the 76 // ConnState() method, for convenience. 77 func MakeServer(stopper *stop.Stopper, tlsConfig *tls.Config, handler http.Handler) Server { 78 var mu syncutil.Mutex 79 activeConns := make(map[net.Conn]struct{}) 80 server := Server{ 81 Server: &http.Server{ 82 Handler: handler, 83 TLSConfig: tlsConfig, 84 ConnState: func(conn net.Conn, state http.ConnState) { 85 mu.Lock() 86 switch state { 87 case http.StateNew: 88 activeConns[conn] = struct{}{} 89 case http.StateClosed: 90 delete(activeConns, conn) 91 } 92 mu.Unlock() 93 }, 94 ErrorLog: httpLogger, 95 }, 96 } 97 98 ctx := context.TODO() 99 100 // net/http.(*Server).Serve/http2.ConfigureServer are not thread safe with 101 // respect to net/http.(*Server).TLSConfig, so we call it synchronously here. 102 if err := http2.ConfigureServer(server.Server, nil); err != nil { 103 log.Fatalf(ctx, "%v", err) 104 } 105 106 stopper.RunWorker(ctx, func(context.Context) { 107 <-stopper.ShouldStop() 108 109 mu.Lock() 110 for conn := range activeConns { 111 conn.Close() 112 } 113 mu.Unlock() 114 }) 115 116 return server 117 } 118 119 // ServeWith accepts connections on ln and serves them using serveConn. 120 func (s *Server) ServeWith( 121 ctx context.Context, stopper *stop.Stopper, l net.Listener, serveConn func(net.Conn), 122 ) error { 123 // Inspired by net/http.(*Server).Serve 124 var tempDelay time.Duration // how long to sleep on accept failure 125 for { 126 rw, e := l.Accept() 127 if e != nil { 128 if ne := (net.Error)(nil); errors.As(e, &ne) && ne.Temporary() { 129 if tempDelay == 0 { 130 tempDelay = 5 * time.Millisecond 131 } else { 132 tempDelay *= 2 133 } 134 if max := 1 * time.Second; tempDelay > max { 135 tempDelay = max 136 } 137 httpLogger.Printf("http: Accept error: %v; retrying in %v", e, tempDelay) 138 time.Sleep(tempDelay) 139 continue 140 } 141 return e 142 } 143 tempDelay = 0 144 go func() { 145 defer stopper.Recover(ctx) 146 s.Server.ConnState(rw, http.StateNew) // before Serve can return 147 serveConn(rw) 148 s.Server.ConnState(rw, http.StateClosed) 149 }() 150 } 151 } 152 153 // IsClosedConnection returns true if err is cmux.ErrListenerClosed, 154 // grpc.ErrServerStopped, io.EOF, or the net package's errClosed. 155 func IsClosedConnection(err error) bool { 156 return errors.IsAny(err, cmux.ErrListenerClosed, grpc.ErrServerStopped, io.EOF) || 157 strings.Contains(err.Error(), "use of closed network connection") 158 } 159 160 // FatalIfUnexpected calls Log.Fatal(err) unless err is nil, 161 // cmux.ErrListenerClosed, or the net package's errClosed. 162 func FatalIfUnexpected(err error) { 163 if err != nil && !IsClosedConnection(err) { 164 log.Fatalf(context.TODO(), "%+v", err) 165 } 166 } 167 168 // InitialHeartbeatFailedError indicates that while attempting a GRPC 169 // connection to a node, we aren't successful and have never seen a 170 // heartbeat over that connection before. 171 type InitialHeartbeatFailedError struct { 172 WrappedErr error 173 } 174 175 var _ error = (*InitialHeartbeatFailedError)(nil) 176 var _ fmt.Formatter = (*InitialHeartbeatFailedError)(nil) 177 var _ errors.Formatter = (*InitialHeartbeatFailedError)(nil) 178 179 // Error implements error. 180 func (e *InitialHeartbeatFailedError) Error() string { return fmt.Sprintf("%v", e) } 181 182 // Cause implements causer. 183 func (e *InitialHeartbeatFailedError) Cause() error { return e.WrappedErr } 184 185 // Format implements fmt.Formatter. 186 func (e *InitialHeartbeatFailedError) Format(s fmt.State, verb rune) { errors.FormatError(e, s, verb) } 187 188 // FormatError implements errors.FormatError. 189 func (e *InitialHeartbeatFailedError) FormatError(p errors.Printer) error { 190 p.Print("initial connection heartbeat failed") 191 return e.WrappedErr 192 } 193 194 // NewInitialHeartBeatFailedError creates a new InitialHeartbeatFailedError. 195 func NewInitialHeartBeatFailedError(cause error) *InitialHeartbeatFailedError { 196 return &InitialHeartbeatFailedError{ 197 WrappedErr: cause, 198 } 199 }