middleware.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package server
  2. import (
  3. "net/http"
  4. "runtime"
  5. "strings"
  6. "github.com/Sirupsen/logrus"
  7. "github.com/docker/docker/api"
  8. "github.com/docker/docker/autogen/dockerversion"
  9. "github.com/docker/docker/errors"
  10. "github.com/docker/docker/pkg/version"
  11. "golang.org/x/net/context"
  12. )
  13. // apiVersionKey is the client's requested API version.
  14. const apiVersionKey = "api-version"
  15. // middleware is an adapter to allow the use of ordinary functions as Docker API filters.
  16. // Any function that has the appropriate signature can be register as a middleware.
  17. type middleware func(handler HTTPAPIFunc) HTTPAPIFunc
  18. // loggingMiddleware logs each request when logging is enabled.
  19. func (s *Server) loggingMiddleware(handler HTTPAPIFunc) HTTPAPIFunc {
  20. return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  21. if s.cfg.Logging {
  22. logrus.Infof("%s %s", r.Method, r.RequestURI)
  23. }
  24. return handler(ctx, w, r, vars)
  25. }
  26. }
  27. // userAgentMiddleware checks the User-Agent header looking for a valid docker client spec.
  28. func (s *Server) userAgentMiddleware(handler HTTPAPIFunc) HTTPAPIFunc {
  29. return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  30. if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
  31. dockerVersion := version.Version(s.cfg.Version)
  32. userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
  33. // v1.20 onwards includes the GOOS of the client after the version
  34. // such as Docker/1.7.0 (linux)
  35. if len(userAgent) == 2 && strings.Contains(userAgent[1], " ") {
  36. userAgent[1] = strings.Split(userAgent[1], " ")[0]
  37. }
  38. if len(userAgent) == 2 && !dockerVersion.Equal(version.Version(userAgent[1])) {
  39. logrus.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
  40. }
  41. }
  42. return handler(ctx, w, r, vars)
  43. }
  44. }
  45. // corsMiddleware sets the CORS header expectations in the server.
  46. func (s *Server) corsMiddleware(handler HTTPAPIFunc) HTTPAPIFunc {
  47. return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  48. // If "api-cors-header" is not given, but "api-enable-cors" is true, we set cors to "*"
  49. // otherwise, all head values will be passed to HTTP handler
  50. corsHeaders := s.cfg.CorsHeaders
  51. if corsHeaders == "" && s.cfg.EnableCors {
  52. corsHeaders = "*"
  53. }
  54. if corsHeaders != "" {
  55. writeCorsHeaders(w, r, corsHeaders)
  56. }
  57. return handler(ctx, w, r, vars)
  58. }
  59. }
  60. // versionMiddleware checks the api version requirements before passing the request to the server handler.
  61. func versionMiddleware(handler HTTPAPIFunc) HTTPAPIFunc {
  62. return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  63. apiVersion := version.Version(vars["version"])
  64. if apiVersion == "" {
  65. apiVersion = api.Version
  66. }
  67. if apiVersion.GreaterThan(api.Version) {
  68. return errors.ErrorCodeNewerClientVersion.WithArgs(apiVersion, api.Version)
  69. }
  70. if apiVersion.LessThan(api.MinVersion) {
  71. return errors.ErrorCodeOldClientVersion.WithArgs(apiVersion, api.Version)
  72. }
  73. w.Header().Set("Server", "Docker/"+dockerversion.VERSION+" ("+runtime.GOOS+")")
  74. ctx = context.WithValue(ctx, apiVersionKey, apiVersion)
  75. return handler(ctx, w, r, vars)
  76. }
  77. }
  78. // handleWithGlobalMiddlwares wraps the handler function for a request with
  79. // the server's global middlewares. The order of the middlewares is backwards,
  80. // meaning that the first in the list will be evaludated last.
  81. //
  82. // Example: handleWithGlobalMiddlewares(s.getContainersName)
  83. //
  84. // s.loggingMiddleware(
  85. // s.userAgentMiddleware(
  86. // s.corsMiddleware(
  87. // versionMiddleware(s.getContainersName)
  88. // )
  89. // )
  90. // )
  91. func (s *Server) handleWithGlobalMiddlewares(handler HTTPAPIFunc) HTTPAPIFunc {
  92. middlewares := []middleware{
  93. versionMiddleware,
  94. s.corsMiddleware,
  95. s.userAgentMiddleware,
  96. s.loggingMiddleware,
  97. }
  98. h := handler
  99. for _, m := range middlewares {
  100. h = m(h)
  101. }
  102. return h
  103. }
  104. // versionFromContext returns an API version from the context using apiVersionKey.
  105. // It panics if the context value does not have version.Version type.
  106. func versionFromContext(ctx context.Context) (ver version.Version) {
  107. if ctx == nil {
  108. return
  109. }
  110. val := ctx.Value(apiVersionKey)
  111. if val == nil {
  112. return
  113. }
  114. return val.(version.Version)
  115. }