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