httputils.go 3.0 KB

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