// Package server implements a configurable, general-purpose web server. // It relies on configurations obtained from the adjacent config package // and can execute middleware as defined by the adjacent middleware package. package server import ( "crypto/tls" "crypto/x509" "fmt" "io/ioutil" "log" "net" "net/http" "os" "runtime" "sync" "time" ) // Server represents an instance of a server, which serves // HTTP requests at a particular address (host and port). A // server is capable of serving numerous virtual hosts on // the same address and the listener may be stopped for // graceful termination (POSIX only). type Server struct { *http.Server HTTP2 bool // whether to enable HTTP/2 tls bool // whether this server is serving all HTTPS hosts or not OnDemandTLS bool // whether this server supports on-demand TLS (load certs at handshake-time) vhosts map[string]virtualHost // virtual hosts keyed by their address listener ListenerFile // the listener which is bound to the socket listenerMu sync.Mutex // protects listener httpWg sync.WaitGroup // used to wait on outstanding connections startChan chan struct{} // used to block until server is finished starting connTimeout time.Duration // the maximum duration of a graceful shutdown ReqCallback OptionalCallback // if non-nil, is executed at the beginning of every request SNICallback func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) } // ListenerFile represents a listener. type ListenerFile interface { net.Listener File() (*os.File, error) } // OptionalCallback is a function that may or may not handle a request. // It returns whether or not it handled the request. If it handled the // request, it is presumed that no further request handling should occur. type OptionalCallback func(http.ResponseWriter, *http.Request) bool // New creates a new Server which will bind to addr and serve // the sites/hosts configured in configs. Its listener will // gracefully close when the server is stopped which will take // no longer than gracefulTimeout. // // This function does not start serving. // // Do not re-use a server (start, stop, then start again). We // could probably add more locking to make this possible, but // as it stands, you should dispose of a server after stopping it. // The behavior of serving with a spent server is undefined. func New(addr string, configs []Config, gracefulTimeout time.Duration) (*Server, error) { var useTLS, useOnDemandTLS bool if len(configs) > 0 { useTLS = configs[0].TLS.Enabled useOnDemandTLS = configs[0].TLS.OnDemand } s := &Server{ Server: &http.Server{ Addr: addr, TLSConfig: new(tls.Config), // TODO: Make these values configurable? // ReadTimeout: 2 * time.Minute, // WriteTimeout: 2 * time.Minute, // MaxHeaderBytes: 1 << 16, }, tls: useTLS, OnDemandTLS: useOnDemandTLS, vhosts: make(map[string]virtualHost), startChan: make(chan struct{}), connTimeout: gracefulTimeout, } s.Handler = s // this is weird, but whatever // We have to bound our wg with one increment // to prevent a "race condition" that is hard-coded // into sync.WaitGroup.Wait() - basically, an add // with a positive delta must be guaranteed to // occur before Wait() is called on the wg. // In a way, this kind of acts as a safety barrier. s.httpWg.Add(1) // Set up each virtualhost for _, conf := range configs { if _, exists := s.vhosts[conf.Host]; exists { return nil, fmt.Errorf("cannot serve %s - host already defined for address %s", conf.Address(), s.Addr) } vh := virtualHost{config: conf} // Build middleware stack err := vh.buildStack() if err != nil { return nil, err } s.vhosts[conf.Host] = vh } return s, nil } // Serve starts the server with an existing listener. It blocks until the // server stops. func (s *Server) Serve(ln ListenerFile) error { err := s.setup() if err != nil { defer close(s.startChan) // MUST defer so error is properly reported, same with all cases in this file return err } return s.serve(ln) } // ListenAndServe starts the server with a new listener. It blocks until the server stops. func (s *Server) ListenAndServe() error { err := s.setup() if err != nil { defer close(s.startChan) return err } ln, err := net.Listen("tcp", s.Addr) if err != nil { var succeeded bool if runtime.GOOS == "windows" { // TODO: Limit this to Windows only? (it keeps sockets open after closing listeners) for i := 0; i < 20; i++ { time.Sleep(100 * time.Millisecond) ln, err = net.Listen("tcp", s.Addr) if err == nil { succeeded = true break } } } if !succeeded { defer close(s.startChan) return err } } return s.serve(ln.(*net.TCPListener)) } // serve prepares s to listen on ln by wrapping ln in a // tcpKeepAliveListener (if ln is a *net.TCPListener) and // then in a gracefulListener, so that keep-alive is supported // as well as graceful shutdown/restart. It also configures // TLS listener on top of that if applicable. func (s *Server) serve(ln ListenerFile) error { if tcpLn, ok := ln.(*net.TCPListener); ok { ln = tcpKeepAliveListener{TCPListener: tcpLn} } s.listenerMu.Lock() s.listener = newGracefulListener(ln, &s.httpWg) s.listenerMu.Unlock() if s.tls { var tlsConfigs []TLSConfig for _, vh := range s.vhosts { tlsConfigs = append(tlsConfigs, vh.config.TLS) } return serveTLS(s, s.listener, tlsConfigs) } close(s.startChan) // unblock anyone waiting for this to start listening return s.Server.Serve(s.listener) } // setup prepares the server s to begin listening; it should be // called just before the listener announces itself on the network // and should only be called when the server is just starting up. func (s *Server) setup() error { if !s.HTTP2 { s.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler)) } // Execute startup functions now for _, vh := range s.vhosts { for _, startupFunc := range vh.config.Startup { err := startupFunc() if err != nil { return err } } } return nil } // serveTLS serves TLS with SNI and client auth support if s has them enabled. It // blocks until s quits. func serveTLS(s *Server, ln net.Listener, tlsConfigs []TLSConfig) error { // Customize our TLS configuration s.TLSConfig.MinVersion = tlsConfigs[0].ProtocolMinVersion s.TLSConfig.MaxVersion = tlsConfigs[0].ProtocolMaxVersion s.TLSConfig.CipherSuites = tlsConfigs[0].Ciphers s.TLSConfig.PreferServerCipherSuites = tlsConfigs[0].PreferServerCipherSuites // TLS client authentication, if user enabled it err := setupClientAuth(tlsConfigs, s.TLSConfig) if err != nil { defer close(s.startChan) return err } // Create TLS listener - note that we do not replace s.listener // with this TLS listener; tls.listener is unexported and does // not implement the File() method we need for graceful restarts // on POSIX systems. ln = tls.NewListener(ln, s.TLSConfig) close(s.startChan) // unblock anyone waiting for this to start listening return s.Server.Serve(ln) } // Stop stops the server. It blocks until the server is // totally stopped. On POSIX systems, it will wait for // connections to close (up to a max timeout of a few // seconds); on Windows it will close the listener // immediately. func (s *Server) Stop() (err error) { s.Server.SetKeepAlivesEnabled(false) if runtime.GOOS != "windows" { // force connections to close after timeout done := make(chan struct{}) go func() { s.httpWg.Done() // decrement our initial increment used as a barrier s.httpWg.Wait() close(done) }() // Wait for remaining connections to finish or // force them all to close after timeout select { case <-time.After(s.connTimeout): case <-done: } } // Close the listener now; this stops the server without delay s.listenerMu.Lock() if s.listener != nil { err = s.listener.Close() } s.listenerMu.Unlock() return } // WaitUntilStarted blocks until the server s is started, meaning // that practically the next instruction is to start the server loop. // It also unblocks if the server encounters an error during startup. func (s *Server) WaitUntilStarted() { <-s.startChan } // ListenerFd gets a dup'ed file of the listener. If there // is no underlying file, the return value will be nil. It // is the caller's responsibility to close the file. func (s *Server) ListenerFd() *os.File { s.listenerMu.Lock() defer s.listenerMu.Unlock() if s.listener != nil { file, _ := s.listener.File() return file } return nil } // ServeHTTP is the entry point for every request to the address that s // is bound to. It acts as a multiplexer for the requests hostname as // defined in the Host header so that the correct virtualhost // (configuration and middleware stack) will handle the request. func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { defer func() { // In case the user doesn't enable error middleware, we still // need to make sure that we stay alive up here if rec := recover(); rec != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } }() w.Header().Set("Server", "Caddy") // Execute the optional request callback if it exists if s.ReqCallback != nil && s.ReqCallback(w, r) { return } host, _, err := net.SplitHostPort(r.Host) if err != nil { host = r.Host // oh well } // Try the host as given, or try falling back to 0.0.0.0 (wildcard) if _, ok := s.vhosts[host]; !ok { if _, ok2 := s.vhosts["0.0.0.0"]; ok2 { host = "0.0.0.0" } else if _, ok2 := s.vhosts[""]; ok2 { host = "" } } if vh, ok := s.vhosts[host]; ok { status, _ := vh.stack.ServeHTTP(w, r) // Fallback error response in case error handling wasn't chained in if status >= 400 { DefaultErrorFunc(w, r, status) } } else { // Get the remote host remoteHost, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { remoteHost = r.RemoteAddr } w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, "No such host at %s", s.Server.Addr) log.Printf("[INFO] %s - No such host at %s (Remote: %s, Referer: %s)", host, s.Server.Addr, remoteHost, r.Header.Get("Referer")) } } // DefaultErrorFunc responds to an HTTP request with a simple description // of the specified HTTP status code. func DefaultErrorFunc(w http.ResponseWriter, r *http.Request, status int) { w.WriteHeader(status) fmt.Fprintf(w, "%d %s", status, http.StatusText(status)) } // setupClientAuth sets up TLS client authentication only if // any of the TLS configs specified at least one cert file. func setupClientAuth(tlsConfigs []TLSConfig, config *tls.Config) error { var clientAuth bool for _, cfg := range tlsConfigs { if len(cfg.ClientCerts) > 0 { clientAuth = true break } } if clientAuth { pool := x509.NewCertPool() for _, cfg := range tlsConfigs { for _, caFile := range cfg.ClientCerts { caCrt, err := ioutil.ReadFile(caFile) // Anyone that gets a cert from this CA can connect if err != nil { return err } if !pool.AppendCertsFromPEM(caCrt) { return fmt.Errorf("error loading client certificate '%s': no certificates were successfully parsed", caFile) } } } config.ClientCAs = pool config.ClientAuth = tls.RequireAndVerifyClientCert } return nil } // RunFirstStartupFuncs runs all of the server's FirstStartup // callback functions unless one of them returns an error first. // It is the caller's responsibility to call this only once and // at the correct time. The functions here should not be executed // at restarts or where the user does not explicitly start a new // instance of the server. func (s *Server) RunFirstStartupFuncs() error { for _, vh := range s.vhosts { for _, f := range vh.config.FirstStartup { if err := f(); err != nil { return err } } } return nil } // tcpKeepAliveListener sets TCP keep-alive timeouts on accepted // connections. It's used by ListenAndServe and ListenAndServeTLS so // dead TCP connections (e.g. closing laptop mid-download) eventually // go away. // // Borrowed from the Go standard library. type tcpKeepAliveListener struct { *net.TCPListener } // Accept accepts the connection with a keep-alive enabled. func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { tc, err := ln.AcceptTCP() if err != nil { return } tc.SetKeepAlive(true) tc.SetKeepAlivePeriod(3 * time.Minute) return tc, nil } // File implements ListenerFile; returns the underlying file of the listener. func (ln tcpKeepAliveListener) File() (*os.File, error) { return ln.TCPListener.File() } // copied from net/http/transport.go /* TODO - remove - not necessary? func cloneTLSConfig(cfg *tls.Config) *tls.Config { if cfg == nil { return &tls.Config{} } return &tls.Config{ Rand: cfg.Rand, Time: cfg.Time, Certificates: cfg.Certificates, NameToCertificate: cfg.NameToCertificate, GetCertificate: cfg.GetCertificate, RootCAs: cfg.RootCAs, NextProtos: cfg.NextProtos, ServerName: cfg.ServerName, ClientAuth: cfg.ClientAuth, ClientCAs: cfg.ClientCAs, InsecureSkipVerify: cfg.InsecureSkipVerify, CipherSuites: cfg.CipherSuites, PreferServerCipherSuites: cfg.PreferServerCipherSuites, SessionTicketsDisabled: cfg.SessionTicketsDisabled, SessionTicketKey: cfg.SessionTicketKey, ClientSessionCache: cfg.ClientSessionCache, MinVersion: cfg.MinVersion, MaxVersion: cfg.MaxVersion, CurvePreferences: cfg.CurvePreferences, } }*/ // ShutdownCallbacks executes all the shutdown callbacks // for all the virtualhosts in servers, and returns all the // errors generated during their execution. In other words, // an error executing one shutdown callback does not stop // execution of others. Only one shutdown callback is executed // at a time. You must protect the servers that are passed in // if they are shared across threads. func ShutdownCallbacks(servers []*Server) []error { var errs []error for _, s := range servers { for _, vhost := range s.vhosts { for _, shutdownFunc := range vhost.config.Shutdown { err := shutdownFunc() if err != nil { errs = append(errs, err) } } } } return errs }