github.com/pwn-term/docker@v0.0.0-20210616085119-6e977cce2565/moby/api/server/httputils/httputils.go (about) 1 package httputils // import "github.com/docker/docker/api/server/httputils" 2 3 import ( 4 "context" 5 "io" 6 "mime" 7 "net/http" 8 "strings" 9 10 "github.com/docker/docker/api/types" 11 "github.com/docker/docker/api/types/versions" 12 "github.com/docker/docker/errdefs" 13 "github.com/gorilla/mux" 14 "github.com/pkg/errors" 15 "github.com/sirupsen/logrus" 16 "google.golang.org/grpc/status" 17 ) 18 19 // APIVersionKey is the client's requested API version. 20 type APIVersionKey struct{} 21 22 // APIFunc is an adapter to allow the use of ordinary functions as Docker API endpoints. 23 // Any function that has the appropriate signature can be registered as an API endpoint (e.g. getVersion). 24 type APIFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error 25 26 // HijackConnection interrupts the http response writer to get the 27 // underlying connection and operate with it. 28 func HijackConnection(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) { 29 conn, _, err := w.(http.Hijacker).Hijack() 30 if err != nil { 31 return nil, nil, err 32 } 33 // Flush the options to make sure the client sets the raw mode 34 _, _ = conn.Write([]byte{}) 35 return conn, conn, nil 36 } 37 38 // CloseStreams ensures that a list for http streams are properly closed. 39 func CloseStreams(streams ...interface{}) { 40 for _, stream := range streams { 41 if tcpc, ok := stream.(interface { 42 CloseWrite() error 43 }); ok { 44 _ = tcpc.CloseWrite() 45 } else if closer, ok := stream.(io.Closer); ok { 46 _ = closer.Close() 47 } 48 } 49 } 50 51 // CheckForJSON makes sure that the request's Content-Type is application/json. 52 func CheckForJSON(r *http.Request) error { 53 ct := r.Header.Get("Content-Type") 54 55 // No Content-Type header is ok as long as there's no Body 56 if ct == "" { 57 if r.Body == nil || r.ContentLength == 0 { 58 return nil 59 } 60 } 61 62 // Otherwise it better be json 63 if matchesContentType(ct, "application/json") { 64 return nil 65 } 66 return errdefs.InvalidParameter(errors.Errorf("Content-Type specified (%s) must be 'application/json'", ct)) 67 } 68 69 // ParseForm ensures the request form is parsed even with invalid content types. 70 // If we don't do this, POST method without Content-type (even with empty body) will fail. 71 func ParseForm(r *http.Request) error { 72 if r == nil { 73 return nil 74 } 75 if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") { 76 return errdefs.InvalidParameter(err) 77 } 78 return nil 79 } 80 81 // VersionFromContext returns an API version from the context using APIVersionKey. 82 // It panics if the context value does not have version.Version type. 83 func VersionFromContext(ctx context.Context) string { 84 if ctx == nil { 85 return "" 86 } 87 88 if val := ctx.Value(APIVersionKey{}); val != nil { 89 return val.(string) 90 } 91 92 return "" 93 } 94 95 // MakeErrorHandler makes an HTTP handler that decodes a Docker error and 96 // returns it in the response. 97 func MakeErrorHandler(err error) http.HandlerFunc { 98 return func(w http.ResponseWriter, r *http.Request) { 99 statusCode := errdefs.GetHTTPErrorStatusCode(err) 100 vars := mux.Vars(r) 101 if apiVersionSupportsJSONErrors(vars["version"]) { 102 response := &types.ErrorResponse{ 103 Message: err.Error(), 104 } 105 _ = WriteJSON(w, statusCode, response) 106 } else { 107 http.Error(w, status.Convert(err).Message(), statusCode) 108 } 109 } 110 } 111 112 func apiVersionSupportsJSONErrors(version string) bool { 113 const firstAPIVersionWithJSONErrors = "1.23" 114 return version == "" || versions.GreaterThan(version, firstAPIVersionWithJSONErrors) 115 } 116 117 // matchesContentType validates the content type against the expected one 118 func matchesContentType(contentType, expectedType string) bool { 119 mimetype, _, err := mime.ParseMediaType(contentType) 120 if err != nil { 121 logrus.Errorf("Error parsing media type: %s error: %v", contentType, err) 122 } 123 return err == nil && mimetype == expectedType 124 }