middleware.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package server
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "io/ioutil"
  6. "net/http"
  7. "runtime"
  8. "strings"
  9. "github.com/Sirupsen/logrus"
  10. "github.com/docker/docker/api"
  11. "github.com/docker/docker/api/server/httputils"
  12. "github.com/docker/docker/errors"
  13. "github.com/docker/docker/pkg/version"
  14. dockerversion "github.com/docker/docker/version"
  15. "golang.org/x/net/context"
  16. )
  17. // middleware is an adapter to allow the use of ordinary functions as Docker API filters.
  18. // Any function that has the appropriate signature can be register as a middleware.
  19. type middleware func(handler httputils.APIFunc) httputils.APIFunc
  20. // loggingMiddleware logs each request when logging is enabled.
  21. func (s *Server) loggingMiddleware(handler httputils.APIFunc) httputils.APIFunc {
  22. return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  23. if s.cfg.Logging {
  24. logrus.Infof("%s %s", r.Method, r.RequestURI)
  25. }
  26. return handler(ctx, w, r, vars)
  27. }
  28. }
  29. // debugRequestMiddleware dumps the request to logger
  30. // This is implemented separately from `loggingMiddleware` so that we don't have to
  31. // check the logging level or have httputil.DumpRequest called on each request.
  32. // Instead the middleware is only injected when the logging level is set to debug
  33. func (s *Server) debugRequestMiddleware(handler httputils.APIFunc) httputils.APIFunc {
  34. return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  35. if s.cfg.Logging && r.Method == "POST" {
  36. if err := httputils.CheckForJSON(r); err == nil {
  37. var buf bytes.Buffer
  38. if _, err := buf.ReadFrom(r.Body); err == nil {
  39. r.Body.Close()
  40. r.Body = ioutil.NopCloser(&buf)
  41. var postForm map[string]interface{}
  42. if err := json.Unmarshal(buf.Bytes(), &postForm); err == nil {
  43. if _, exists := postForm["password"]; exists {
  44. postForm["password"] = "*****"
  45. }
  46. logrus.Debugf("form data: %q", postForm)
  47. }
  48. }
  49. }
  50. }
  51. return handler(ctx, w, r, vars)
  52. }
  53. }
  54. // userAgentMiddleware checks the User-Agent header looking for a valid docker client spec.
  55. func (s *Server) userAgentMiddleware(handler httputils.APIFunc) httputils.APIFunc {
  56. return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  57. if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
  58. dockerVersion := version.Version(s.cfg.Version)
  59. userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
  60. // v1.20 onwards includes the GOOS of the client after the version
  61. // such as Docker/1.7.0 (linux)
  62. if len(userAgent) == 2 && strings.Contains(userAgent[1], " ") {
  63. userAgent[1] = strings.Split(userAgent[1], " ")[0]
  64. }
  65. if len(userAgent) == 2 && !dockerVersion.Equal(version.Version(userAgent[1])) {
  66. logrus.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
  67. }
  68. }
  69. return handler(ctx, w, r, vars)
  70. }
  71. }
  72. // corsMiddleware sets the CORS header expectations in the server.
  73. func (s *Server) corsMiddleware(handler httputils.APIFunc) httputils.APIFunc {
  74. return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  75. // If "api-cors-header" is not given, but "api-enable-cors" is true, we set cors to "*"
  76. // otherwise, all head values will be passed to HTTP handler
  77. corsHeaders := s.cfg.CorsHeaders
  78. if corsHeaders == "" && s.cfg.EnableCors {
  79. corsHeaders = "*"
  80. }
  81. if corsHeaders != "" {
  82. writeCorsHeaders(w, r, corsHeaders)
  83. }
  84. return handler(ctx, w, r, vars)
  85. }
  86. }
  87. // versionMiddleware checks the api version requirements before passing the request to the server handler.
  88. func versionMiddleware(handler httputils.APIFunc) httputils.APIFunc {
  89. return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  90. apiVersion := version.Version(vars["version"])
  91. if apiVersion == "" {
  92. apiVersion = api.Version
  93. }
  94. if apiVersion.GreaterThan(api.Version) {
  95. return errors.ErrorCodeNewerClientVersion.WithArgs(apiVersion, api.Version)
  96. }
  97. if apiVersion.LessThan(api.MinVersion) {
  98. return errors.ErrorCodeOldClientVersion.WithArgs(apiVersion, api.Version)
  99. }
  100. w.Header().Set("Server", "Docker/"+dockerversion.VERSION+" ("+runtime.GOOS+")")
  101. ctx = context.WithValue(ctx, httputils.APIVersionKey, apiVersion)
  102. return handler(ctx, w, r, vars)
  103. }
  104. }
  105. // handleWithGlobalMiddlwares wraps the handler function for a request with
  106. // the server's global middlewares. The order of the middlewares is backwards,
  107. // meaning that the first in the list will be evaludated last.
  108. //
  109. // Example: handleWithGlobalMiddlewares(s.getContainersName)
  110. //
  111. // s.loggingMiddleware(
  112. // s.userAgentMiddleware(
  113. // s.corsMiddleware(
  114. // versionMiddleware(s.getContainersName)
  115. // )
  116. // )
  117. // )
  118. // )
  119. func (s *Server) handleWithGlobalMiddlewares(handler httputils.APIFunc) httputils.APIFunc {
  120. middlewares := []middleware{
  121. versionMiddleware,
  122. s.corsMiddleware,
  123. s.userAgentMiddleware,
  124. s.loggingMiddleware,
  125. }
  126. // Only want this on debug level
  127. // this is separate from the logging middleware so that we can do this check here once,
  128. // rather than for each request.
  129. if logrus.GetLevel() == logrus.DebugLevel {
  130. middlewares = append(middlewares, s.debugRequestMiddleware)
  131. }
  132. h := handler
  133. for _, m := range middlewares {
  134. h = m(h)
  135. }
  136. return h
  137. }