encode.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. package httpbinding
  2. import (
  3. "fmt"
  4. "net/http"
  5. "net/url"
  6. "strconv"
  7. "strings"
  8. )
  9. const (
  10. contentLengthHeader = "Content-Length"
  11. floatNaN = "NaN"
  12. floatInfinity = "Infinity"
  13. floatNegInfinity = "-Infinity"
  14. )
  15. // An Encoder provides encoding of REST URI path, query, and header components
  16. // of an HTTP request. Can also encode a stream as the payload.
  17. //
  18. // Does not support SetFields.
  19. type Encoder struct {
  20. path, rawPath, pathBuffer []byte
  21. query url.Values
  22. header http.Header
  23. }
  24. // NewEncoder creates a new encoder from the passed in request. It assumes that
  25. // raw path contains no valuable information at this point, so it passes in path
  26. // as path and raw path for subsequent trans
  27. func NewEncoder(path, query string, headers http.Header) (*Encoder, error) {
  28. return NewEncoderWithRawPath(path, path, query, headers)
  29. }
  30. // NewHTTPBindingEncoder creates a new encoder from the passed in request. All query and
  31. // header values will be added on top of the request's existing values. Overwriting
  32. // duplicate values.
  33. func NewEncoderWithRawPath(path, rawPath, query string, headers http.Header) (*Encoder, error) {
  34. parseQuery, err := url.ParseQuery(query)
  35. if err != nil {
  36. return nil, fmt.Errorf("failed to parse query string: %w", err)
  37. }
  38. e := &Encoder{
  39. path: []byte(path),
  40. rawPath: []byte(rawPath),
  41. query: parseQuery,
  42. header: headers.Clone(),
  43. }
  44. return e, nil
  45. }
  46. // Encode returns a REST protocol encoder for encoding HTTP bindings.
  47. //
  48. // Due net/http requiring `Content-Length` to be specified on the http.Request#ContentLength directly. Encode
  49. // will look for whether the header is present, and if so will remove it and set the respective value on http.Request.
  50. //
  51. // Returns any error occurring during encoding.
  52. func (e *Encoder) Encode(req *http.Request) (*http.Request, error) {
  53. req.URL.Path, req.URL.RawPath = string(e.path), string(e.rawPath)
  54. req.URL.RawQuery = e.query.Encode()
  55. // net/http ignores Content-Length header and requires it to be set on http.Request
  56. if v := e.header.Get(contentLengthHeader); len(v) > 0 {
  57. iv, err := strconv.ParseInt(v, 10, 64)
  58. if err != nil {
  59. return nil, err
  60. }
  61. req.ContentLength = iv
  62. e.header.Del(contentLengthHeader)
  63. }
  64. req.Header = e.header
  65. return req, nil
  66. }
  67. // AddHeader returns a HeaderValue for appending to the given header name
  68. func (e *Encoder) AddHeader(key string) HeaderValue {
  69. return newHeaderValue(e.header, key, true)
  70. }
  71. // SetHeader returns a HeaderValue for setting the given header name
  72. func (e *Encoder) SetHeader(key string) HeaderValue {
  73. return newHeaderValue(e.header, key, false)
  74. }
  75. // Headers returns a Header used for encoding headers with the given prefix
  76. func (e *Encoder) Headers(prefix string) Headers {
  77. return Headers{
  78. header: e.header,
  79. prefix: strings.TrimSpace(prefix),
  80. }
  81. }
  82. // HasHeader returns if a header with the key specified exists with one or
  83. // more value.
  84. func (e Encoder) HasHeader(key string) bool {
  85. return len(e.header[key]) != 0
  86. }
  87. // SetURI returns a URIValue used for setting the given path key
  88. func (e *Encoder) SetURI(key string) URIValue {
  89. return newURIValue(&e.path, &e.rawPath, &e.pathBuffer, key)
  90. }
  91. // SetQuery returns a QueryValue used for setting the given query key
  92. func (e *Encoder) SetQuery(key string) QueryValue {
  93. return NewQueryValue(e.query, key, false)
  94. }
  95. // AddQuery returns a QueryValue used for appending the given query key
  96. func (e *Encoder) AddQuery(key string) QueryValue {
  97. return NewQueryValue(e.query, key, true)
  98. }
  99. // HasQuery returns if a query with the key specified exists with one or
  100. // more values.
  101. func (e *Encoder) HasQuery(key string) bool {
  102. return len(e.query.Get(key)) != 0
  103. }