encode.go raw

   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  }
 124