src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

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 }