Esempio n. 1
0
// 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
}
Esempio n. 2
0
// 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
}