mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-30 18:22:49 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			445 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			445 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // 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"
 | |
| 	"strings"
 | |
| 	"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")
 | |
| 
 | |
| 	host, _, err := net.SplitHostPort(r.Host)
 | |
| 	if err != nil {
 | |
| 		host = r.Host // oh well
 | |
| 	}
 | |
| 
 | |
| 	// "The host subcomponent is case-insensitive." (RFC 3986)
 | |
| 	host = strings.ToLower(host)
 | |
| 
 | |
| 	// 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 = ""
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Execute the optional request callback if it exists and it's not disabled
 | |
| 	if s.ReqCallback != nil && !s.vhosts[host].config.TLS.Manual && s.ReqCallback(w, r) {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	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()
 | |
| }
 | |
| 
 | |
| // 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
 | |
| }
 |