httputils.go 2.4 KB

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