api/server/server.go
a7365a62
 package server
a11b3139
 
 import (
bfed4b7c
 	"crypto/tls"
322e2a7d
 	"fmt"
3adf9ce0
 	"net"
a11b3139
 	"net/http"
b295239d
 	"strings"
a7365a62
 
c452e1bf
 	"github.com/docker/docker/api/errors"
da982cf5
 	"github.com/docker/docker/api/server/httputils"
8d346762
 	"github.com/docker/docker/api/server/middleware"
da982cf5
 	"github.com/docker/docker/api/server/router"
408c7ade
 	"github.com/docker/docker/api/server/router/debug"
64981b9f
 	"github.com/docker/docker/dockerversion"
94e3b0f4
 	"github.com/gorilla/mux"
1009e6a4
 	"github.com/sirupsen/logrus"
94e3b0f4
 	"golang.org/x/net/context"
a11b3139
 )
 
2ab94e11
 // versionMatcher defines a variable matcher to be parsed by the router
 // when a request is about to be served.
 const versionMatcher = "/v{version:[0-9.]+}"
 
351f6b8e
 // Config provides the configuration for the API server
 type Config struct {
8d346762
 	Logging     bool
 	EnableCors  bool
 	CorsHeaders string
 	Version     string
 	SocketGroup string
 	TLSConfig   *tls.Config
a0bf80fe
 }
 
351f6b8e
 // Server contains instance details for the server
d9ed3165
 type Server struct {
677a6b35
 	cfg           *Config
 	servers       []*HTTPServer
 	routers       []router.Router
 	routerSwapper *routerSwapper
8d346762
 	middlewares   []middleware.Middleware
d9ed3165
 }
 
351f6b8e
 // New returns a new instance of the server based on the specified configuration.
5eda566f
 // It allocates resources which will be needed for ServeAPI(ports, unix-sockets).
34c29277
 func New(cfg *Config) *Server {
 	return &Server{
ca5795ce
 		cfg: cfg,
d9ed3165
 	}
34c29277
 }
 
8d346762
 // UseMiddleware appends a new middleware to the request chain.
 // This needs to be called before the API routes are configured.
 func (s *Server) UseMiddleware(m middleware.Middleware) {
 	s.middlewares = append(s.middlewares, m)
 }
 
34c29277
 // Accept sets a listener the server accepts connections into.
 func (s *Server) Accept(addr string, listeners ...net.Listener) {
 	for _, listener := range listeners {
 		httpServer := &HTTPServer{
 			srv: &http.Server{
 				Addr: addr,
 			},
 			l: listener,
5eda566f
 		}
34c29277
 		s.servers = append(s.servers, httpServer)
5eda566f
 	}
d9ed3165
 }
 
351f6b8e
 // Close closes servers and thus stop receiving requests
531f4122
 func (s *Server) Close() {
 	for _, srv := range s.servers {
 		if err := srv.Close(); err != nil {
 			logrus.Error(err)
 		}
 	}
 }
 
677a6b35
 // serveAPI loops through all initialized servers and spawns goroutine
97a4548b
 // with Serve method for each. It sets createMux() as Handler also.
677a6b35
 func (s *Server) serveAPI() error {
5eda566f
 	var chErrors = make(chan error, len(s.servers))
 	for _, srv := range s.servers {
677a6b35
 		srv.srv.Handler = s.routerSwapper
5eda566f
 		go func(srv *HTTPServer) {
 			var err error
cc833c5e
 			logrus.Infof("API listen on %s", srv.l.Addr())
5eda566f
 			if err = srv.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") {
 				err = nil
 			}
 			chErrors <- err
 		}(srv)
d9ed3165
 	}
 
1ab62a0b
 	for range s.servers {
d9ed3165
 		err := <-chErrors
 		if err != nil {
 			return err
 		}
 	}
 	return nil
 }
 
351f6b8e
 // HTTPServer contains an instance of http server and the listener.
1f039a66
 // srv *http.Server, contains configuration to create an http server and a mux router with all api end points.
