github.com/sams1990/dockerrepo@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  }