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