github.com/ncdc/docker@v0.10.1-0.20160129113957-6c6729ef5b74/api/server/server.go (about) 1 package server 2 3 import ( 4 "crypto/tls" 5 "net" 6 "net/http" 7 "strings" 8 9 "github.com/Sirupsen/logrus" 10 "github.com/docker/docker/api/server/httputils" 11 "github.com/docker/docker/api/server/router" 12 "github.com/docker/docker/api/server/router/build" 13 "github.com/docker/docker/api/server/router/container" 14 "github.com/docker/docker/api/server/router/local" 15 "github.com/docker/docker/api/server/router/network" 16 "github.com/docker/docker/api/server/router/system" 17 "github.com/docker/docker/api/server/router/volume" 18 "github.com/docker/docker/daemon" 19 "github.com/docker/docker/pkg/authorization" 20 "github.com/docker/docker/utils" 21 "github.com/docker/go-connections/sockets" 22 "github.com/gorilla/mux" 23 "golang.org/x/net/context" 24 ) 25 26 // versionMatcher defines a variable matcher to be parsed by the router 27 // when a request is about to be served. 28 const versionMatcher = "/v{version:[0-9.]+}" 29 30 // Config provides the configuration for the API server 31 type Config struct { 32 Logging bool 33 EnableCors bool 34 CorsHeaders string 35 AuthorizationPluginNames []string 36 Version string 37 SocketGroup string 38 TLSConfig *tls.Config 39 Addrs []Addr 40 } 41 42 // Server contains instance details for the server 43 type Server struct { 44 cfg *Config 45 servers []*HTTPServer 46 routers []router.Router 47 authZPlugins []authorization.Plugin 48 routerSwapper *routerSwapper 49 } 50 51 // Addr contains string representation of address and its protocol (tcp, unix...). 52 type Addr struct { 53 Proto string 54 Addr string 55 } 56 57 // New returns a new instance of the server based on the specified configuration. 58 // It allocates resources which will be needed for ServeAPI(ports, unix-sockets). 59 func New(cfg *Config) (*Server, error) { 60 s := &Server{ 61 cfg: cfg, 62 } 63 for _, addr := range cfg.Addrs { 64 srv, err := s.newServer(addr.Proto, addr.Addr) 65 if err != nil { 66 return nil, err 67 } 68 logrus.Debugf("Server created for HTTP on %s (%s)", addr.Proto, addr.Addr) 69 s.servers = append(s.servers, srv...) 70 } 71 return s, nil 72 } 73 74 // Close closes servers and thus stop receiving requests 75 func (s *Server) Close() { 76 for _, srv := range s.servers { 77 if err := srv.Close(); err != nil { 78 logrus.Error(err) 79 } 80 } 81 } 82 83 // serveAPI loops through all initialized servers and spawns goroutine 84 // with Server method for each. It sets createMux() as Handler also. 85 func (s *Server) serveAPI() error { 86 s.initRouterSwapper() 87 88 var chErrors = make(chan error, len(s.servers)) 89 for _, srv := range s.servers { 90 srv.srv.Handler = s.routerSwapper 91 go func(srv *HTTPServer) { 92 var err error 93 logrus.Infof("API listen on %s", srv.l.Addr()) 94 if err = srv.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") { 95 err = nil 96 } 97 chErrors <- err 98 }(srv) 99 } 100 101 for i := 0; i < len(s.servers); i++ { 102 err := <-chErrors 103 if err != nil { 104 return err 105 } 106 } 107 108 return nil 109 } 110 111 // HTTPServer contains an instance of http server and the listener. 112 // srv *http.Server, contains configuration to create a http server and a mux router with all api end points. 113 // l net.Listener, is a TCP or Socket listener that dispatches incoming request to the router. 114 type HTTPServer struct { 115 srv *http.Server 116 l net.Listener 117 } 118 119 // Serve starts listening for inbound requests. 120 func (s *HTTPServer) Serve() error { 121 return s.srv.Serve(s.l) 122 } 123 124 // Close closes the HTTPServer from listening for the inbound requests. 125 func (s *HTTPServer) Close() error { 126 return s.l.Close() 127 } 128 129 func writeCorsHeaders(w http.ResponseWriter, r *http.Request, corsHeaders string) { 130 logrus.Debugf("CORS header is enabled and set to: %s", corsHeaders) 131 w.Header().Add("Access-Control-Allow-Origin", corsHeaders) 132 w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth") 133 w.Header().Add("Access-Control-Allow-Methods", "HEAD, GET, POST, DELETE, PUT, OPTIONS") 134 } 135 136 func (s *Server) initTCPSocket(addr string) (l net.Listener, err error) { 137 if s.cfg.TLSConfig == nil || s.cfg.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert { 138 logrus.Warn("/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") 139 } 140 if l, err = sockets.NewTCPSocket(addr, s.cfg.TLSConfig); err != nil { 141 return nil, err 142 } 143 if err := allocateDaemonPort(addr); err != nil { 144 return nil, err 145 } 146 return 147 } 148 149 func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc { 150 return func(w http.ResponseWriter, r *http.Request) { 151 // log the handler call 152 logrus.Debugf("Calling %s %s", r.Method, r.URL.Path) 153 154 // Define the context that we'll pass around to share info 155 // like the docker-request-id. 156 // 157 // The 'context' will be used for global data that should 158 // apply to all requests. Data that is specific to the 159 // immediate function being called should still be passed 160 // as 'args' on the function call. 161 ctx := context.Background() 162 handlerFunc := s.handleWithGlobalMiddlewares(handler) 163 164 vars := mux.Vars(r) 165 if vars == nil { 166 vars = make(map[string]string) 167 } 168 169 if err := handlerFunc(ctx, w, r, vars); err != nil { 170 logrus.Errorf("Handler for %s %s returned error: %s", r.Method, r.URL.Path, utils.GetErrorMessage(err)) 171 httputils.WriteError(w, err) 172 } 173 } 174 } 175 176 // InitRouters initializes a list of routers for the server. 177 func (s *Server) InitRouters(d *daemon.Daemon) { 178 s.addRouter(container.NewRouter(d)) 179 s.addRouter(local.NewRouter(d)) 180 s.addRouter(network.NewRouter(d)) 181 s.addRouter(system.NewRouter(d)) 182 s.addRouter(volume.NewRouter(d)) 183 s.addRouter(build.NewRouter(d)) 184 } 185 186 // addRouter adds a new router to the server. 187 func (s *Server) addRouter(r router.Router) { 188 s.routers = append(s.routers, r) 189 } 190 191 // createMux initializes the main router the server uses. 192 func (s *Server) createMux() *mux.Router { 193 m := mux.NewRouter() 194 if utils.IsDebugEnabled() { 195 profilerSetup(m, "/debug/") 196 } 197 198 logrus.Debugf("Registering routers") 199 for _, apiRouter := range s.routers { 200 for _, r := range apiRouter.Routes() { 201 f := s.makeHTTPHandler(r.Handler()) 202 203 logrus.Debugf("Registering %s, %s", r.Method(), r.Path()) 204 m.Path(versionMatcher + r.Path()).Methods(r.Method()).Handler(f) 205 m.Path(r.Path()).Methods(r.Method()).Handler(f) 206 } 207 } 208 209 return m 210 } 211 212 // Wait blocks the server goroutine until it exits. 213 // It sends an error message if there is any error during 214 // the API execution. 215 func (s *Server) Wait(waitChan chan error) { 216 if err := s.serveAPI(); err != nil { 217 logrus.Errorf("ServeAPI error: %v", err) 218 waitChan <- err 219 return 220 } 221 waitChan <- nil 222 } 223 224 func (s *Server) initRouterSwapper() { 225 s.routerSwapper = &routerSwapper{ 226 router: s.createMux(), 227 } 228 } 229 230 // Reload reads configuration changes and modifies the 231 // server according to those changes. 232 // Currently, only the --debug configuration is taken into account. 233 func (s *Server) Reload(config *daemon.Config) { 234 debugEnabled := utils.IsDebugEnabled() 235 switch { 236 case debugEnabled && !config.Debug: // disable debug 237 utils.DisableDebug() 238 s.routerSwapper.Swap(s.createMux()) 239 case config.Debug && !debugEnabled: // enable debug 240 utils.EnableDebug() 241 s.routerSwapper.Swap(s.createMux()) 242 } 243 }