code.dwrz.net

Go monorepo.
Log | Files | Refs

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 }