mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 03:27:23 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			415 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			415 lines
		
	
	
		
			12 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"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"golang.org/x/net/http2"
 | 
						|
)
 | 
						|
 | 
						|
// 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                   // temporary while http2 is not in std lib (TODO: remove flag when part of std lib)
 | 
						|
	tls        bool                   // whether this server is serving all HTTPS hosts or not
 | 
						|
	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
 | 
						|
}
 | 
						|
 | 
						|
type ListenerFile interface {
 | 
						|
	net.Listener
 | 
						|
	File() (*os.File, error)
 | 
						|
}
 | 
						|
 | 
						|
// New creates a new Server which will bind to addr and serve
 | 
						|
// the sites/hosts configured in configs. This function does
 | 
						|
// not start serving.
 | 
						|
func New(addr string, configs []Config) (*Server, error) {
 | 
						|
	var tls bool
 | 
						|
	if len(configs) > 0 {
 | 
						|
		tls = configs[0].TLS.Enabled
 | 
						|
	}
 | 
						|
 | 
						|
	s := &Server{
 | 
						|
		Server: &http.Server{
 | 
						|
			Addr: addr,
 | 
						|
			// TODO: Make these values configurable?
 | 
						|
			// ReadTimeout:    2 * time.Minute,
 | 
						|
			// WriteTimeout:   2 * time.Minute,
 | 
						|
			// MaxHeaderBytes: 1 << 16,
 | 
						|
		},
 | 
						|
		tls:    tls,
 | 
						|
		vhosts: make(map[string]virtualHost),
 | 
						|
	}
 | 
						|
	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.
 | 
						|
	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 {
 | 
						|
		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 {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	ln, err := net.Listen("tcp", s.Addr)
 | 
						|
	if err != nil {
 | 
						|
		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 serveTLSWithSNI(s, s.listener, tlsConfigs)
 | 
						|
	}
 | 
						|
 | 
						|
	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 {
 | 
						|
		// TODO: This call may not be necessary after HTTP/2 is merged into std lib
 | 
						|
		http2.ConfigureServer(s.Server, nil)
 | 
						|
	}
 | 
						|
 | 
						|
	// Execute startup functions now
 | 
						|
	for _, vh := range s.vhosts {
 | 
						|
		for _, startupFunc := range vh.config.Startup {
 | 
						|
			err := startupFunc()
 | 
						|
			if err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// serveTLSWithSNI serves TLS with Server Name Indication (SNI) support, which allows
 | 
						|
// multiple sites (different hostnames) to be served from the same address. It also
 | 
						|
// supports client authentication if srv has it enabled. It blocks until s quits.
 | 
						|
//
 | 
						|
// This method is adapted from the std lib's net/http ServeTLS function, which was written
 | 
						|
// by the Go Authors. It has been modified to support multiple certificate/key pairs,
 | 
						|
// client authentication, and our custom Server type.
 | 
						|
func serveTLSWithSNI(s *Server, ln net.Listener, tlsConfigs []TLSConfig) error {
 | 
						|
	config := cloneTLSConfig(s.TLSConfig)
 | 
						|
	if config.NextProtos == nil {
 | 
						|
		config.NextProtos = []string{"http/1.1"}
 | 
						|
	}
 | 
						|
 | 
						|
	// Here we diverge from the stdlib a bit by loading multiple certs/key pairs
 | 
						|
	// then we map the server names to their certs
 | 
						|
	var err error
 | 
						|
	config.Certificates = make([]tls.Certificate, len(tlsConfigs))
 | 
						|
	for i, tlsConfig := range tlsConfigs {
 | 
						|
		config.Certificates[i], err = tls.LoadX509KeyPair(tlsConfig.Certificate, tlsConfig.Key)
 | 
						|
		config.Certificates[i].OCSPStaple = tlsConfig.OCSPStaple
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	config.BuildNameToCertificate()
 | 
						|
 | 
						|
	// Customize our TLS configuration
 | 
						|
	config.MinVersion = tlsConfigs[0].ProtocolMinVersion
 | 
						|
	config.MaxVersion = tlsConfigs[0].ProtocolMaxVersion
 | 
						|
	config.CipherSuites = tlsConfigs[0].Ciphers
 | 
						|
	config.PreferServerCipherSuites = tlsConfigs[0].PreferServerCipherSuites
 | 
						|
 | 
						|
	// TLS client authentication, if user enabled it
 | 
						|
	err = setupClientAuth(tlsConfigs, config)
 | 
						|
	if err != nil {
 | 
						|
		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, config)
 | 
						|
 | 
						|
	// Begin serving; block until done
 | 
						|
	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() error {
 | 
						|
	s.Server.SetKeepAlivesEnabled(false) // TODO: Does this even do anything? :P
 | 
						|
 | 
						|
	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(5 * time.Second): // TODO: configurable?
 | 
						|
		case <-done:
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Close the listener now; this stops the server and
 | 
						|
	s.listenerMu.Lock()
 | 
						|
	err := s.listener.Close()
 | 
						|
	s.listenerMu.Unlock()
 | 
						|
	if err != nil {
 | 
						|
		// TODO: Better logging
 | 
						|
		log.Println(err)
 | 
						|
	}
 | 
						|
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
// ListenerFd gets the file descriptor of the listener.
 | 
						|
func (s *Server) ListenerFd() uintptr {
 | 
						|
	s.listenerMu.Lock()
 | 
						|
	defer s.listenerMu.Unlock()
 | 
						|
	file, err := s.listener.File()
 | 
						|
	if err != nil {
 | 
						|
		return 0
 | 
						|
	}
 | 
						|
	return file.Fd()
 | 
						|
}
 | 
						|
 | 
						|
// 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)
 | 
						|
		}
 | 
						|
	}()
 | 
						|
 | 
						|
	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 {
 | 
						|
		w.Header().Set("Server", "Caddy")
 | 
						|
 | 
						|
		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 {
 | 
						|
		w.WriteHeader(http.StatusNotFound)
 | 
						|
		fmt.Fprintf(w, "No such host at %s", s.Server.Addr)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// 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
 | 
						|
}
 | 
						|
 | 
						|
// 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
 | 
						|
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
 | 
						|
}
 |