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