encode.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  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. All query and
  25. // header values will be added on top of the request's existing values. Overwriting
  26. // duplicate values.
  27. func NewEncoder(path, query string, headers http.Header) (*Encoder, error) {
  28. parseQuery, err := url.ParseQuery(query)
  29. if err != nil {
  30. return nil, fmt.Errorf("failed to parse query string: %w", err)
  31. }
  32. e := &Encoder{
  33. path: []byte(path),
  34. rawPath: []byte(path),
  35. query: parseQuery,
  36. header: headers.Clone(),
  37. }
  38. return e, nil
  39. }
  40. // Encode returns a REST protocol encoder for encoding HTTP bindings.
  41. //
  42. // Due net/http requiring `Content-Length` to be specified on the http.Request#ContentLength directly. Encode
  43. // will look for whether the header is present, and if so will remove it and set the respective value on http.Request.
  44. //
  45. // Returns any error occurring during encoding.
  46. func (e *Encoder) Encode(req *http.Request) (*http.Request, error) {
  47. req.URL.Path, req.URL.RawPath = string(e.path), string(e.rawPath)
  48. req.URL.RawQuery = e.query.Encode()
  49. // net/http ignores Content-Length header and requires it to be set on http.Request
  50. if v := e.header.Get(contentLengthHeader); len(v) > 0 {
  51. iv, err := strconv.ParseInt(v, 10, 64)
  52. if err != nil {
  53. return nil, err
  54. }
  55. req.ContentLength = iv
  56. e.header.Del(contentLengthHeader)
  57. }
  58. req.Header = e.header
  59. return req, nil
  60. }
  61. // AddHeader returns a HeaderValue for appending to the given header name
  62. func (e *Encoder) AddHeader(key string) HeaderValue {
  63. return newHeaderValue(e.header, key, true)
  64. }
  65. // SetHeader returns a HeaderValue for setting the given header name
  66. func (e *Encoder) SetHeader(key string) HeaderValue {
  67. return newHeaderValue(e.header, key, false)
  68. }
  69. // Headers returns a Header used for encoding headers with the given prefix
  70. func (e *Encoder) Headers(prefix string) Headers {
  71. return Headers{
  72. header: e.header,
  73. prefix: strings.TrimSpace(prefix),
  74. }
  75. }
  76. // HasHeader returns if a header with the key specified exists with one or
  77. // more value.
  78. func (e Encoder) HasHeader(key string) bool {
  79. return len(e.header[key]) != 0
  80. }
  81. // SetURI returns a URIValue used for setting the given path key
  82. func (e *Encoder) SetURI(key string) URIValue {
  83. return newURIValue(&e.path, &e.rawPath, &e.pathBuffer, key)
  84. }
  85. // SetQuery returns a QueryValue used for setting the given query key
  86. func (e *Encoder) SetQuery(key string) QueryValue {
  87. return NewQueryValue(e.query, key, false)
  88. }
  89. // AddQuery returns a QueryValue used for appending the given query key
  90. func (e *Encoder) AddQuery(key string) QueryValue {
  91. return NewQueryValue(e.query, key, true)
  92. }
  93. // HasQuery returns if a query with the key specified exists with one or
  94. // more values.
  95. func (e *Encoder) HasQuery(key string) bool {
  96. return len(e.query.Get(key)) != 0
  97. }