httpbp

package
v0.4.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 7, 2020 License: BSD-3-Clause Imports: 24 Imported by: 0

Documentation

Overview

Package httpbp provides Baseplate specific helpers and integrations for http services.

Index

Examples

Constants

View Source
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"
)
View Source
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"
)
View Source
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"
)
View Source
const AllowHeader = "Allow"

AllowHeader is the "Allow" header. This should be set when returning a 405 - Method Not Allowed error.

View Source
const HealthCheckProbeQuery = "type"

HealthCheckProbeQuery is the name of HTTP query defined in Baseplate spec.

Variables

This section is empty.

Functions

func GetHealthCheckProbe added in v0.4.2

func GetHealthCheckProbe(query url.Values) (int64, error)

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.

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"

	baseplate "github.com/reddit/baseplate.go"
	"github.com/reddit/baseplate.go/httpbp"
	"github.com/reddit/baseplate.go/log"
	"github.com/reddit/baseplate.go/redisbp"
	"github.com/reddit/baseplate.go/secrets"
)

type config struct {
	Redis redisbp.ClusterConfig `yaml:"redis"`
}

type body struct {
	X int `json:"x"`
	Y int `json:"y"`
}

type Handlers struct {
	secrets    *secrets.Store
	redisAddrs []string
}

func (h Handlers) Home(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	return httpbp.WriteJSON(w, httpbp.NewResponse(body{X: 1, Y: 2}))
}

func (h Handlers) ServerErr(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	return httpbp.JSONError(httpbp.InternalServerError(), errors.New("example"))
}

func (h Handlers) 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 (h Handlers) 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 (h Handlers) Endpoints() map[httpbp.Pattern]httpbp.Endpoint {
	return map[httpbp.Pattern]httpbp.Endpoint{
		"/": {
			Name:    "home",
			Handle:  h.Home,
			Methods: []string{http.MethodGet},
		},
		"/err": {
			Name:    "err",
			Handle:  h.ServerErr,
			Methods: []string{http.MethodGet, http.MethodPost},
		},
		"/ratelimit": {
			Name:    "ratelimit",
			Handle:  h.Ratelimit,
			Methods: []string{http.MethodGet},
		},
		"/invalid-input": {
			Name:    "invalid-input",
			Handle:  h.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() {
	var cfg config
	ctx, bp, err := baseplate.New(context.Background(), "example.yaml", &cfg)
	if err != nil {
		panic(err)
	}
	defer bp.Close()

	handlers := Handlers{
		secrets:    bp.Secrets(),
		redisAddrs: cfg.Redis.Addrs,
	}
	server, err := httpbp.NewBaseplateServer(httpbp.ServerArgs{
		Baseplate:   bp,
		Endpoints:   handlers.Endpoints(),
		Middlewares: []httpbp.Middleware{loggingMiddleware},
	})
	if err != nil {
		panic(err)
	}
	log.Info(baseplate.Serve(ctx, server))
}
Output:

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

func RegisterDefaultErrorTemplate(t *template.Template) (*template.Template, error)

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 SetEdgeContextHeader

func SetEdgeContextHeader(ec *edgecontext.EdgeRequestContext, 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 WriteHTML

func WriteHTML(w http.ResponseWriter, resp Response, templates *template.Template) error

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.

func (AlwaysTrustHeaders) TrustSpan

func (h AlwaysTrustHeaders) TrustSpan(r *http.Request) bool

TrustSpan always returns true. The span 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 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 edgecontext implementation to use. Required.
	EdgeContextImpl *edgecontext.Impl

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

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.

func (Endpoint) Validate

func (e Endpoint) Validate() error

Validate checks for input errors on the Endpoint and returns an error if any exist.

type EndpointRegistry

type EndpointRegistry interface {
	http.Handler

	Handle(pattern string, handler http.Handler)
}

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.

func RawError

func RawError(resp *ErrorResponse, cause error, contentType string) HTTPError

RawError returns the given error as an HTTPError that will write a raw response of the given content-type.

type HandlerFunc

type HandlerFunc func(context.Context, http.ResponseWriter, *http.Request) error

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 edgecontext implementation to use. Required.
	EdgeContextImpl *edgecontext.Impl

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

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 of the default Middleware for a Baseplate HTTP server.

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 HansderFunc in a new server span and stop the span after the function returns.

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 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.

func (NeverTrustHeaders) TrustSpan

func (h NeverTrustHeaders) TrustSpan(r *http.Request) bool

TrustSpan always returns false. The span 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

func (r Response) WithCode(code int) Response

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
}

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.

type TrustHeaderSignatureArgs

type TrustHeaderSignatureArgs struct {
	SecretsStore          *secrets.Store
	EdgeContextSecretPath string
	SpanSecretPath        string
}

TrustHeaderSignatureArgs is used as input to create a new TrustHeaderSignature.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL