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  }