// ListenAndServe creates a listener and serves handler on it, closing the // listener when signalled by the stopper. The handling server implements HTTP1 // and HTTP2, with or without TLS. Note that the "real" server also implements // the postgres wire protocol, and so does not use this function, but the // pattern used is similar; that implementation is in server/server.go. func ListenAndServe(stopper *stop.Stopper, handler http.Handler, addr net.Addr, tlsConfig *tls.Config) (net.Listener, error) { ln, err := net.Listen(addr.Network(), addr.String()) if err != nil { return ln, err } stopper.RunWorker(func() { <-stopper.ShouldDrain() // Some unit tests manually close `ln`, so it may already be closed // when we get here. FatalIfUnexpected(ln.Close()) }) if tlsConfig != nil { // We're in TLS mode. ALPN will be used to automatically handle HTTP1 and // HTTP2 requests. ServeHandler(stopper, handler, tls.NewListener(ln, tlsConfig), tlsConfig) } else { // We're not in TLS mode. We're going to implement h2c (HTTP2 Clear Text) // ourselves. m := cmux.New(ln) // HTTP2 connections are easy to identify because they have a common // preface. h2L := m.Match(cmux.HTTP2()) // All other connections will get the default treatment. anyL := m.Match(cmux.Any()) // Construct our h2c handler function. var h2 http2.Server serveConnOpts := &http2.ServeConnOpts{ Handler: handler, } serveH2 := func(conn net.Conn) { h2.ServeConn(conn, serveConnOpts) } // Start serving HTTP1 on all non-HTTP2 connections. serveConn := ServeHandler(stopper, handler, anyL, tlsConfig) // Start serving h2c on all HTTP2 connections. stopper.RunWorker(func() { FatalIfUnexpected(serveConn(h2L, serveH2)) }) // Finally start the multiplexing listener. stopper.RunWorker(func() { FatalIfUnexpected(m.Serve()) }) } return ln, nil }
// Start starts the server on the specified port, starts gossip and // initializes the node using the engines from the server's context. func (s *Server) Start() error { s.initHTTP() tlsConfig, err := s.ctx.GetServerTLSConfig() if err != nil { return err } // The following code is a specialization of util/net.go's ListenAndServe // which adds pgwire support. A single port is used to serve all protocols // (pg, http, h2) via the following construction: // // non-TLS case: // net.Listen -> cmux.New -> pgwire.Match -> pgwire.Server.ServeConn // | // - -> cmux.HTTP2 -> http2.(*Server).ServeConn // - -> cmux.Any -> http.(*Server).Serve // // TLS case: // net.Listen -> cmux.New -> pgwire.Match -> pgwire.Server.ServeConn // | // - -> cmux.Any -> tls.NewListener -> http.(*Server).Serve // // Note that the difference between the TLS and non-TLS cases exists due to // Go's lack of an h2c (HTTP2 Clear Text) implementation. See inline comments // in util.ListenAndServe for an explanation of how h2c is implemented there // and here. ln, err := net.Listen("tcp", s.ctx.Addr) if err != nil { return err } unresolvedAddr, err := officialAddr(s.ctx.Addr, ln.Addr()) if err != nil { return err } s.ctx.Addr = unresolvedAddr.String() s.stopper.RunWorker(func() { <-s.stopper.ShouldDrain() if err := ln.Close(); err != nil { log.Fatal(err) } }) m := cmux.New(ln) pgL := m.Match(pgwire.Match) var serveConn func(net.Listener, func(net.Conn)) error if tlsConfig != nil { anyL := m.Match(cmux.Any()) serveConn = util.ServeHandler(s.stopper, s, tls.NewListener(anyL, tlsConfig), tlsConfig) } else { h2L := m.Match(cmux.HTTP2()) anyL := m.Match(cmux.Any()) var h2 http2.Server serveConnOpts := &http2.ServeConnOpts{ Handler: s, } serveH2 := func(conn net.Conn) { h2.ServeConn(conn, serveConnOpts) } serveConn = util.ServeHandler(s.stopper, s, anyL, tlsConfig) s.stopper.RunWorker(func() { util.FatalIfUnexpected(serveConn(h2L, serveH2)) }) } s.stopper.RunWorker(func() { util.FatalIfUnexpected(serveConn(pgL, func(conn net.Conn) { if err := s.pgServer.ServeConn(conn); err != nil && !util.IsClosedConnection(err) { log.Error(err) } })) }) s.stopper.RunWorker(func() { util.FatalIfUnexpected(m.Serve()) }) s.rpcContext.SetLocalServer(s.rpc, s.ctx.Addr) s.gossip.Start(s.grpc, unresolvedAddr) if err := s.node.start(s.rpc, unresolvedAddr, s.ctx.Engines, s.ctx.NodeAttributes); err != nil { return err } // Begin recording runtime statistics. runtime := status.NewRuntimeStatRecorder(s.node.Descriptor.NodeID, s.clock) s.tsDB.PollSource(runtime, s.ctx.MetricsFrequency, ts.Resolution10s, s.stopper) // Begin recording time series data collected by the status monitor. s.tsDB.PollSource(s.recorder, s.ctx.MetricsFrequency, ts.Resolution10s, s.stopper) // Begin recording status summaries. s.node.startWriteSummaries(s.ctx.MetricsFrequency) s.sqlExecutor.SetNodeID(s.node.Descriptor.NodeID) // Create and start the schema change manager only after a NodeID // has been assigned. s.schemaChangeManager = sql.NewSchemaChangeManager(*s.db, s.gossip, s.leaseMgr) s.schemaChangeManager.Start(s.stopper) log.Infof("starting %s/postgres server at %s", s.ctx.HTTPRequestScheme(), unresolvedAddr) return nil }