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