github.com/portworx/docker@v1.12.1/api/server/httputils/httputils.go (about)

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