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