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