Docker Server

Docker Server, aka API server, the main functionality is to accept the requests from client, and routes to according handlers. The handler then calls daemon services; backend interfaces of each router class defines interface between server and daemon.

Code is in api/server/

Factory

func New(cfg *Config) *Server{..}

CmdDaemon invokes it to get a new server.

api := apiserver.New(serverConfig)

Life Cycle

Server, Router, Route and HTTPServer

Wait()

func (s *Server) Wait(waitChan chan error) {
    if err := s.serveAPI(); err != nil {
        logrus.Errorf("ServeAPI error: %v", err)
        waitChan <- err
        return
    }
    waitChan <- nil
}

serveAPI loops through all initialized servers and spawns goroutine with Server method for each. It sets createMux() as Handler also.

func (s *Server) serveAPI() error {
    var chErrors = make(chan error, len(s.servers))
    for _, srv := range s.servers {
        srv.srv.Handler = s.routerSwapper
        go func(srv *HTTPServer) {
            var err error
            logrus.Infof("API listen on %s", srv.l.Addr())
            if err = srv.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") {
                err = nil
            }
            chErrors <- err
        }(srv)
    }

    for i := 0; i < len(s.servers); i++ {
        err := <-chErrors
        if err != nil {
            return err
        }
    }

    return nil
}

Router Initialization

Docker employes gorilla/mux, a powerful URL router and dispatcher, to implement routing logic.

Daemon.initRouter

Server.initRouter

InitRouter initializes the list of routers for the server. This method also enables the Go profiler if enableProfiler is true.

func (s *Server) InitRouter(enableProfiler bool, routers ...router.Router) {
    for _, r := range routers {
        s.routers = append(s.routers, r)
    }

    m := s.createMux()
    if enableProfiler {
        profilerSetup(m)
    }
    s.routerSwapper = &routerSwapper{
        router: m,
    }
}

createMux initializes the main router the server uses.

func (s *Server) createMux() *mux.Router {
    m := mux.NewRouter()

    logrus.Debugf("Registering routers")
    for _, apiRouter := range s.routers {
        for _, r := range apiRouter.Routes() {
            f := s.makeHTTPHandler(r.Handler())

            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)
        }
    }

    return m
}
func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        // 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.
        ctx := context.Background()
        handlerFunc := s.handleWithGlobalMiddlewares(handler)

        vars := mux.Vars(r)
        if vars == nil {
            vars = make(map[string]string)
        }

        if err := handlerFunc(ctx, w, r, vars); err != nil {
            logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
            httputils.WriteError(w, err)
        }
    }
}

Router