Documentation ¶
Overview ¶
Package mid contains assorted middleware for use in HTTP services.
Index ¶
- Variables
- func CSRFCheck(s Session, inp string) error
- func CSRFToken(s Session) (string, error)
- func Err(f func(http.ResponseWriter, *http.Request) error) http.Handler
- func Errf(w http.ResponseWriter, code int, format string, args ...interface{})
- func IsNoSession(err error) bool
- func JSON(f interface{}) http.Handler
- func Log(next http.Handler) http.Handler
- func Request(ctx context.Context) *http.Request
- func RespondJSON(w http.ResponseWriter, obj interface{}) error
- func ResponseWriter(ctx context.Context) http.ResponseWriter
- func SessionHandler(store SessionStore, cookieName string, next http.Handler) http.Handler
- func Trace(next http.Handler) http.Handler
- func TraceID(ctx context.Context) string
- type CodeErr
- type Responder
- type ResponseWrapper
- type Session
- type SessionStore
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrCSRF = errors.New("CSRF check failed")
ErrCSRF is the error produced when an invalid CSRF token is presented to CSRFCheck.
var ErrNoSession = errors.New("no session")
ErrNoSession is the error produced by SessionStore.Get when no matching session is found.
Functions ¶
func CSRFCheck ¶ added in v1.5.0
CSRFCheck checks a CSRF token against a session for validity. TODO: check s is active and unexpired?
func CSRFToken ¶ added in v1.5.0
CSRFToken generates a new token containing a random nonce hashed with this session's CSRF key. It can be used to protect against CSRF attacks. Resources served by the application (e.g. HTML pages) should include a CSRF token. State-changing requests to the application that rely on a Session for authentication should require the caller to supply a valid CSRF token. Validity can be checked with CSRFCheck. For more on this topic see https://en.wikipedia.org/wiki/Cross-site_request_forgery.
func Err ¶
Err wraps an error-returning function as an http.Handler. If the returned error is a Responder (such as a CodeErr), its Respond method is used to respond to the request. Otherwise, if a status code has not already been set, an error return will set it to http.StatusInternalServerError, and the absence of an error will set it to http.StatusOK, or http.StatusNoContent if nothing has been written to the ResponseWriter.
func Errf ¶
func Errf(w http.ResponseWriter, code int, format string, args ...interface{})
Errf is a convenience wrapper for http.Error. It calls http.Error(w, fmt.Sprintf(format, args...), code). It also logs that message with log.Print. If code is 0, it defaults to http.StatusInternalServerError. If format is "", Errf uses http.StatusText instead.
func IsNoSession ¶ added in v1.5.0
IsNoSession tests whether the given error is either ErrNoSession or http.ErrNoCookie.
func JSON ¶
JSON produces an http.Handler by JSON encoding and decoding of a given function's input and output.
The signature of the function is:
func(context.Context, inType) (outType, error)
where inType is any type that can be decoded from JSON and outType is any type that can be encoded to JSON. These may alternatively be pointers to such types.
Every part of the signature is optional (both arguments and both return values).
Passing the wrong type of object to this function produces a panic.
When the function is called:
If a context argument is present, it is supplied from the Context() method of the pending *http.Request. That context is further adorned with the pending *http.Request and the pending http.ResponseWriter, which can be retrieved with the Request and ResponseWriter functions.
If an inType argument is present, the request is checked to ensure that the method is POST and the Content-Type is application/json; then the request body is unmarshaled into the inType argument. Note that the JSON decoder uses the UseNumber setting; see https://golang.org/pkg/encoding/json/#Decoder.UseNumber.
If an outType result is present, it is JSON marshaled and written to the pending ResponseWriter with an HTTP status of 200 (ok). If no outType is present, the default HTTP status is 204 (no content).
If an error result is present, it is handled as in Err.
Some of the code in this function is (liberally) adapted from github.com/chain/chain.
Example ¶
type ( inType struct { Referer bool `json:"referer"` UserAgent bool `json:"user_agent"` } outType struct { Referer string `json:"referer"` UserAgent string `json:"user_agent"` } ) handler := func(ctx context.Context, in inType) (outType, error) { var ( req = Request(ctx) out outType ) if in.Referer { out.Referer = req.Referer() } if in.UserAgent { out.UserAgent = req.UserAgent() } return out, nil } s := httptest.NewServer(JSON(handler)) defer s.Close() inp := `{"user_agent":true}` req, err := http.NewRequest("POST", s.URL, strings.NewReader(inp)) if err != nil { log.Fatal(err) } req.Header.Set("Content-Type", "application/json") req.Header.Set("User-Agent", "ExampleJSON-agent/1.0") var c http.Client resp, err := c.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() var out outType err = json.NewDecoder(resp.Body).Decode(&out) if err != nil { log.Fatal(err) } fmt.Printf("Server got user agent %s\n", out.UserAgent)
Output: Server got user agent ExampleJSON-agent/1.0
func Log ¶
Log adds logging on entry to and exit from an http.Handler.
If the request is decorated with a trace ID (see Trace), it is included in the generated log lines.
func Request ¶
Request returns the pending *http.Request object when called on the context passed to a JSON handler.
func RespondJSON ¶ added in v1.7.0
func RespondJSON(w http.ResponseWriter, obj interface{}) error
RespondJSON responds to an http request with a JSON-encoded object.
func ResponseWriter ¶
func ResponseWriter(ctx context.Context) http.ResponseWriter
ResponseWriter returns the pending http.ResponseWriter object when called on the context passed to a JSON handler.
func SessionHandler ¶ added in v1.5.0
SessionHandler is an http.Handler middleware wrapper. It checks the incoming request for a session in the given store. If one is found, the request's context is decorated with the session. It can be retrieved by the next handler with ContextSession. If an active, unexpired session is not found, a 403 Forbidden error is returned.
func Trace ¶
Trace decorates a request's context with a trace ID. The ID for the request is obtained from the X-Trace-Id field in the request header. If that field does not exist or is empty, Idempotency-Key and X-Idempotency-Key are tried. Failing those, a randomly generated ID is used.
The trace ID can be retrieved from a context so decorated using TraceID. Any trace ID present will be included in log lines generated by Log.
Types ¶
type CodeErr ¶
type CodeErr struct { // C is an HTTP status code. C int // Err is an optional wrapped error. Err error }
CodeErr is an error that can be returned from the function wrapped by Err to control the HTTP status code returned from the pending request.
func (CodeErr) Respond ¶
func (c CodeErr) Respond(w http.ResponseWriter)
Respond implements Responder.
type Responder ¶
type Responder interface {
Respond(http.ResponseWriter)
}
Responder is an interface for objects that know how to respond to an HTTP request. It is useful in the case of errors that want to set custom error strings and/or status codes (e.g. via http.Error).
type ResponseWrapper ¶ added in v1.3.0
type ResponseWrapper struct { // W is the wrapped ResponseWriter to which method calls are delegated. W http.ResponseWriter // N is the number of bytes that have been written with calls to Write. N int // Code is the status code that has been written with WriteHeader, // or zero if no call to WriteHeader has yet been made. // If Write is called before any call to WriteHeader, // then this is set to http.StatusOK (200). Code int }
ResponseWrapper implements http.ResponseWriter, delegating calls to a wrapped http.ResponseWriter object. It also records the status code and the number of response bytes that have been written.
func (*ResponseWrapper) Header ¶ added in v1.3.0
func (ww *ResponseWrapper) Header() http.Header
Header implements http.ResponseWriter.Header.
func (*ResponseWrapper) Result ¶ added in v1.4.0
func (ww *ResponseWrapper) Result() int
Result returns the value of the Code field, if it has been set. Otherwise it returns http.StatusOK or http.StatusNoContent depending on whether any bytes have been written.
func (*ResponseWrapper) Write ¶ added in v1.3.0
func (ww *ResponseWrapper) Write(b []byte) (int, error)
Write implements http.ResponseWriter.Write.
func (*ResponseWrapper) WriteHeader ¶ added in v1.3.0
func (ww *ResponseWrapper) WriteHeader(code int)
WriteHeader implements http.ResponseWriter.WriteHeader.
type Session ¶ added in v1.5.0
type Session interface { // CSRFKey is a persistent random bytestring that can be used for CSRF protection. CSRFKey() [sha256.Size]byte // Active is true when the session is created and false after it is canceled (via SessionStore.Cancel). Active() bool // Exp is the expiration time of the session. Exp() time.Time }
Session is the type of a session stored in a SessionStore.
func ContextSession ¶ added in v1.5.0
ContextSession returns the Session associated with a context (by SessionHandler), if there is one. If there isn't, this returns nil.
func GetSession ¶ added in v1.5.0
func GetSession(ctx context.Context, store SessionStore, cookieName string, req *http.Request) (Session, error)
GetSession checks for a session cookie in a given HTTP request and gets the corresponding session from the store.
type SessionStore ¶ added in v1.5.0
type SessionStore interface { // Get gets the session with the given key. // If no such session is found, it returns ErrNoSession. Get(context.Context, string) (Session, error) // Cancel cancels the session with the given unique key. // If the session does not exist, or is already canceled or expired, // this function silently succeeds. Cancel(context.Context, string) error }
SessionStore is persistent storage for session objects.