Documentation ¶
Overview ¶
Package httpbp provides Baseplate specific helpers and integrations for http services using go-kit.
Index ¶
- Constants
- func BuildEncodeTemplatedResponse(t *template.Template) httpgk.EncodeResponseFunc
- func EncodeJSONResponse(_ context.Context, w http.ResponseWriter, response interface{}) error
- func EncodeTemplatedResponse(_ context.Context, w http.ResponseWriter, response interface{}, ...) error
- func GetHeader(ctx context.Context, key HeaderContextKey) (header string, ok bool)
- func InjectTrustedContext(ctx context.Context, t HeaderTrustHandler, r *http.Request) context.Context
- func PopulateRequestContext(t HeaderTrustHandler) httpgk.RequestFunc
- func SetHeader(ctx context.Context, key HeaderContextKey, value string) context.Context
- type AlwaysTrustHeaders
- type BaseResponse
- func (r *BaseResponse) ClearCookies()
- func (r BaseResponse) Cookies() []*http.Cookie
- func (r BaseResponse) Err() error
- func (r BaseResponse) Headers() http.Header
- func (r *BaseResponse) SetCode(code int)
- func (r *BaseResponse) SetCookie(cookie *http.Cookie)
- func (r *BaseResponse) SetError(e error)
- func (r BaseResponse) StatusCode() int
- type EdgeContextHeaders
- type ErrorResponse
- type HTTPError
- type HeaderContextKey
- type HeaderTrustHandler
- type Headers
- type NeverTrustHeaders
- type ResponseCookies
- type SpanHeaders
- type TrustHeaderSignature
- func (h TrustHeaderSignature) SignEdgeContextHeader(headers EdgeContextHeaders, expiresIn time.Duration) (string, error)
- func (h TrustHeaderSignature) SignSpanHeaders(headers SpanHeaders, expiresIn time.Duration) (string, error)
- func (h TrustHeaderSignature) TrustEdgeContext(r *http.Request) bool
- func (h TrustHeaderSignature) TrustSpan(r *http.Request) bool
- func (h TrustHeaderSignature) VerifyEdgeContextHeader(headers EdgeContextHeaders, signature string) (bool, error)
- func (h TrustHeaderSignature) VerifySpanHeaders(headers SpanHeaders, signature string) (bool, error)
- type TrustHeaderSignatureArgs
Examples ¶
Constants ¶
const ( // EdgeContextHeader is the key use to get the raw edge context from // the HTTP request headers. EdgeContextHeader = "X-Edge-Request" // ParentIDHeader is the key use to get the span parent ID from // the HTTP request headers. ParentIDHeader = "X-Parent" // SpanIDHeader is the key use to get the span ID from the HTTP // request headers. SpanIDHeader = "X-Span" // SpanFlagsHeader is the key use to get the span flags from the HTTP // request headers. SpanFlagsHeader = "X-Flags" // SpanSampledHeader is the key use to get the sampled flag from the // HTTP request headers. SpanSampledHeader = "X-Sampled" // TraceIDHeader is the key use to get the trace ID from the HTTP // request headers. TraceIDHeader = "X-Trace" )
const ( // ContentTypeHeader is the 'Content-Type' header key. ContentTypeHeader = "Content-Type" // JSONContentType is the Content-Type header for JSON responses. JSONContentType = "application/json; charset=utf-8" // HTMLContentType is the Content-Type header for HTML responses. HTMLContentType = "text/html; charset=utf-8" )
const ( // EdgeContextSignatureHeader is the key use to get the signature for // the edge context headers from the HTTP request headers. EdgeContextSignatureHeader = "X-Edge-Request-Signature" // SpanSignatureHeader is the key use to get the signature for // the span headers from the HTTP request headers. SpanSignatureHeader = "X-Span-Signature" )
Variables ¶
This section is empty.
Functions ¶
func BuildEncodeTemplatedResponse ¶
func BuildEncodeTemplatedResponse(t *template.Template) httpgk.EncodeResponseFunc
BuildEncodeTemplatedResponse returns a function that implements go-kits http.EncodeResponseFunc interface and wraps EncodeTemplatedResponse with the template passed in.
func EncodeJSONResponse ¶
func EncodeJSONResponse(_ context.Context, w http.ResponseWriter, response interface{}) error
EncodeJSONResponse implements go-kit's http.EncodeResponseFunc interface and encodes the given response as json.
If the response implements go-kit's http.Headerer interface, then the headers will be applied to the response, after the Content-Type header is set.
If the response implements the ResponseCookie interface, then any cookies returned will be applied to the response, after the headers are set.
If the response implements the ErrorResponse interface, then an error response will be returned if Err() is non-nil. You can use the HTTPError object to customize the error response.
If the response implements go-kit's http.StatusCoder interface, then the status code returned will be used rather than 200. If a response implements this but returns the default integer value of 0, then the code will still be set to 200. If the response also implements the ErrorResponse interface, then this status code is ignored in favor of the error status code.
func EncodeTemplatedResponse ¶
func EncodeTemplatedResponse(_ context.Context, w http.ResponseWriter, response interface{}, t *template.Template) error
EncodeTemplatedResponse encodes the given response as text/html with the given template.
This method does not implement the go-kit http.EncodeResponseFunc interface, if you want to use this with go-kit, use BuildEncodeTemplatedResponse to return a function that wraps EncodeTemplatedResponse with the a single template and does implement the http.EncodeResponseFunc interface.
If the response implements go-kit's http.Headerer interface, then the headers will be applied to the response, after the Content-Type header is set.
If the response implements the ResponseCookie interface, then any cookies returned will be applied to the response, after the headers are set.
If the response implements the ErrorResponse interface, then an error response will be returned if Err() is non-nil. You can use the HTTPError object to customize the error response.
If the response implements go-kit's http.StatusCoder interface, then the status code returned will be used rather than 200. If a response implements this but returns the default integer value of 0, then the code will still be set to 200. If the response also implements the ErrorResponse interface, then this status code is ignored in favor of the error status code.
func GetHeader ¶
func GetHeader(ctx context.Context, key HeaderContextKey) (header string, ok bool)
GetHeader returns the HTTP header stored on the context at key.
func InjectTrustedContext ¶
func InjectTrustedContext(ctx context.Context, t HeaderTrustHandler, r *http.Request) context.Context
InjectTrustedContext takes baseplate HTTP headers from the request, verifies that it should trust the headers using the provided HeaderTrustHandler, and attaches the trusted headers to the context.
These headers can be retrieved using httpbp.GetHeader.
This method does not implement the go-kit http.RequestFunc interface, if you want to use this with go-kit, use PopulateRequestContext to return a function that wraps InjectTrustedContext with the HeaderTrustHandler and does implement the http.RequestFunc interface.
func PopulateRequestContext ¶
func PopulateRequestContext(t HeaderTrustHandler) httpgk.RequestFunc
PopulateRequestContext returns a function that calls InjectTrustedContext with the HeaderTrustHandler you pass to it. The function that this produces implements go-kit's http.RequestFunc interface and can be passed to go-kit's http.ServerBefore ServerOption.
Example ¶
This example demonstrates how to use PopulateRequestContext
package main import ( "log" "net/http" "github.com/go-kit/kit/endpoint" httpgk "github.com/go-kit/kit/transport/http" "github.com/fizx/baseplate.go/httpbp" ) func main() { // variables should be properly initialized in production code var ( IsHealthy endpoint.Endpoint DecodeIsHealthyRequest httpgk.DecodeRequestFunc trustHandler httpbp.NeverTrustHeaders ) handler := http.NewServeMux() handler.Handle("/health", httpgk.NewServer( IsHealthy, DecodeIsHealthyRequest, httpbp.EncodeJSONResponse, httpgk.ServerBefore( httpbp.PopulateRequestContext(trustHandler), ), )) log.Fatal(http.ListenAndServe(":8080", handler)) }
Output:
Types ¶
type AlwaysTrustHeaders ¶
type AlwaysTrustHeaders struct{}
AlwaysTrustHeaders implements the HeaderTrustHandler interface and always returns true.
This handler is appropriate when your service only accept calls from within a secure network and you feel comfortable always trusting these headers.
func (AlwaysTrustHeaders) TrustEdgeContext ¶
func (h AlwaysTrustHeaders) TrustEdgeContext(r *http.Request) bool
TrustEdgeContext always returns true. The edge context headers will always be added to the context.
type BaseResponse ¶
type BaseResponse struct {
// contains filtered or unexported fields
}
BaseResponse can be embedded into other response structs to allow them to implement the go-kit Headerer and StatusCoder interfaces as well as the baseplate.go ResponseCookies and ErrorResponse interfaces.
BaseResponse must be initalized using NewBaseResponse before use, if it is not, some methods will panic.
type Response struct { httpbp.BaseResponse } func NewResponse() *Response { return &Response{ BaseResponse: httpbp.NewBaseResponse(), } }
func NewBaseResponse ¶
func NewBaseResponse() BaseResponse
NewBaseResponse returns an initialized BaseResponse.
Intended to be used by the constructor methods for Response structs that embed BaseResponse.
func (*BaseResponse) ClearCookies ¶
func (r *BaseResponse) ClearCookies()
ClearCookies clears all cookies set on the response.
func (BaseResponse) Cookies ¶
func (r BaseResponse) Cookies() []*http.Cookie
Cookies returns the a copy of the current list of cookies to set on the response.
func (BaseResponse) Err ¶
func (r BaseResponse) Err() error
Err returns the error to send back to the client.
func (BaseResponse) Headers ¶
func (r BaseResponse) Headers() http.Header
Headers returns the http.Header collection of headers to set on the response.
func (*BaseResponse) SetCode ¶
func (r *BaseResponse) SetCode(code int)
SetCode sets the status code for this response.
func (*BaseResponse) SetCookie ¶
func (r *BaseResponse) SetCookie(cookie *http.Cookie)
SetCookie adds a cookie to set on the response.
func (*BaseResponse) SetError ¶
func (r *BaseResponse) SetError(e error)
SetError sets the error to return as an error response.
func (BaseResponse) StatusCode ¶
func (r BaseResponse) StatusCode() int
StatusCode returns the current status code set for this response.
type EdgeContextHeaders ¶
type EdgeContextHeaders struct {
EdgeRequest string
}
EdgeContextHeaders implements the Headers interface for HTTP EdgeContext headers.
func NewEdgeContextHeaders ¶
func NewEdgeContextHeaders(h http.Header) EdgeContextHeaders
NewEdgeContextHeaders returns a new EdgeContextHeaders object from the given HTTP headers.
func (EdgeContextHeaders) AsMap ¶
func (s EdgeContextHeaders) AsMap() map[string]string
AsMap returns the EdgeContextHeaders as a map of header keys to header values.
type ErrorResponse ¶
type ErrorResponse interface { // Err returns the HTTPError set on the response. Err() error }
ErrorResponse is an interface that your Response objects can implement in order to have the httpbp.Encode methods automatically return http errors.
Example ¶
This example demonstrates how to use HTTPError along with ErrorResponse to return errors to API clients.
Example request and response:
request: {"error": false} response:
code: 200 content-type: "application/json; charset=utf-8" body: {"message": "hello world!"}
request: {"error": true} response:
code: 502 content-type: "text/plain; charset=utf-8" body: Disruption dowstream
package main import ( "context" "encoding/json" "errors" "log" "net/http" "github.com/fizx/baseplate.go/httpbp" "github.com/go-kit/kit/endpoint" httpgk "github.com/go-kit/kit/transport/http" ) // ExampleResponse is an example response that implements the ErrorResponse // interface. type ExampleResponse struct { httpbp.BaseResponse Message string `json:"message,omitempty"` } // NewExampleResponse returns a pointer to a new, initialized ExampleResponse. func NewExampleResponse() *ExampleResponse { return &ExampleResponse{ BaseResponse: httpbp.NewBaseResponse(), } } var ( // Verify that both ExampleResponse and *ExampleResponse implement the // ResponseCookies interface. _ httpbp.ResponseCookies = ExampleResponse{} _ httpbp.ResponseCookies = (*ExampleResponse)(nil) // Verify that both ExampleResponse and *ExampleResponse implement the // go-kit http.Headerer interface. _ httpgk.Headerer = ExampleResponse{} _ httpgk.Headerer = (*ExampleResponse)(nil) // Verify that both ExampleResponse and *ExampleResponse implement the // go-kit http.StatusCoder interface. _ httpgk.StatusCoder = ExampleResponse{} _ httpgk.StatusCoder = (*ExampleResponse)(nil) // Verify that both ExampleResponse and *ExampleResponse implement the // ErrorResponse interface. _ httpbp.ErrorResponse = ExampleResponse{} _ httpbp.ErrorResponse = (*ExampleResponse)(nil) ) // ExampleRequest is the request struct for our Example endpoint. type ExampleRequest struct { // Error signals to the example endpoint whether it should return an error // or not. Error bool `json:"error"` } // DecodeExampleRequest decodes the request body into an ExampleRequest. func DecodeExampleRequest(_ context.Context, r *http.Request) (interface{}, error) { var req ExampleRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { return req, err } return req, nil } // MakeExampleEndpoint builds a go-kit endpoint.Endpoint function that simply // returns an ExampleResponse with an error set. func MakeExampleEndpoint() endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { resp := NewExampleResponse() req := request.(ExampleRequest) if req.Error { // Return a response that returns a non-nil error when // httpbp.EncodeJSONResponse checks response.Err() which will signal it // to send an error response rather than a normal one. resp.Message = "you'll never see this" resp.SetError( httpbp.HTTPError{ // Code sets the status code to return, defaults to // http.InternalServerError (500). Code: http.StatusBadGateway, // Message sets a custom message for the error response // body, defaults to http.StatusText for the status code. Message: "Disruption downstream", // Cause holds the error that triggered an error response. // This is not communicated to the client but inspected by // your service. Cause: errors.New("database offline"), }, ) } else { // Return a non-error response. resp.Message = "hello world!" } return resp, nil } } // This example demonstrates how to use HTTPError along with ErrorResponse to // return errors to API clients. // // Example request and response: // // request: {"error": false} // response: // // code: 200 // content-type: "application/json; charset=utf-8" // body: {"message": "hello world!"} // // request: {"error": true} // response: // // code: 502 // content-type: "text/plain; charset=utf-8" // body: Disruption dowstream func main() { // Create server handler handler := http.NewServeMux() // Register our example endpoint handler.Handle("/example", httpgk.NewServer( MakeExampleEndpoint(), DecodeExampleRequest, httpbp.EncodeJSONResponse, )) // Start the server log.Fatal(http.ListenAndServe(":8080", handler)) }
Output:
type HTTPError ¶
type HTTPError struct { // Code is the status code to set on the HTTP response. Defaults to 500 if // it is not set. Code int // Message is an optional message that can be returned to the // client. Defaults to the native http.StatusText message for the // StatusCode() of the HTTPError. Message string // Cause is an optional error that can be used to retain the error that // led to us returning an HTTP error to the client. Cause error }
HTTPError is a specialized error that is returned be the Err method specified in the ErrorResponse interface.
func (HTTPError) As ¶
As implements helper interface for errors.As.
If v is pointer to either HTTPError or *HTTPError, *v will be set into this error.
func (HTTPError) Error ¶
Error returns the standard error string, this is not returned to the client.
func (HTTPError) ResponseMessage ¶
ResponseMessage returns the error message to send to the client.
func (HTTPError) StatusCode ¶
StatusCode returns the HTTP status code to set on the response. Defaults to 500 if Code is not set on the HTTPError.
type HeaderContextKey ¶
type HeaderContextKey int
HeaderContextKey is a key used to get HTTP headers from a context object.
const ( // EdgeContextContextKey is the key for the raw edge request context EdgeContextContextKey HeaderContextKey = iota // TraceIDContextKey is the header for the trace ID passed by the caller TraceIDContextKey // ParentIDContextKey is the header for the parent ID passed by the caller ParentIDContextKey // SpanIDContextKey is the header for the span ID passed by the caller SpanIDContextKey // SpanFlagsContextKey is the header for the span flags passed by the caller SpanFlagsContextKey // SpanSampledContextKey is the header for the sampled flag passed by the caller SpanSampledContextKey )
type HeaderTrustHandler ¶
type HeaderTrustHandler interface { // TrustEdgeContext informs the function returned by PopulateBaseplateRequestContext // if it can trust the HTTP headers that can be used to create an edge // context. // // If it can trust those headers, then the headers will be copied into the // context object to be later used to initialize the edge context for the // request. TrustEdgeContext(r *http.Request) bool // TrustSpan informs the function returned by PopulateBaseplateRequestContext // if it can trust the HTTP headers that can be used to create a server // span. // // If it can trust those headers, then the headers will be copied into the // context object to later be used to initialize the server span for the // request. TrustSpan(r *http.Request) bool }
HeaderTrustHandler provides an interface PopulateBaseplateRequestContext to verify that it should trust the HTTP headers it receives.
type Headers ¶
type Headers interface { // AsMap returns the Headers struct as a map of header keys to header // values. AsMap() map[string]string }
Headers is an interface to collect all of the HTTP headers for a particular baseplate resource (spans and edge contexts) into a struct that provides an easy way to convert them into HTTP headers.
This interface exists so we can avoid having to do runtime checks on maps to ensure that they have the right keys set when we are trying to sign or verify a set of HTTP headers.
type NeverTrustHeaders ¶
type NeverTrustHeaders struct{}
NeverTrustHeaders implements the HeaderTrustHandler interface and always returns false.
This handler is appropriate when your service is exposed to the public internet and also do not expect to receive these headers anyways, or simply does not care to parse these headers.
func (NeverTrustHeaders) TrustEdgeContext ¶
func (h NeverTrustHeaders) TrustEdgeContext(r *http.Request) bool
TrustEdgeContext always returns false. The edge context headers will never be added to the context.
type ResponseCookies ¶
type ResponseCookies interface { // Return a list of all cookies to set on the response. Cookies() []*http.Cookie }
ResponseCookies is an interface that your Response objects can implement in order to have the httpbp.Encode methods automatically add cookies to the response.
type SpanHeaders ¶
type SpanHeaders struct { TraceID string ParentID string SpanID string Flags string Sampled string }
SpanHeaders implements the Headers interface for HTTP Span headers.
func NewSpanHeaders ¶
func NewSpanHeaders(h http.Header) SpanHeaders
NewSpanHeaders returns a new SpanHeaders object from the given HTTP headers.
func (SpanHeaders) AsMap ¶
func (s SpanHeaders) AsMap() map[string]string
AsMap returns the SpanHeaders as a map of header keys to header values.
type TrustHeaderSignature ¶
type TrustHeaderSignature struct {
// contains filtered or unexported fields
}
TrustHeaderSignature implements the HeaderTrustHandler interface and checks the headers for a valid signature header. If the headers are signed, then they can be trusted and the Trust request returns true. If there is no signature or the signature is invalid, then the Trust request returns false.
For both the span and edge context headers, the trust handler expects the caller to provide the signature of a message in the following format:
"{header0}:{value0}|{header1}|{value1}|...|{headerN}:{valueN}"
where the headers are sorted lexicographically. Additionally, the signature should be generated using the baseplate provided `signing.Sign` function.
TrustHeaderSignature provides implementations for both signing and verifying edge context and span headers.
This handler is appropriate when your service wants to be able to trust headers that come from trusted sources, but also receives calls from un-trusted sources that you would not want to accept these headers from. One example would be an HTTP API that is exposed to clients over the public internet where you would not trust these headers but is also used internally where you want to accept these headers.
func NewTrustHeaderSignature ¶
func NewTrustHeaderSignature(args TrustHeaderSignatureArgs) TrustHeaderSignature
NewTrustHeaderSignature returns a new HMACTrustHandler that uses the provided TrustHeaderSignatureArgs
func (TrustHeaderSignature) SignEdgeContextHeader ¶
func (h TrustHeaderSignature) SignEdgeContextHeader(headers EdgeContextHeaders, expiresIn time.Duration) (string, error)
SignEdgeContextHeader signs the edge context header using signing.Sign.
The message that is signed has the following format:
"X-Edge-Request:{headerValue}
func (TrustHeaderSignature) SignSpanHeaders ¶
func (h TrustHeaderSignature) SignSpanHeaders(headers SpanHeaders, expiresIn time.Duration) (string, error)
SignSpanHeaders signs the given span headers using signing.Sign.
The message that is signed has the following format:
"{header0}:{value0}|{header1}|{value1}|...|{headerN}:{valueN}"
where the headers are sorted lexicographically.
func (TrustHeaderSignature) TrustEdgeContext ¶
func (h TrustHeaderSignature) TrustEdgeContext(r *http.Request) bool
TrustEdgeContext returns true if the request has the header "X-Edge-Request-Signature" set and is a valid signature of the header:
"X-Edge-Request"
The message that should be signed is:
"X-Edge-Request:{headerValue}"
func (TrustHeaderSignature) TrustSpan ¶
func (h TrustHeaderSignature) TrustSpan(r *http.Request) bool
TrustSpan returns true if the request has the header "X-Span-Signature" set and is a valid signature of the headers:
"X-Flags" "X-Parent" "X-Sampled" "X-Span" "X-Trace"
The message that should be signed is:
"{header0}:{value0}|{header1}|{value1}|...|{headerN}:{valueN}"
where the headers are sorted lexicographically.
func (TrustHeaderSignature) VerifyEdgeContextHeader ¶
func (h TrustHeaderSignature) VerifyEdgeContextHeader(headers EdgeContextHeaders, signature string) (bool, error)
VerifyEdgeContextHeader verifies the edge context header using signing.Verify.
func (TrustHeaderSignature) VerifySpanHeaders ¶
func (h TrustHeaderSignature) VerifySpanHeaders(headers SpanHeaders, signature string) (bool, error)
VerifySpanHeaders verifies the edge context header using signing.Verify.