// serve accepts incoming connections on the listener l, // creating a new service goroutine for each. The service goroutines // read requests and then call handler to reply to them. func serve(sctx *serveCtx, s *etcdserver.EtcdServer, tlscfg *tls.Config, handler http.Handler) error { logger := defaultLog.New(ioutil.Discard, "etcdhttp", 0) <-s.ReadyNotify() plog.Info("ready to serve client requests") m := cmux.New(sctx.l) if sctx.insecure { gs := v3rpc.Server(s, nil) grpcl := m.Match(cmux.HTTP2()) go func() { plog.Fatal(gs.Serve(grpcl)) }() srvhttp := &http.Server{ Handler: handler, ErrorLog: logger, // do not log user error } httpl := m.Match(cmux.HTTP1()) go func() { plog.Fatal(srvhttp.Serve(httpl)) }() plog.Noticef("serving insecure client requests on %s, this is strongly discouraged!", sctx.host) } if sctx.secure { gs := v3rpc.Server(s, tlscfg) handler = grpcHandlerFunc(gs, handler) tlsl := tls.NewListener(m.Match(cmux.Any()), tlscfg) // TODO: add debug flag; enable logging when debug flag is set srv := &http.Server{ Handler: handler, TLSConfig: tlscfg, ErrorLog: logger, // do not log user error } go func() { plog.Fatal(srv.Serve(tlsl)) }() plog.Infof("serving client requests on %s", sctx.host) } return m.Serve() }
// serve accepts incoming connections on the listener l, // creating a new service goroutine for each. The service goroutines // read requests and then call handler to reply to them. func serve(sctx *serveCtx, s *etcdserver.EtcdServer, tlscfg *tls.Config, handler http.Handler) error { logger := defaultLog.New(ioutil.Discard, "etcdhttp", 0) <-s.ReadyNotify() plog.Info("ready to serve client requests") m := cmux.New(sctx.l) if sctx.insecure { gs := v3rpc.Server(s, nil) grpcl := m.Match(cmux.HTTP2()) go func() { plog.Fatal(gs.Serve(grpcl)) }() opts := []grpc.DialOption{ grpc.WithInsecure(), } gwmux, err := registerGateway(sctx.l.Addr().String(), opts) if err != nil { return err } httpmux := http.NewServeMux() httpmux.Handle("/v3alpha/", gwmux) httpmux.Handle("/", handler) srvhttp := &http.Server{ Handler: httpmux, ErrorLog: logger, // do not log user error } httpl := m.Match(cmux.HTTP1()) go func() { plog.Fatal(srvhttp.Serve(httpl)) }() plog.Noticef("serving insecure client requests on %s, this is strongly discouraged!", sctx.host) } if sctx.secure { gs := v3rpc.Server(s, tlscfg) handler = grpcHandlerFunc(gs, handler) dtls := *tlscfg // trust local server dtls.InsecureSkipVerify = true creds := credentials.NewTLS(&dtls) opts := []grpc.DialOption{grpc.WithTransportCredentials(creds)} gwmux, err := registerGateway(sctx.l.Addr().String(), opts) if err != nil { return err } tlsl := tls.NewListener(m.Match(cmux.Any()), tlscfg) // TODO: add debug flag; enable logging when debug flag is set httpmux := http.NewServeMux() httpmux.Handle("/v3alpha/", gwmux) httpmux.Handle("/", handler) srv := &http.Server{ Handler: httpmux, TLSConfig: tlscfg, ErrorLog: logger, // do not log user error } go func() { plog.Fatal(srv.Serve(tlsl)) }() plog.Infof("serving client requests on %s", sctx.host) } return m.Serve() }
// startEtcd launches the etcd server and HTTP handlers for client/server communication. func startEtcd(cfg *config) (<-chan struct{}, error) { urlsmap, token, err := getPeerURLsMapAndToken(cfg, "etcd") if err != nil { return nil, fmt.Errorf("error setting up initial cluster: %v", err) } if cfg.PeerAutoTLS && cfg.peerTLSInfo.Empty() { var phosts []string for _, u := range cfg.lpurls { phosts = append(phosts, u.Host) } cfg.peerTLSInfo, err = transport.SelfCert(path.Join(cfg.Dir, "fixtures/peer"), phosts) if err != nil { plog.Fatalf("could not get certs (%v)", err) } } else if cfg.PeerAutoTLS { plog.Warningf("ignoring peer auto TLS since certs given") } if !cfg.peerTLSInfo.Empty() { plog.Infof("peerTLS: %s", cfg.peerTLSInfo) } var plns []net.Listener for _, u := range cfg.lpurls { if u.Scheme == "http" { if !cfg.peerTLSInfo.Empty() { plog.Warningf("The scheme of peer url %s is HTTP while peer key/cert files are presented. Ignored peer key/cert files.", u.String()) } if cfg.peerTLSInfo.ClientCertAuth { plog.Warningf("The scheme of peer url %s is HTTP while client cert auth (--peer-client-cert-auth) is enabled. Ignored client cert auth for this url.", u.String()) } } var ( l net.Listener tlscfg *tls.Config ) if !cfg.peerTLSInfo.Empty() { tlscfg, err = cfg.peerTLSInfo.ServerConfig() if err != nil { return nil, err } } l, err = rafthttp.NewListener(u, tlscfg) if err != nil { return nil, err } urlStr := u.String() plog.Info("listening for peers on ", urlStr) defer func() { if err != nil { l.Close() plog.Info("stopping listening for peers on ", urlStr) } }() plns = append(plns, l) } if cfg.ClientAutoTLS && cfg.clientTLSInfo.Empty() { var chosts []string for _, u := range cfg.lcurls { chosts = append(chosts, u.Host) } cfg.clientTLSInfo, err = transport.SelfCert(path.Join(cfg.Dir, "fixtures/client"), chosts) if err != nil { plog.Fatalf("could not get certs (%v)", err) } } else if cfg.ClientAutoTLS { plog.Warningf("ignoring client auto TLS since certs given") } var ctlscfg *tls.Config if !cfg.clientTLSInfo.Empty() { plog.Infof("clientTLS: %s", cfg.clientTLSInfo) ctlscfg, err = cfg.clientTLSInfo.ServerConfig() if err != nil { return nil, err } } sctxs := make(map[string]*serveCtx) for _, u := range cfg.lcurls { if u.Scheme == "http" { if !cfg.clientTLSInfo.Empty() { plog.Warningf("The scheme of client url %s is HTTP while peer key/cert files are presented. Ignored key/cert files.", u.String()) } if cfg.clientTLSInfo.ClientCertAuth { plog.Warningf("The scheme of client url %s is HTTP while client cert auth (--client-cert-auth) is enabled. Ignored client cert auth for this url.", u.String()) } } if u.Scheme == "https" && ctlscfg == nil { return nil, fmt.Errorf("TLS key/cert (--cert-file, --key-file) must be provided for client url %s with HTTPs scheme", u.String()) } ctx := &serveCtx{host: u.Host} if u.Scheme == "https" { ctx.secure = true } else { ctx.insecure = true } if sctxs[u.Host] != nil { if ctx.secure { sctxs[u.Host].secure = true } if ctx.insecure { sctxs[u.Host].insecure = true } continue } var l net.Listener l, err = net.Listen("tcp", u.Host) if err != nil { return nil, err } var fdLimit uint64 if fdLimit, err = runtimeutil.FDLimit(); err == nil { if fdLimit <= reservedInternalFDNum { plog.Fatalf("file descriptor limit[%d] of etcd process is too low, and should be set higher than %d to ensure internal usage", fdLimit, reservedInternalFDNum) } l = transport.LimitListener(l, int(fdLimit-reservedInternalFDNum)) } l, err = transport.NewKeepAliveListener(l, "tcp", nil) ctx.l = l if err != nil { return nil, err } plog.Info("listening for client requests on ", u.Host) defer func() { if err != nil { l.Close() plog.Info("stopping listening for client requests on ", u.Host) } }() sctxs[u.Host] = ctx } srvcfg := &etcdserver.ServerConfig{ Name: cfg.Name, ClientURLs: cfg.acurls, PeerURLs: cfg.apurls, DataDir: cfg.Dir, DedicatedWALDir: cfg.WalDir, SnapCount: cfg.SnapCount, MaxSnapFiles: cfg.MaxSnapFiles, MaxWALFiles: cfg.MaxWalFiles, InitialPeerURLsMap: urlsmap, InitialClusterToken: token, DiscoveryURL: cfg.Durl, DiscoveryProxy: cfg.Dproxy, NewCluster: cfg.isNewCluster(), ForceNewCluster: cfg.ForceNewCluster, PeerTLSInfo: cfg.peerTLSInfo, TickMs: cfg.TickMs, ElectionTicks: cfg.electionTicks(), AutoCompactionRetention: cfg.autoCompactionRetention, QuotaBackendBytes: cfg.QuotaBackendBytes, StrictReconfigCheck: cfg.StrictReconfigCheck, EnablePprof: cfg.enablePprof, } var s *etcdserver.EtcdServer s, err = etcdserver.NewServer(srvcfg) if err != nil { return nil, err } s.Start() osutil.RegisterInterruptHandler(s.Stop) if cfg.corsInfo.String() != "" { plog.Infof("cors = %s", cfg.corsInfo) } ch := http.Handler(&cors.CORSHandler{ Handler: v2http.NewClientHandler(s, srvcfg.ReqTimeout()), Info: cfg.corsInfo, }) ph := v2http.NewPeerHandler(s) // Start the peer server in a goroutine for _, l := range plns { go func(l net.Listener) { plog.Fatal(servePeerHTTP(l, ph)) }(l) } // Start a client server goroutine for each listen address for _, sctx := range sctxs { go func(sctx *serveCtx) { // read timeout does not work with http close notify // TODO: https://github.com/golang/go/issues/9524 plog.Fatal(serve(sctx, s, ctlscfg, ch)) }(sctx) } <-s.ReadyNotify() return s.StopNotify(), nil }