351f6b8e
 // l   net.Listener, is a TCP or Socket listener that dispatches incoming request to the router.
 type HTTPServer struct {
dacae746
 	srv *http.Server
 	l   net.Listener
 }
 
351f6b8e
 // Serve starts listening for inbound requests.
 func (s *HTTPServer) Serve() error {
dacae746
 	return s.srv.Serve(s.l)
 }
351f6b8e
 
 // Close closes the HTTPServer from listening for the inbound requests.
 func (s *HTTPServer) Close() error {
dacae746
 	return s.l.Close()
 }
 
da982cf5
 func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc {
166eba3e
 	return func(w http.ResponseWriter, r *http.Request) {
8b454dd7
 		// Define the context that we'll pass around to share info
 		// like the docker-request-id.
 		//
 		// The 'context' will be used for global data that should
 		// apply to all requests. Data that is specific to the
 		// immediate function being called should still be passed
 		// as 'args' on the function call.
64981b9f
 		ctx := context.WithValue(context.Background(), dockerversion.UAStringKey, r.Header.Get("User-Agent"))
e743ab08
 		handlerFunc := s.handlerWithGlobalMiddlewares(handler)
126529c6
 
389ce0aa
 		vars := mux.Vars(r)
 		if vars == nil {
 			vars = make(map[string]string)
 		}
 
 		if err := handlerFunc(ctx, w, r, vars); err != nil {
f7d9bb62
 			statusCode := httputils.GetHTTPErrorStatusCode(err)
7381fffb
 			if statusCode >= 500 {
 				logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
f7d9bb62
 			}
322e2a7d
 			httputils.MakeErrorHandler(err)(w, r)
166eba3e
 		}
 	}
 }
 
e8f569b3
 // InitRouter initializes the list of routers for the server.
 // This method also enables the Go profiler if enableProfiler is true.
408c7ade
 func (s *Server) InitRouter(routers ...router.Router) {
64238fef
 	s.routers = append(s.routers, routers...)
da982cf5
 
e8f569b3
 	m := s.createMux()
 	s.routerSwapper = &routerSwapper{
 		router: m,
 	}
da982cf5
 }
 
677a6b35
 // createMux initializes the main router the server uses.
 func (s *Server) createMux() *mux.Router {
da982cf5
 	m := mux.NewRouter()
b419699a
 
a72b45db
 	logrus.Debug("Registering routers")
2ab94e11
 	for _, apiRouter := range s.routers {
 		for _, r := range apiRouter.Routes() {
da982cf5
 			f := s.makeHTTPHandler(r.Handler())
2ab94e11
 
 			logrus.Debugf("Registering %s, %s", r.Method(), r.Path())
 			m.Path(versionMatcher + r.Path()).Methods(r.Method()).Handler(f)
 			m.Path(r.Path()).Methods(r.Method()).Handler(f)
131c6ab3
 		}
b56b2da5
 	}
8eeff019
 
408c7ade
 	debugRouter := debug.NewRouter()
 	s.routers = append(s.routers, debugRouter)
 	for _, r := range debugRouter.Routes() {
 		f := s.makeHTTPHandler(r.Handler())
 		m.Path("/debug" + r.Path()).Handler(f)
 	}
 
322e2a7d
 	err := errors.NewRequestNotFoundError(fmt.Errorf("page not found"))
 	notFoundHandler := httputils.MakeErrorHandler(err)
 	m.HandleFunc(versionMatcher+"/{path:.*}", notFoundHandler)
 	m.NotFoundHandler = notFoundHandler
 
da982cf5
 	return m
 }
677a6b35
 
 // Wait blocks the server goroutine until it exits.
 // It sends an error message if there is any error during
 // the API execution.
 func (s *Server) Wait(waitChan chan error) {
 	if err := s.serveAPI(); err != nil {
 		logrus.Errorf("ServeAPI error: %v", err)
 		waitChan <- err
 		return
 	}
 	waitChan <- nil
 }