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  }