server.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. package server // import "github.com/docker/docker/api/server"
  2. import (
  3. "context"
  4. "net/http"
  5. "github.com/containerd/log"
  6. "github.com/docker/docker/api/server/httpstatus"
  7. "github.com/docker/docker/api/server/httputils"
  8. "github.com/docker/docker/api/server/middleware"
  9. "github.com/docker/docker/api/server/router"
  10. "github.com/docker/docker/api/server/router/debug"
  11. "github.com/docker/docker/dockerversion"
  12. "github.com/gorilla/mux"
  13. "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
  14. )
  15. // versionMatcher defines a variable matcher to be parsed by the router
  16. // when a request is about to be served.
  17. const versionMatcher = "/v{version:[0-9.]+}"
  18. // Server contains instance details for the server
  19. type Server struct {
  20. middlewares []middleware.Middleware
  21. }
  22. // UseMiddleware appends a new middleware to the request chain.
  23. // This needs to be called before the API routes are configured.
  24. func (s *Server) UseMiddleware(m middleware.Middleware) {
  25. s.middlewares = append(s.middlewares, m)
  26. }
  27. func (s *Server) makeHTTPHandler(handler httputils.APIFunc, operation string) http.HandlerFunc {
  28. return otelhttp.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  29. // Define the context that we'll pass around to share info
  30. // like the docker-request-id.
  31. //
  32. // The 'context' will be used for global data that should
  33. // apply to all requests. Data that is specific to the
  34. // immediate function being called should still be passed
  35. // as 'args' on the function call.
  36. // use intermediate variable to prevent "should not use basic type
  37. // string as key in context.WithValue" golint errors
  38. ctx := context.WithValue(r.Context(), dockerversion.UAStringKey{}, r.Header.Get("User-Agent"))
  39. r = r.WithContext(ctx)
  40. handlerFunc := s.handlerWithGlobalMiddlewares(handler)
  41. vars := mux.Vars(r)
  42. if vars == nil {
  43. vars = make(map[string]string)
  44. }
  45. if err := handlerFunc(ctx, w, r, vars); err != nil {
  46. statusCode := httpstatus.FromError(err)
  47. if statusCode >= 500 {
  48. log.G(ctx).Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
  49. }
  50. makeErrorHandler(err)(w, r)
  51. }
  52. }), operation).ServeHTTP
  53. }
  54. type pageNotFoundError struct{}
  55. func (pageNotFoundError) Error() string {
  56. return "page not found"
  57. }
  58. func (pageNotFoundError) NotFound() {}
  59. // CreateMux returns a new mux with all the routers registered.
  60. func (s *Server) CreateMux(routers ...router.Router) *mux.Router {
  61. m := mux.NewRouter()
  62. log.G(context.TODO()).Debug("Registering routers")
  63. for _, apiRouter := range routers {
  64. for _, r := range apiRouter.Routes() {
  65. f := s.makeHTTPHandler(r.Handler(), r.Method()+" "+r.Path())
  66. log.G(context.TODO()).Debugf("Registering %s, %s", r.Method(), r.Path())
  67. m.Path(versionMatcher + r.Path()).Methods(r.Method()).Handler(f)
  68. m.Path(r.Path()).Methods(r.Method()).Handler(f)
  69. }
  70. }
  71. debugRouter := debug.NewRouter()
  72. for _, r := range debugRouter.Routes() {
  73. f := s.makeHTTPHandler(r.Handler(), r.Method()+" "+r.Path())
  74. m.Path("/debug" + r.Path()).Handler(f)
  75. }
  76. notFoundHandler := makeErrorHandler(pageNotFoundError{})
  77. m.HandleFunc(versionMatcher+"/{path:.*}", notFoundHandler)
  78. m.NotFoundHandler = notFoundHandler
  79. m.MethodNotAllowedHandler = notFoundHandler
  80. return m
  81. }