github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/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/errdefs" 11 "github.com/pkg/errors" 12 "github.com/sirupsen/logrus" 13 ) 14 15 // APIVersionKey is the client's requested API version. 16 type APIVersionKey struct{} 17 18 // APIFunc is an adapter to allow the use of ordinary functions as Docker API endpoints. 19 // Any function that has the appropriate signature can be registered as an API endpoint (e.g. getVersion). 20 type APIFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error 21 22 // HijackConnection interrupts the http response writer to get the 23 // underlying connection and operate with it. 24 func HijackConnection(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) { 25 conn, _, err := w.(http.Hijacker).Hijack() 26 if err != nil { 27 return nil, nil, err 28 } 29 // Flush the options to make sure the client sets the raw mode 30 _, _ = conn.Write([]byte{}) 31 return conn, conn, nil 32 } 33 34 // CloseStreams ensures that a list for http streams are properly closed. 35 func CloseStreams(streams ...interface{}) { 36 for _, stream := range streams { 37 if tcpc, ok := stream.(interface { 38 CloseWrite() error 39 }); ok { 40 _ = tcpc.CloseWrite() 41 } else if closer, ok := stream.(io.Closer); ok { 42 _ = closer.Close() 43 } 44 } 45 } 46 47 // CheckForJSON makes sure that the request's Content-Type is application/json. 48 func CheckForJSON(r *http.Request) error { 49 ct := r.Header.Get("Content-Type") 50 51 // No Content-Type header is ok as long as there's no Body 52 if ct == "" { 53 if r.Body == nil || r.ContentLength == 0 { 54 return nil 55 } 56 } 57 58 // Otherwise it better be json 59 if matchesContentType(ct, "application/json") { 60 return nil 61 } 62 return errdefs.InvalidParameter(errors.Errorf("Content-Type specified (%s) must be 'application/json'", ct)) 63 } 64 65 // ParseForm ensures the request form is parsed even with invalid content types. 66 // If we don't do this, POST method without Content-type (even with empty body) will fail. 67 func ParseForm(r *http.Request) error { 68 if r == nil { 69 return nil 70 } 71 if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") { 72 return errdefs.InvalidParameter(err) 73 } 74 return nil 75 } 76 77 // VersionFromContext returns an API version from the context using APIVersionKey. 78 // It panics if the context value does not have version.Version type. 79 func VersionFromContext(ctx context.Context) string { 80 if ctx == nil { 81 return "" 82 } 83 84 if val := ctx.Value(APIVersionKey{}); val != nil { 85 return val.(string) 86 } 87 88 return "" 89 } 90 91 // matchesContentType validates the content type against the expected one 92 func matchesContentType(contentType, expectedType string) bool { 93 mimetype, _, err := mime.ParseMediaType(contentType) 94 if err != nil { 95 logrus.Errorf("Error parsing media type: %s error: %v", contentType, err) 96 } 97 return err == nil && mimetype == expectedType 98 }