0
Fork 0
mirror of https://github.com/caddyserver/caddy.git synced 2024-12-23 22:27:38 -05:00
caddy/server/server.go

246 lines
6.6 KiB
Go
Raw Normal View History

// 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.
2015-01-13 14:43:45 -05:00
package server
import (
2015-04-15 15:11:32 -05:00
"crypto/tls"
2015-06-02 00:22:11 -05:00
"crypto/x509"
"fmt"
2015-06-02 00:22:11 -05:00
"io/ioutil"
2015-01-13 14:43:45 -05:00
"log"
2015-04-15 15:11:32 -05:00
"net"
2015-01-13 14:43:45 -05:00
"net/http"
2015-03-26 10:52:03 -05:00
"os"
"os/signal"
2015-01-13 14:43:45 -05:00
2015-03-16 12:44:54 -05:00
"github.com/bradfitz/http2"
2015-01-13 14:43:45 -05:00
)
// Server represents an instance of a server, which serves
// static content at a particular address (host and port).
type Server struct {
2015-04-21 17:00:16 -05:00
HTTP2 bool // temporary while http2 is not in std lib (TODO: remove flag when part of std lib)
address string // the actual address for net.Listen to listen on
tls bool // whether this server is serving all HTTPS hosts or not
vhosts map[string]virtualHost // virtual hosts keyed by their address
2015-01-13 14:43:45 -05:00
}
2015-04-15 15:11:32 -05:00
// New creates a new Server which will bind to addr and serve
// the sites/hosts configured in configs. This function does
// not start serving.
2015-07-11 13:00:11 -05:00
func New(addr string, configs []Config) (*Server, error) {
var tls bool
if len(configs) > 0 {
tls = configs[0].TLS.Enabled
}
2015-04-15 15:11:32 -05:00
s := &Server{
address: addr,
tls: tls,
vhosts: make(map[string]virtualHost),
2015-01-13 14:43:45 -05:00
}
2015-04-15 15:11:32 -05:00
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.address)
2015-04-15 15:11:32 -05:00
}
2015-04-15 15:11:32 -05:00
vh := virtualHost{config: conf}
2015-01-13 14:43:45 -05:00
2015-04-15 15:11:32 -05:00
// Build middleware stack
err := vh.buildStack()
if err != nil {
2015-04-15 15:11:32 -05:00
return nil, err
}
2015-04-15 15:11:32 -05:00
s.vhosts[conf.Host] = vh
2015-01-13 14:43:45 -05:00
}
2015-04-15 15:11:32 -05:00
return s, nil
}
2015-04-15 15:11:32 -05:00
// Serve starts the server. It blocks until the server quits.
func (s *Server) Serve() error {
2015-03-16 12:44:54 -05:00
server := &http.Server{
2015-04-15 15:11:32 -05:00
Addr: s.address,
2015-03-16 12:44:54 -05:00
Handler: s,
}
if s.HTTP2 {
// TODO: This call may not be necessary after HTTP/2 is merged into std lib
http2.ConfigureServer(server, nil)
}
2015-03-16 12:44:54 -05:00
2015-04-15 15:11:32 -05:00
for _, vh := range s.vhosts {
// Execute startup functions now
2015-04-15 15:11:32 -05:00
for _, start := range vh.config.Startup {
err := start()
2015-03-26 10:52:03 -05:00
if err != nil {
2015-04-15 15:11:32 -05:00
return err
2015-03-26 10:52:03 -05:00
}
}
// Execute shutdown commands on exit
2015-04-15 15:11:32 -05:00
if len(vh.config.Shutdown) > 0 {
go func(vh virtualHost) {
// Wait for signal
2015-04-15 15:11:32 -05:00
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, os.Kill)
2015-04-15 15:11:32 -05:00
<-interrupt
// Run callbacks
exitCode := 0
2015-04-15 15:11:32 -05:00
for _, shutdownFunc := range vh.config.Shutdown {
err := shutdownFunc()
if err != nil {
exitCode = 1
log.Println(err)
2015-04-15 15:11:32 -05:00
}
}
os.Exit(exitCode) // BUG: Other shutdown goroutines might be running; use sync.WaitGroup
}(vh)
2015-04-15 15:11:32 -05:00
}
}
if s.tls {
var tlsConfigs []TLSConfig
2015-04-15 15:11:32 -05:00
for _, vh := range s.vhosts {
tlsConfigs = append(tlsConfigs, vh.config.TLS)
}
return ListenAndServeTLSWithSNI(server, tlsConfigs)
2015-01-13 14:43:45 -05:00
}
2015-05-24 21:52:34 -05:00
return server.ListenAndServe()
2015-01-13 14:43:45 -05:00
}
2015-04-15 15:11:32 -05:00
// ListenAndServeTLSWithSNI serves TLS with Server Name Indication (SNI) support, which allows
// multiple sites (different hostnames) to be served from the same address. This method is
// adapted directly from the std lib's net/http ListenAndServeTLS function, which was
// written by the Go Authors. It has been modified to support multiple certificate/key pairs.
func ListenAndServeTLSWithSNI(srv *http.Server, tlsConfigs []TLSConfig) error {
2015-04-15 15:11:32 -05:00
addr := srv.Addr
if addr == "" {
addr = ":https"
}
config := new(tls.Config)
if srv.TLSConfig != nil {
*config = *srv.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)
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
2015-05-04 22:25:29 -05:00
2015-06-02 00:22:11 -05:00
// TLS client authentication, if user enabled it
err = setupClientAuth(tlsConfigs, config)
2015-04-15 15:11:32 -05:00
if err != nil {
return err
}
2015-06-02 00:22:11 -05:00
// Create listener and we're on our way
conn, err := net.Listen("tcp", addr)
if err != nil {
return err
}
2015-04-15 15:11:32 -05:00
tlsListener := tls.NewListener(conn, config)
2015-06-02 00:22:11 -05:00
2015-04-15 15:11:32 -05:00
return srv.Serve(tlsListener)
}
2015-06-02 00:22:11 -05:00
// 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 Matt Holt 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)
2015-06-02 00:22:11 -05:00
}
}
}
config.ClientCAs = pool
config.ClientAuth = tls.RequireAndVerifyClientCert
}
return nil
}
2015-04-15 15:11:32 -05:00
// 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.
2015-01-13 14:43:45 -05:00
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
2015-03-26 23:52:27 -05:00
defer func() {
// In case the user doesn't enable error middleware, we still
// need to make sure that we stay alive up here
2015-03-26 23:52:27 -05:00
if rec := recover(); rec != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError),
http.StatusInternalServerError)
2015-03-26 23:52:27 -05:00
}
}()
2015-04-15 15:11:32 -05:00
host, _, err := net.SplitHostPort(r.Host)
if err != nil {
host = r.Host // oh well
}
2015-01-13 14:43:45 -05:00
// 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 = ""
}
}
2015-04-15 15:11:32 -05:00
if vh, ok := s.vhosts[host]; ok {
w.Header().Set("Server", "Caddy")
2015-04-15 15:11:32 -05:00
status, _ := vh.stack.ServeHTTP(w, r)
2015-01-13 14:43:45 -05:00
2015-04-15 15:11:32 -05:00
// Fallback error response in case error handling wasn't chained in
if status >= 400 {
DefaultErrorFunc(w, r, status)
2015-04-15 15:11:32 -05:00
}
} else {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "No such host at %s", s.address)
2015-01-13 14:43:45 -05:00
}
}
func DefaultErrorFunc(w http.ResponseWriter, r *http.Request, status int) {
w.WriteHeader(status)
fmt.Fprintf(w, "%d %s", status, http.StatusText(status))
}