Documentation ¶
Overview ¶
Package httpbp provides Baseplate specific helpers and integrations for http services.
Index ¶
- Constants
- Variables
- func ClientErrorFromResponse(resp *http.Response) error
- func DrainAndClose(r io.ReadCloser) error
- func GetHealthCheckProbe(query url.Values) (int64, error)
- func InitializeEdgeContextFromTrustedRequest(ctx context.Context, r *http.Request, args InjectEdgeRequestContextArgs) context.Context
- func NewBaseplateServer(args ServerArgs) (baseplate.Server, error)
- func NewClient(config ClientConfig, middleware ...ClientMiddleware) (*http.Client, error)
- func NewHandler(name string, handle HandlerFunc, middlewares ...Middleware) http.Handler
- func NewTestBaseplateServer(args ServerArgs) (baseplate.Server, *httptest.Server, error)
- func PerformanceMonitoringMiddleware() (timeToWriteHeader, timeToFirstByte *prometheus.HistogramVec)
- func RegisterCustomDefaultErrorTemplate(t *template.Template, text string) (*template.Template, error)
- func RegisterDefaultErrorTemplate(t *template.Template) (*template.Template, error)
- func ServeAdmin(healthCheck HandlerFunc)
- func SetEdgeContextHeader(header string, w http.ResponseWriter)
- func StartSpanFromTrustedRequest(ctx context.Context, name string, truster HeaderTrustHandler, r *http.Request) (context.Context, *tracing.Span)
- func WrapTransport(transport http.RoundTripper, middleware ...ClientMiddleware) http.RoundTripper
- func WriteHTML(w http.ResponseWriter, resp Response, templates *template.Template) error
- func WriteJSON(w http.ResponseWriter, resp Response) error
- func WriteRawContent(w http.ResponseWriter, resp Response, contentType string) error
- func WriteResponse(w http.ResponseWriter, cw ContentWriter, resp Response) error
- type AlwaysTrustHeaders
- type BaseHTMLBody
- type ClientConfig
- type ClientError
- type ClientMiddleware
- func CircuitBreaker(config breakerbp.Config) ClientMiddleware
- func ClientErrorWrapper(maxErrorReadAhead int) ClientMiddleware
- func MaxConcurrency(maxConcurrency int64) ClientMiddleware
- func MonitorClient(slug string) ClientMiddleware
- func PrometheusClientMetrics(serverSlug string) ClientMiddleware
- func Retries(maxErrorReadAhead int, retryOptions ...retry.Option) ClientMiddleware
- type ContentWriter
- type DefaultMiddlewareArgs
- type EdgeContextHeaders
- type Endpoint
- type EndpointRegistry
- type ErrorResponse
- func BadGateway() *ErrorResponse
- func BadRequest() *ErrorResponse
- func Conflict() *ErrorResponse
- func ErrorForCode(code int) *ErrorResponse
- func Forbidden() *ErrorResponse
- func GatewayTimeout() *ErrorResponse
- func Gone() *ErrorResponse
- func InternalServerError() *ErrorResponse
- func LegalBlock() *ErrorResponse
- func MethodNotAllowed() *ErrorResponse
- func NewErrorResponse(code int, reason string, explanation string) *ErrorResponse
- func NotFound() *ErrorResponse
- func NotImplemented() *ErrorResponse
- func PayloadTooLarge() *ErrorResponse
- func PaymentRequired() *ErrorResponse
- func ServiceUnavailable() *ErrorResponse
- func Teapot() *ErrorResponse
- func TooEarly() *ErrorResponse
- func TooManyRequests() *ErrorResponse
- func Unauthorized() *ErrorResponse
- func UnprocessableEntity() *ErrorResponse
- func UnsupportedMediaType() *ErrorResponse
- func (r *ErrorResponse) Retryable(w http.ResponseWriter, retryAfter time.Duration) *ErrorResponse
- func (r ErrorResponse) String() string
- func (r ErrorResponse) TemplateName() string
- func (r *ErrorResponse) WithDetails(details map[string]string) *ErrorResponse
- func (r *ErrorResponse) WithRawResponse(raw string) *ErrorResponse
- func (r *ErrorResponse) WithTemplateName(name string) *ErrorResponse
- type ErrorResponseJSONWrapper
- type HTMLBody
- type HTTPError
- type HandlerFunc
- type HeaderTrustHandler
- type Headers
- type InjectEdgeRequestContextArgs
- type Middleware
- func DefaultMiddleware(args DefaultMiddlewareArgs) []Middleware
- func InjectEdgeRequestContext(args InjectEdgeRequestContextArgs) Middleware
- func InjectServerSpan(truster HeaderTrustHandler) Middleware
- func PrometheusServerMetrics(_ string) Middleware
- func RecordStatusCode() Middlewaredeprecated
- func SupportedMethods(method string, additional ...string) Middleware
- type NeverTrustHeaders
- type Pattern
- type Response
- type ServerArgs
- 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 ( // RetryAfterHeader is the standard "Retry-After" header key defined in RFC2616. // // https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html RetryAfterHeader = "Retry-After" // ErrorPageTemplate is the template for the default HTML error response. ErrorPageTemplate = `` /* 485-byte string literal not displayed */ // DefaultErrorTemplateName is the name for the shared, default HTML template // for error responses. DefaultErrorTemplateName = "httpbp/error" )
const ( // EdgeContextHeader is the key use to get the raw edge context from // the HTTP request headers. EdgeContextHeader = "X-Edge-Request" // EdgeContextSignatureHeader is the key use to get the signature for // the edge context headers from the HTTP request headers. EdgeContextSignatureHeader = "X-Edge-Request-Signature" // 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" // SpanSignatureHeader is the key use to get the signature for // the span headers from the HTTP request headers. SpanSignatureHeader = "X-Span-Signature" // 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" // PlainTextContentType is the Content-Type header for plain text responses. PlainTextContentType = "text/plain; charset=utf-8" )
const AllowHeader = "Allow"
AllowHeader is the "Allow" header. This should be set when returning a 405 - Method Not Allowed error.
const ( // DefaultMaxErrorReadAhead defines the maximum bytes to be read from a // failed HTTP response to be attached as additional information in a // ClientError response. DefaultMaxErrorReadAhead = 1024 )
const HealthCheckProbeQuery = "type"
HealthCheckProbeQuery is the name of HTTP query defined in Baseplate spec.
Variables ¶
var ( ErrConfigMissingSlug = errors.New("slug cannot be empty") ErrConfigInvalidMaxErrorReadAhead = errors.New("maxErrorReadAhead value needs to be positive") ErrConfigInvalidMaxConnections = errors.New("maxConnections value needs to be positive") )
ClientConfig errors are returned if the configuration validation fails.
var ( // ErrConcurrencyLimit is returned by the max concurrency middleware if // there are too many requests in-flight. ErrConcurrencyLimit = errors.New("hit concurrency limit") )
Well-known errors for middleware layer.
Functions ¶
func ClientErrorFromResponse ¶ added in v0.8.0
ClientErrorFromResponse creates ClientError from http response.
It returns nil error when the response code are in range of [100, 400), or non-nil error otherwise (including response being nil). When the returned error is non-nil, it's guaranteed to be of type *ClientError.
It does not read from resp.Body in any case, even if the returned error is non-nil. So it's always the caller's responsibility to read/close the body to ensure the HTTP connection can be reused with keep-alive.
The caller can (optionally) choose to read the body and feed that back into ClientError.AdditionalInfo (it's always empty string when returned). See the example for more details regarding this.
func DrainAndClose ¶ added in v0.8.0
func DrainAndClose(r io.ReadCloser) error
DrainAndClose reads r fully then closes it.
It's required for http response bodies by stdlib http clients to reuse keep-alive connections, so you should always defer it after checking error.
Example ¶
This example demonstrates how DrainAndClose should be used with http requests.
package main import ( "net/http" "github.com/reddit/baseplate.go/httpbp" "github.com/reddit/baseplate.go/log" ) func main() { resp, err := http.Get("https://www.reddit.com") if err != nil { log.Errorw("http request failed", "err", err) return } defer httpbp.DrainAndClose(resp.Body) // now read/handle body normally }
Output:
func GetHealthCheckProbe ¶ added in v0.4.2
GetHealthCheckProbe parses the health check probe from the request.
Unrecognized string probes will fallback to READINESS. When that happens a non-nil error will also be returned. If a probe is not specified in the request, this function will return READINESS with nil error.
This function only supports the probes known to this version of Baseplate.go for the string version of probes passed into the request. If in the future a new probe is added to baseplate.thrift, you would need to update Baseplate.go library, otherwise it would fallback to READINESS. Currently the supported probes are:
- READINESS
- LIVENESS
- STARTUP
If the probe specified in the request is the int value, this function just blindly return the parsed int value, even if it's not one of the defined enum values known to this version of Baseplate.go.
Note that because of go's type system, to make this function more useful the returned probe value is casted back to int64 from the thrift defined enum type.
func InitializeEdgeContextFromTrustedRequest ¶
func InitializeEdgeContextFromTrustedRequest( ctx context.Context, r *http.Request, args InjectEdgeRequestContextArgs, ) context.Context
InitializeEdgeContextFromTrustedRequest initializen an EdgeRequestContext on the context object if the provided HeaderTrustHandler confirms that the headers can be trusted and the header is set on the request. If the header cannot be trusted and/or the header is not set, then no EdgeRequestContext is set on the context object.
InitializeEdgeContextFromTrustedRequest is used by InjectEdgeRequestContext and should not generally be used directly but is provided for testing purposes or use cases that are not covered by Baseplate.
func NewBaseplateServer ¶
func NewBaseplateServer(args ServerArgs) (baseplate.Server, error)
NewBaseplateServer returns a new HTTP implementation of a Baseplate server with the given ServerArgs.
The Endpoints given in the ServerArgs will be wrapped using the default Baseplate Middleware as well as any additional Middleware passed in. In addition, panics will be automatically recovered from, reported, and passed up the middleware chain as an HTTPError with the status code 500.
Example ¶
This example demonstrates what a typical main function should look like for a Baseplate HTTP service.
package main import ( "context" "errors" "net/http" "time" "github.com/reddit/baseplate.go" "github.com/reddit/baseplate.go/ecinterface" "github.com/reddit/baseplate.go/httpbp" "github.com/reddit/baseplate.go/log" "github.com/reddit/baseplate.go/redis/db/redisbp" "github.com/reddit/baseplate.go/secrets" ) type config struct { baseplate.Config `yaml:",inline"` Redis redisbp.ClusterConfig `yaml:"redis"` } type body struct { X int `json:"x"` Y int `json:"y"` } type isHealthyResponse struct { Status string `json:"status,omitempty"` } type TestService struct { secrets *secrets.Store redisAddrs []string } func (s *TestService) IsHealthy(ctx context.Context, w http.ResponseWriter, r *http.Request) error { return httpbp.WriteJSON(w, httpbp.NewResponse(isHealthyResponse{Status: "healthy"})) } func (s *TestService) Home(ctx context.Context, w http.ResponseWriter, r *http.Request) error { return httpbp.WriteJSON(w, httpbp.NewResponse(body{X: 1, Y: 2})) } func (s *TestService) ServerErr(ctx context.Context, w http.ResponseWriter, r *http.Request) error { return httpbp.JSONError(httpbp.InternalServerError(), errors.New("example")) } func (s *TestService) Ratelimit(ctx context.Context, w http.ResponseWriter, r *http.Request) error { return httpbp.JSONError( httpbp.TooManyRequests().Retryable(w, time.Minute), errors.New("rate-limit"), ) } func (s *TestService) InvalidInput(ctx context.Context, w http.ResponseWriter, r *http.Request) error { return httpbp.JSONError( httpbp.BadRequest().WithDetails(map[string]string{ "foo": "must be >= 0", "bar": "must be non-nil", }), errors.New("invalid-input"), ) } func (s *TestService) Endpoints() map[httpbp.Pattern]httpbp.Endpoint { return map[httpbp.Pattern]httpbp.Endpoint{ "/": { Name: "home", Handle: s.Home, Methods: []string{http.MethodGet}, }, "/err": { Name: "err", Handle: s.ServerErr, Methods: []string{http.MethodGet, http.MethodPost}, }, "/ratelimit": { Name: "ratelimit", Handle: s.Ratelimit, Methods: []string{http.MethodGet}, }, "/invalid-input": { Name: "invalid-input", Handle: s.InvalidInput, Methods: []string{http.MethodPost}, }, } } func loggingMiddleware(name string, next httpbp.HandlerFunc) httpbp.HandlerFunc { return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { log.Infof("Request %q: %#v", name, r) return next(ctx, w, r) } } var ( _ httpbp.Middleware = loggingMiddleware ) // This example demonstrates what a typical main function should look like for a // Baseplate HTTP service. func main() { // In real code this MUST be replaced by the factory from the actual implementation. var ecFactory ecinterface.Factory var cfg config if err := baseplate.ParseConfigYAML(&cfg); err != nil { panic(err) } ctx, bp, err := baseplate.New(context.Background(), baseplate.NewArgs{ Config: cfg, EdgeContextFactory: ecFactory, }) if err != nil { panic(err) } defer bp.Close() svc := TestService{ secrets: bp.Secrets(), redisAddrs: cfg.Redis.Addrs, } server, err := httpbp.NewBaseplateServer(httpbp.ServerArgs{ Baseplate: bp, Endpoints: svc.Endpoints(), Middlewares: []httpbp.Middleware{loggingMiddleware}, }) if err != nil { panic(err) } go httpbp.ServeAdmin(svc.IsHealthy) log.Info(baseplate.Serve(ctx, baseplate.ServeArgs{Server: server})) }
Output:
func NewClient ¶ added in v0.8.3
func NewClient(config ClientConfig, middleware ...ClientMiddleware) (*http.Client, error)
NewClient returns a standard HTTP client wrapped with the default middleware plus any additional client middleware passed into this function. Default middlewares are:
* MonitorClient with transport.WithRetrySlugSuffix
* PrometheusClientMetrics with transport.WithRetrySlugSuffix
* Retries
* MonitorClient
* PrometheusClientMetrics
ClientErrorWrapper is included as transitive middleware through Retries.
func NewHandler ¶
func NewHandler(name string, handle HandlerFunc, middlewares ...Middleware) http.Handler
NewHandler returns a new http.Handler with the given HandlerFunc wrapped with the given Middleware. The given "name" will be passed to all of the middlewares.
Most services should not use NewHander and should use NewBaseplateServer to create an entire Server with all of its handlers instead. NewHander is provided for those who need to avoid the default Baseplate middleware or for testing purposes.
func NewTestBaseplateServer ¶
func NewTestBaseplateServer(args ServerArgs) (baseplate.Server, *httptest.Server, error)
NewTestBaseplateServer returns a new HTTP implementation of a Baseplate server with the given ServerArgs that uses a Server from httptest rather than a real server.
The underlying httptest.Server is started when the the test BaseplateServer is created and does not need to be started manually. It is closed by calling Close, Close should not be called more than once. Serve does not need to be called but will wait until Close is called to exit if it is called.
func PerformanceMonitoringMiddleware ¶ added in v0.9.2
func PerformanceMonitoringMiddleware() (timeToWriteHeader, timeToFirstByte *prometheus.HistogramVec)
PerformanceMonitoringMiddleware returns optional Prometheus historgram metrics for monitoring the following:
- http server time to write header in seconds
- http server time to write header in seconds
func RegisterCustomDefaultErrorTemplate ¶
func RegisterCustomDefaultErrorTemplate(t *template.Template, text string) (*template.Template, error)
RegisterCustomDefaultErrorTemplate adds the custom template passed in as the default HTML template for error pages, rather than the default template provided by baseplate.
If you are creating a custom template, note that the ErrorResponse struct is what is passed to the template to render so you are limited to the values exported by ErrorResponse.
func RegisterDefaultErrorTemplate ¶
RegisterDefaultErrorTemplate adds the default HTML template for error pages to the given templates and returns the result.
This only registeres the single, shared, default template, if you want to use custom HTML templates for specific errors, you will need to customize the template name on the error by calling `SetTemplateName`
func ServeAdmin ¶ added in v0.9.5
func ServeAdmin(healthCheck HandlerFunc)
ServeAdmin starts a blocking HTTP server for internal functions:
health - serve /health for health checking metrics - serve /metrics for prometheus profiling - serve /debug/pprof for profiling, ref: https://pkg.go.dev/net/http/pprof
Default server address is admin.Addr.
This function blocks, so it should be run as its own goroutine.
func SetEdgeContextHeader ¶
func SetEdgeContextHeader(header string, w http.ResponseWriter)
SetEdgeContextHeader attach EdgeRequestContext into response header.
func StartSpanFromTrustedRequest ¶
func StartSpanFromTrustedRequest( ctx context.Context, name string, truster HeaderTrustHandler, r *http.Request, ) (context.Context, *tracing.Span)
StartSpanFromTrustedRequest starts a server span using the Span headers from the given request if the provided HeaderTrustHandler confirms that they can be trusted and the Span headers are provided, otherwise it starts a new server span.
StartSpanFromTrustedRequest is used by InjectServerSpan and should not generally be used directly but is provided for testing purposes or use cases that are not covered by Baseplate.
func WrapTransport ¶ added in v0.8.3
func WrapTransport(transport http.RoundTripper, middleware ...ClientMiddleware) http.RoundTripper
WrapTransport takes a list of client middleware and wraps them around the given transport. This is useful for using client middleware outside of this package.
func WriteHTML ¶
WriteHTML calls WriteResponse with an HTML ContentWriter using the given templates.
func WriteJSON ¶
func WriteJSON(w http.ResponseWriter, resp Response) error
WriteJSON calls WriteResponse with a JSON ContentWriter.
func WriteRawContent ¶
func WriteRawContent(w http.ResponseWriter, resp Response, contentType string) error
WriteRawContent calls WriteResponse with a Raw ContentWriter with the given Content-Type.
func WriteResponse ¶
func WriteResponse(w http.ResponseWriter, cw ContentWriter, resp Response) error
WriteResponse writes the given Response to the given ResponseWriter using the given ContentWriter. It also sets the Content-Type header on the response to the one defined by the ContentWriter and sets the status code of the response if set on the Response object.
WriteResponse generally does not need to be called directly, instead you can use one of the helper methods to call it with a pre-defined ContentWriter.
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 BaseHTMLBody ¶
type BaseHTMLBody struct {
Name string
}
BaseHTMLBody can be embedded in another struct to allow that struct to fufill the HTMLBody interface.
func (BaseHTMLBody) TemplateName ¶
func (b BaseHTMLBody) TemplateName() string
TemplateName returns the name of the template to use to render the HTML response.
type ClientConfig ¶ added in v0.8.3
type ClientConfig struct { Slug string `yaml:"slug"` MaxErrorReadAhead int `yaml:"limitErrorReading"` MaxConnections int `yaml:"maxConnections"` CircuitBreaker *breakerbp.Config `yaml:"circuitBreaker"` RetryOptions []retry.Option }
ClientConfig provides the configuration for a HTTP client including its middlewares.
func (ClientConfig) Validate ¶ added in v0.8.3
func (c ClientConfig) Validate() error
Validate checks ClientConfig for any missing or erroneous values.
type ClientError ¶ added in v0.8.0
type ClientError struct { Status string StatusCode int RetryAfter time.Duration AdditionalInfo string }
ClientError defines the client side error constructed from an HTTP response.
Please see ClientErrorFromResponse for more details.
Example ¶
This example demonstrates how you can read the response body and add that back to ClientError's AdditionalInfo field.
package main import ( "errors" "io" "net/http" "github.com/reddit/baseplate.go/httpbp" "github.com/reddit/baseplate.go/log" ) func main() { resp, err := http.Get("https://www.reddit.com") if err != nil { log.Errorw("http request failed", "err", err) return } defer httpbp.DrainAndClose(resp.Body) err = httpbp.ClientErrorFromResponse(resp) if err != nil { var ce *httpbp.ClientError if errors.As(err, &ce) { if body, e := io.ReadAll(resp.Body); e == nil { ce.AdditionalInfo = string(body) } } log.Errorw("http request failed", "err", err) return } // now read/handle body normally }
Output:
func (ClientError) Error ¶ added in v0.8.0
func (ce ClientError) Error() string
func (ClientError) RetryAfterDuration ¶ added in v0.8.0
func (ce ClientError) RetryAfterDuration() time.Duration
RetryAfterDuration implements retrybp.RetryAfterError.
func (ClientError) Retryable ¶ added in v0.8.0
func (ce ClientError) Retryable() int
Retryable implements retrybp.RetryableError.
It returns true (1) on any of the following conditions and no decision (0) otherwise:
- There was a valid Retry-After header in the response
- The status code was one of:
- 425 (too early)
- 429 (too many requests)
- 503 (service unavailable)
type ClientMiddleware ¶ added in v0.8.3
type ClientMiddleware func(next http.RoundTripper) http.RoundTripper
ClientMiddleware is used to build HTTP client middleware by implementing http.RoundTripper which http.Client accepts as Transport.
func CircuitBreaker ¶ added in v0.8.3
func CircuitBreaker(config breakerbp.Config) ClientMiddleware
CircuitBreaker is a middleware that prevents sending requests that are likely to fail through a configurable failure ratio based on total failures and requests. The circuit breaker is applied on a per-host basis, e.g. failed requests are counting per host.
func ClientErrorWrapper ¶ added in v0.8.3
func ClientErrorWrapper(maxErrorReadAhead int) ClientMiddleware
ClientErrorWrapper applies ClientErrorFromResponse to the returned response ensuring an HTTP status response outside the range [200, 400) is wrapped in an error relieving users from the need to check the status response.
If a response is wrapped in an error this middleware will perform DrainAndClose on the response body and will read up to limit to store ClientError.AdditionalInfo about the HTTP response.
In the event of an error the response payload is read up to number of maxErrorReadAhead bytes. If the parameter is set to a value <= 0 it will be set to DefaultMaxErrorReadAhead.
func MaxConcurrency ¶ added in v0.8.3
func MaxConcurrency(maxConcurrency int64) ClientMiddleware
MaxConcurrency is a middleware to limit the number of concurrent in-flight requests at any given time by returning an error if the maximum is reached.
func MonitorClient ¶ added in v0.8.3
func MonitorClient(slug string) ClientMiddleware
MonitorClient is an HTTP client middleware that wraps HTTP requests in a client span.
func PrometheusClientMetrics ¶ added in v0.9.2
func PrometheusClientMetrics(serverSlug string) ClientMiddleware
PrometheusClientMetrics returns a middleware that tracks Prometheus metrics for client http.
It emits the following prometheus metrics:
* http_client_active_requests gauge with labels:
- http_method: method of the HTTP request
- http_client_name: the remote service being contacted, the serverSlug arg
* http_client_latency_seconds histogram with labels above plus:
- http_success: true if the status code is 2xx or 3xx, false otherwise
* http_client_requests_total counter with all labels above plus:
- http_response_code: numeric status code as a string, e.g. 200
func Retries ¶ added in v0.8.3
func Retries(maxErrorReadAhead int, retryOptions ...retry.Option) ClientMiddleware
Retries provides a retry middleware by ensuring certain HTTP responses are wrapped in errors. Retries wraps the ClientErrorWrapper middleware, e.g. if you are using Retries there is no need to also use ClientErrorWrapper.
type ContentWriter ¶
type ContentWriter interface { // ContentType returns the value to set on the "Content-Type" header of the // response. ContentType() string // WriteBody takes the given response body and writes it to the given // writer. WriteBody(w io.Writer, v interface{}) error }
ContentWriter is responsible writing the response body and communicating the "Content-Type" of the response body.
To use a ContentWriter, pass it to httpbp.WriteResponse rather than using it directly.
func HTMLContentWriter ¶
func HTMLContentWriter(templates *template.Template) ContentWriter
HTMLContentWriter returns a ContentWriter for writing HTML using the given templates.
When using an HTML ContentWriter, your Response.Body should be an object that implements the HTMLBody interface and can be given as input to t.Execute. If it does not, an error will be returned. An error will also be returned if there is no template available with the TemplateName() returned by Response.Body.
func JSONContentWriter ¶
func JSONContentWriter() ContentWriter
JSONContentWriter returns a ContentWriter for writing JSON.
When using a JSON ContentWriter, your Response.Body should be a value that can be marshalled into JSON. This can either be a struct that defines JSON reflection tags or a `map` of values that can be Marshalled to JSON.
func RawContentWriter ¶
func RawContentWriter(contentType string) ContentWriter
RawContentWriter returns a ContentWriter for writing raw content with the given Content-Type.
When using a raw content writer, your your Response.Body should be an object that implements one of the io.Reader or fmt.Stringer interfaces, a string, or a byte slice. If it is not one of these, an error will be returned.
type DefaultMiddlewareArgs ¶
type DefaultMiddlewareArgs struct { // The HeaderTrustHandler to use. // If empty, NeverTrustHeaders will be used instead. TrustHandler HeaderTrustHandler // The logger to be called when edgecontext parsing failed. Logger log.Wrapper // The edgecontext implementation to use. Optional. // If not set, the global one from ecinterface.Get will be used instead. EdgeContextImpl ecinterface.Interface }
DefaultMiddlewareArgs provides the arguments for the default, Baseplate Middlewares
type EdgeContextHeaders ¶
type EdgeContextHeaders struct {
EdgeRequest string
}
EdgeContextHeaders implements the Headers interface for HTTP EdgeContext headers.
func NewEdgeContextHeaders ¶
func NewEdgeContextHeaders(h http.Header) (EdgeContextHeaders, error)
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 Endpoint ¶
type Endpoint struct { // Name is required, it is the "name" of the endpoint that will be passed // to any Middleware wrapping the HandlerFunc. Name string // Methods is the list of HTTP methods that the endpoint supports. Methods // must have at least one entry and all entries must be valid HTTP methods. // // Method names should be in all upper case. // Use the http.Method* constants from "net/http" for the values in this slice // to ensure that you are using methods that are supported and in the format // we expect. // If you add http.MethodGet, http.MethodHead will be supported automatically. Methods []string // Handle is required, it is the base HandlerFunc that will be wrapped // by any Middleware. Handle HandlerFunc // Middlewares is an optional list of additional Middleware to wrap the // given HandlerFunc. Middlewares []Middleware }
Endpoint holds the values needed to create a new HandlerFunc.
type EndpointRegistry ¶
EndpointRegistry is the minimal interface needed by a Baseplate HTTP server for the underlying HTTP server.
*http.ServeMux implements this interface and is the default EndpointRegistry used by NewBaseplateServer.
type ErrorResponse ¶
type ErrorResponse struct { // A standard, machine readable string representing the error. Should be // UPPER_SNAKE_CASE. // // Ex: "INTERNAL_SERVER_ERROR" Reason string `json:"reason"` // A human readable explanation for the error. // // Ex: "The server has either erred or is incapable of performing the request." Explanation string `json:"explanation"` // Optional map of invalid fields to error messages, for use with errors where // the client sent invalid input (400). // // This allows servers to return validation information for multiple // fields at once. // // Ex: // { // "foo.name": "This field is required.", // "bar.id": "This field is required.", // } // // Details can be set manually or using ErrorResponse.WithDetails. // Details are returned to the caller and should be something you are // comfortable presenting to an end-user. Details map[string]string `json:"details,omitempty"` // contains filtered or unexported fields }
ErrorResponse is the base struct used by all the of standard errors in httpbp.
You should not generally need to create ErrorResponses manually as standard ones have been provided for each of the common 4xx and 5xx errors but it is made available for custom scenarios.
func BadGateway ¶
func BadGateway() *ErrorResponse
BadGateway is for 502 responses.
This is appropriate to use when your service is responsible for making requests to other services and one returns a bad (unexpected error or malformed) response.
func BadRequest ¶
func BadRequest() *ErrorResponse
BadRequest is for 400 responses.
This is appropriate for when the client sends a malformed or invalid request.
If the client sent an invalid request, you can send the details using the Details map in the ErrorResponse.
func Conflict ¶
func Conflict() *ErrorResponse
Conflict is for 409 responses.
This is appropriate for when a client request would cause a conflict with the current state of the server.
func ErrorForCode ¶
func ErrorForCode(code int) *ErrorResponse
ErrorForCode returns a new *ErrorResponse for the given HTTP status code if one is configured and falls back to returning InternalServerError() if the given code is not configured.
This is intended to be used in cases where you have multiple potential error codes and want to return the appropriate error response. If you are only returning a particular error, your code will likely be cleaner using the specific functions provided rather than using ErrorForCode.
Supported codes are as follows:
// 4xx http.StatusBadRequest: httpbp.BadRequest http.StatusUnauthorized: httpbp.Unauthorized http.StatusPaymentRequired: httpbp.PaymentRequired http.StatusForbidden: httpbp.Forbidden http.StatusNotFound: httpbp.NotFound http.StatusMethodNotAllowed: httpbp.MethodNotAllowed http.StatusConflict: httpbp.Conflict http.StatusGone: httpbp.Gone http.StatusRequestEntityTooLarge: httpbp.PayloadTooLarge http.StatusUnsupportedMediaType: httpbp.UnsupportedMediaType http.StatusTeapot: httpbp.Teapot http.StatusUnprocessableEntity: httpbp.UnprocessableEntity http.StatusTooEarly: httpbp.TooEarly http.StatusTooManyRequests: httpbp.TooManyRequests http.StatusUnavailableForLegalReasons: httpbp.LegalBlock // 5xx http.StatusInternalServerError: httpbp.InternalServerError http.StatusNotImplemented: httpbp.NotImplemented http.StatusBadGateway: httpbp.BadGateway http.StatusServiceUnavailable: httpbp.ServiceUnavailable http.StatusGatewayTimeout: httpbp.GatewayTimeout
func Forbidden ¶
func Forbidden() *ErrorResponse
Forbidden is for 403 responses.
This is appropriate for when you can authenticate a request but the client does not have access to the requested resource.
Unlike Unauthorized, refreshing an authentication resource and trying again will not make a difference.
func GatewayTimeout ¶
func GatewayTimeout() *ErrorResponse
GatewayTimeout is for 504 responses.
This is appropriate to use when your service is responsible for making requests to other services and one times out.
func Gone ¶
func Gone() *ErrorResponse
Gone is for 410 responses.
This is appropriate for when the resource requested was once available but is no longer.
func InternalServerError ¶
func InternalServerError() *ErrorResponse
InternalServerError is for 500 responses.
This is appropriate for generic, unhandled server errors.
func LegalBlock ¶
func LegalBlock() *ErrorResponse
LegalBlock is for 451 responses.
This is appropriate for when the requested resource is unavailable for legal reasons, such as when the content is censored in a country.
func MethodNotAllowed ¶ added in v0.4.0
func MethodNotAllowed() *ErrorResponse
MethodNotAllowed is for 405 responses.
This is appropriate for when the client tries to access a resource that does exist using an HTTP method that it does not support.
func NewErrorResponse ¶
func NewErrorResponse(code int, reason string, explanation string) *ErrorResponse
NewErrorResponse returns a new ErrorResponse with the given inputs.
This can be used to create custom NewErrorResponses, but it is encouraged that developers use the standard ones provided by httpbp rather than creating them directly.
func NotFound ¶
func NotFound() *ErrorResponse
NotFound is for 404 responses.
This is appropriate for when the client tries to access something that does not exist.
func NotImplemented ¶
func NotImplemented() *ErrorResponse
NotImplemented is for 501 responses.
This applies when a request is made for an HTTP method that the server understands but does not support.
func PayloadTooLarge ¶
func PayloadTooLarge() *ErrorResponse
PayloadTooLarge is for 413 responses.
This is appropriate for when the client sends a request that is larger than the limits set by the server, such as when they try to upload a file that is too big.
func PaymentRequired ¶
func PaymentRequired() *ErrorResponse
PaymentRequired is for 402 responses.
PaymentRequired is reserved for future use but has no standard around its use. It is intended to communicate to the client that their request can not be completed until a payment is made.
func ServiceUnavailable ¶
func ServiceUnavailable() *ErrorResponse
ServiceUnavailable is for 503 responses.
This is appropriate for when a server is not ready to handle a request such as when it is down for maintenance or overloaded.
Clients may retry 503's with exponential backoff.
func Teapot ¶
func Teapot() *ErrorResponse
Teapot is for 418 responses.
This is appropriate for when the server is a teapot rather than a coffee maker.
func TooEarly ¶
func TooEarly() *ErrorResponse
TooEarly is for 425 responses.
This is appropriate for when the server is concerned that the request may be replayed, resulting in a replay attack.
func TooManyRequests ¶
func TooManyRequests() *ErrorResponse
TooManyRequests is for 429 responses.
This is appropriate for when the client has been rate limited by the server.
It may be appropriate for the client to retry the request after some time has passed, it is encouraged to use this along with Retryable to communicate to the client when they are able to retry.
func Unauthorized ¶
func Unauthorized() *ErrorResponse
Unauthorized is for 401 responses.
This is appropriate for when you fail to authenticate the request.
It may be appropriate for the client to retry this request in the event, for example, if they used an expired authentication credential, they can retry after fetching a new one.
func UnprocessableEntity ¶
func UnprocessableEntity() *ErrorResponse
UnprocessableEntity is for 422 responses.
This is appropriate for when the request is valid but the server is unable to process the request instructions.
The request should not be retried without modification.
func UnsupportedMediaType ¶
func UnsupportedMediaType() *ErrorResponse
UnsupportedMediaType is for 415 responses.
This is appropriate for when the request has an unsupported media format.
func (*ErrorResponse) Retryable ¶
func (r *ErrorResponse) Retryable(w http.ResponseWriter, retryAfter time.Duration) *ErrorResponse
Retryable communicates to the caller that the request may be retried after the given duration.
This returns an ErrorResponse so it can be chained in a call to any of the `Error` methods provided in httpbp.
return httpbp.JSONError( httpbp.ServiceUnavailable().Retryable(w, time.Hour), errors.New("downtime"), )
func (ErrorResponse) String ¶
func (r ErrorResponse) String() string
String implements the fmt.Stringer interface which allows ErrorResponse to be used as a Raw response.
Returns r.Reason by default, this can be customized by using ErrorResponse.WithRawResponse.
func (ErrorResponse) TemplateName ¶
func (r ErrorResponse) TemplateName() string
TemplateName implements the httpbp.HTMLBody interface which allows ErrorResponse to be used as an HTML response.
Return httpbp.DefaultErrorTemplateName by default, this can be customized by using ErrorResponse.WithTemplateName.
func (*ErrorResponse) WithDetails ¶
func (r *ErrorResponse) WithDetails(details map[string]string) *ErrorResponse
WithDetails can be used to set the Details on an ErrorResponse in a way that can be chained in a call to `HTMLError`.
Ex:
return httpbp.JSONError( httpbp.InvalidRequest().WithDetails(map[string]string{ "foo": "foo must be > 0", "bar": "bar must be non-nil", }) errors.New("validation"), )
This is provided as syntactic sugar and is not required to set Details.
func (*ErrorResponse) WithRawResponse ¶
func (r *ErrorResponse) WithRawResponse(raw string) *ErrorResponse
WithRawResponse is used to set the respose to return when using a Raw content writer.
This is ignored when using any other content writer.
This returns an ErrorResponse so it can be chained in a call to `HTMLError`:
return httpbp.RawError( httpbp.BadGateway().WithRawResponse("oops"), errors.New("example"), )
func (*ErrorResponse) WithTemplateName ¶
func (r *ErrorResponse) WithTemplateName(name string) *ErrorResponse
WithTemplateName is used to set the name of the HTML template to use when using an HTML content writer.
This is ignored when using any other content writer. If you are creating a custom template, note that the ErrorResponse struct is what is passed to the template to render so you are limited to the values exported by ErrorResponse.
This returns an ErrorResponse so it can be chained in a call to `HTMLError`:
return httpbp.HTMLError( httpbp.BadGateway().WithTemplateName("custom"), errors.New("example"), )
type ErrorResponseJSONWrapper ¶
type ErrorResponseJSONWrapper struct { Success bool `json:"success"` Error *ErrorResponse `json:"error"` }
ErrorResponseJSONWrapper wraps the ErrorResponseBody for JSON responses.
ErrorResponseJSONWrapper should not be used directly, it is used automatically by JSONError. It is exported to provide documentation for the final response format.
type HTMLBody ¶
type HTMLBody interface { // TemplateName returns the name of the template to use to render the HTML // response. TemplateName() string }
HTMLBody is the interface that is expected by an HTML ContentWriter.
type HTTPError ¶
type HTTPError interface { error // Response returns the custom Response for the error to be written by // the ContentWriter. Response() Response // ContentWriter returns the ContentWriter object to use to write the error // response. ContentWriter() ContentWriter // Unwrap implements helper interface for errors.Unwrap. Should return the // internal error that triggered the HTTPError to be returned to the caller. Unwrap() error }
HTTPError is an error that and can be returned by an HTTPHandler to return a customized error response.
func HTMLError ¶
func HTMLError(resp *ErrorResponse, cause error, t *template.Template) HTTPError
HTMLError returns the given error as an HTTPError that will write HTML.
func JSONError ¶
func JSONError(resp *ErrorResponse, cause error) HTTPError
JSONError returns the given error as an HTTPError that will write JSON.
type HandlerFunc ¶
HandlerFunc handles a single HTTP request and can be wrapped in Middleware.
The base context is extracted from the http.Request and should be used rather than the context in http.Request. This is provided for conveinence and consistency across Baseplate.
HandlerFuncs are free to write directly to the given ResponseWriter but WriteResponse and its helpers for common Content-Types have been provided to simplify writing responses (including status code) so you should not need to. Headers and cookies should still be set using the ResponseWriter.
If a HandlerFunc returns an error, the Baseplate implementation of http.Handler will attempt to write an error response, so you should generally avoid writing your response until the end of your handler call so you know there are not any errors. If you return an HTTPError, it will use that to return a custom error response, otherwise it returns a generic, plain-text http.StatusInternalServerError (500) error message.
func Wrap ¶
func Wrap(name string, handle HandlerFunc, middlewares ...Middleware) HandlerFunc
Wrap wraps the given HandlerFunc with the given Middlewares and returns the wrapped HandlerFunc passing the given name to each middleware in the chain.
Middlewares will be called in the order that they are defined:
1: Middlewares[0] 2: Middlewares[1] ... N: Middlewares[n]
Wrap is provided for clarity and testing purposes and should not generally be called directly. Instead use one of the provided Handler constructors which will Wrap the HandlerFunc you pass it for you.
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 InjectEdgeRequestContextArgs ¶ added in v0.4.0
type InjectEdgeRequestContextArgs struct { // The HeaderTrustHandler to use. // If empty, NeverTrustHeaders{} will be used instead. TrustHandler HeaderTrustHandler // The logger to be called when edgecontext parsing failed. Logger log.Wrapper // The edgecontext implementation to use. Optional. // If not set, the global one from ecinterface.Get will be used instead. EdgeContextImpl ecinterface.Interface }
InjectEdgeRequestContextArgs are the args to be passed into InjectEdgeRequestContext function.
type Middleware ¶
type Middleware func(name string, next HandlerFunc) HandlerFunc
Middleware wraps the given HandlerFunc and returns a new, wrapped, HandlerFunc.
func DefaultMiddleware ¶
func DefaultMiddleware(args DefaultMiddlewareArgs) []Middleware
DefaultMiddleware returns a slice of all the default Middleware for a Baseplate HTTP server. The default middleware are (in order):
- InjectServerSpan
- InjectEdgeRequestContext
- PrometheusServerMetrics
func InjectEdgeRequestContext ¶
func InjectEdgeRequestContext(args InjectEdgeRequestContextArgs) Middleware
InjectEdgeRequestContext returns a Middleware that will automatically parse the EdgeRequestContext header from the request headers and attach it to the context object if present.
InjectEdgeRequestContext should generally not be used directly, instead use the NewBaseplateServer function which will automatically include InjectEdgeRequestContext as one of the Middlewares to wrap your handlers in.
func InjectServerSpan ¶
func InjectServerSpan(truster HeaderTrustHandler) Middleware
InjectServerSpan returns a Middleware that will automatically wrap the HandlerFunc in a new server span and stop the span after the function returns.
Starts the server span before calling the `next` HandlerFunc and stops the span after it finishes. If the function returns an error that's an HTTPError with a status code < 500, then it will not be passed to span.Stop, otherwise it will.
InjectServerSpan should generally not be used directly, instead use the NewBaseplateServer function which will automatically include InjectServerSpan as one of the Middlewares to wrap your handlers in.
func PrometheusServerMetrics ¶ added in v0.9.2
func PrometheusServerMetrics(_ string) Middleware
PrometheusServerMetrics returns a middleware that tracks Prometheus metrics for client http.
It emits the following prometheus metrics:
* http_server_active_requests gauge with labels:
- http_method: method of the HTTP request
* http_server_latency_seconds, http_server_request_size_bytes, http_server_response_size_bytes histograms with labels above plus:
- http_success: true if the status code is 2xx or 3xx, false otherwise
* http_server_requests_total counter with all labels above plus:
- http_response_code: numeric status code as a string, e.g. 200
TODO: the arg of this function is unused and will be removed in a future version.
func RecordStatusCode
deprecated
added in
v0.9.2
func RecordStatusCode() Middleware
RecordStatusCode extracts the status code set on the request in the following order:
- Check if WriteHeader was called on the ResponseWriter and use that code if it was.
- If an error was returned, check if it is an HTTPError. If it is, use the code from the error, otherwise assume 500.
- Assume 200.
If it sees an invalid status code (<100 or >599), it will record the status as "-nan" for codes <100 and "nan" for codes >599. Note that a code that is <100 or >599 is unlikely to appear here and will cause a panic if passed to WriteHeader.
RecordStatusCode should generally not be used directly, instead use the NewBaseplateServer function which will automatically include RecordStatusCode as one of the Middlewares to wrap your handlers in.
Deprecated: This is deprecated with statsd metrics.
func SupportedMethods ¶ added in v0.4.0
func SupportedMethods(method string, additional ...string) Middleware
SupportedMethods returns a middleware that checks if the request is made using one of the given HTTP methods.
Returns a raw, plain text 405 error response if the method is not supported. If GET is supported, HEAD will be automatically supported as well. Sets the "Allow" header automatically to the methods given.
SupportedMethods should generally not be used directly, instead use the NewBaseplateServer function which will automatically include SupportedMethods as one of the Middlewares to wrap your handlers in.
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 Pattern ¶
type Pattern string
Pattern is the pattern passed to a EndpointRegistry when registering an Endpoint.
type Response ¶
type Response struct { // Body is the response body to write using a ContentWriter. You should // ensure that Body is something that can be successfully written by the // ContentWriter, otherwise an error will be returned instead. Body interface{} // Code is the status code to set on the response, this is optional and only // should be set if you want to return something other than http.StatusOK (200). Code int }
Response is the non-header content to be written in an HTTP response.
func NewResponse ¶
func NewResponse(body interface{}) Response
NewResponse returns a new Response with the given body and default status code.
func (Response) WithCode ¶
WithCode can be used to set the Code on a Response in a way that can be chained in a call to one of the Write methods.
Ex:
httpbp.WriteRawContent( w, httpbp.NewResponse(nil).WithCode(http.StatusAccepted), httpbp.PlainTextContentType, )
This is provided as syntactic sugar and is not required to set Code.
type ServerArgs ¶
type ServerArgs struct { // Baseplate is a required argument to NewBaseplateServer and must // be non-nil. Baseplate baseplate.Baseplate // Endpoints is the mapping of endpoint patterns to Endpoint objects that // the Server will handle. // // While endpoints is not technically required, if none are provided, your // server will not handle any Endpoints. Endpoints map[Pattern]Endpoint // EndpointRegistry is an optional argument that can be used to customize // the EndpointRegistry used by the Baseplate HTTP server. // // Defaults to a new *http.ServeMux. // // Most servers will not need to set this, it has been provided for cases // where you need to use something other than http.ServeMux. // // If you do customize this, you should use a new EndpointRegistry and // register your endpoints using server.Handle rather than pre-registering // endpoints. Any endpoints registered in other ways will not be // httpbp.HandlerFunc-s and will not be wrapped in any Middleware. EndpointRegistry EndpointRegistry // Middlewares is optional, additional Middleware that will wrap any // HandlerFuncs registered to the server using server.Handle. Middlewares []Middleware // OnShutdown is an optional list of functions that can be run when // server.Stop is called. OnShutdown []func() // TrustHandler is an optional HeaderTrustHandler that will be used // by the default Middleware to determine if we can trust the HTTP // headers that can be used to initialize spans/edge request contexts. // // Defaults to NeverTrustHeaders. TrustHandler HeaderTrustHandler // Logger is an optional arg to be called when the InjectEdgeRequestContext // middleware failed to parse the edge request header for any reason. Logger log.Wrapper // The http.Server from stdlib would emit a log regarding [1] whenever it // happens. Set SuppressIssue25192 to true to suppress that log. // // Regardless of the value of SuppressIssue25192, // we always emit a prometheus counter of: // // httpbp_server_upstream_issue_logs_total{upstream_issue="25192"} // // [1]: https://github.com/golang/go/issues/25192#issuecomment-992276264 SuppressIssue25192 bool }
ServerArgs defines all of the arguments used to create a new HTTP Baseplate server.
func (ServerArgs) SetupEndpoints ¶
func (args ServerArgs) SetupEndpoints() (ServerArgs, error)
SetupEndpoints calls ValidateAndSetDefaults and registeres the Endpoints in args to the EndpointRegistry in args and returns the fully setup ServerArgs.
SetupEndpoints does not generally need to be called manually but can be used for testing purposes. It is called as a part of setting up a new Baseplate server.
func (ServerArgs) ValidateAndSetDefaults ¶
func (args ServerArgs) ValidateAndSetDefaults() (ServerArgs, error)
ValidateAndSetDefaults checks the ServerArgs for any errors and sets any default values.
ValidateAndSetDefaults does not generally need to be called manually but can be used for testing purposes. It is called as a part of setting up a new Baseplate server.
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.