Documentation ¶
Overview ¶
Package gohm is a tiny Go library with HTTP middleware functions.
gohm provides a small collection of middleware functions to be used when creating a HTTP micro service written in Go.
One function in particular, gohm.Error, is not used as HTTP middleware, but as a helper for emitting a sensible error message back to the HTTP client when the HTTP request could not be fulfilled. It emits a text response beginning with the status code, the human friendly status message, followed by an optional text message. It is meant to be a drop in replacement for http.Error message, that formats the error message in a more conventional way to include the status code and message.
// Example function which guards downstream handlers to ensure only HTTP GET method used // to access resource. func onlyGet(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { gohm.Error(w, r.Method, http.StatusMethodNotAllowed) // 405 Method Not Allowed: POST return } next.ServeHTTP(w, r) }) }
All the other gohm functions are HTTP middleware functions, designed to wrap any HTTP handler, composing some functionality around it. They can be interchanged and used with other HTTP middleware functions providing those functions adhere to the http.Handler interface and have a ServeHTTP(http.ResponseHandler, *http.Request) method.
mux := http.NewServeMux() var h http.HandlerFunc = someHandler h = gohm.WithGzip(h) h = gohm.ConvertPanicsToErrors(h) h = gohm.WithTimeout(globalTimeout, h) h = gohm.LogErrors(os.Stderr, h) mux.Handle("/static/", h)
*NOTE:* When both the WithTimeout and the ConvertPanicsToErrors are used, the WithTimeout ought to wrap the ConvertPanicsToErrors. This is because timeout handlers in Go are generally implemented using a separate goroutine, and the panic could occur in an alternate goroutine and not get caught by the ConvertPanicsToErrors.
Index ¶
- Constants
- func AllowedMethodsHandler(allowedMethods []string, next http.Handler) http.Handler
- func CORSHandler(config CORSConfig, next http.Handler) http.Handler
- func DefaultHandler(pathOfIndexFile string) http.Handler
- func Error(w http.ResponseWriter, text string, code int)
- func ForbidDirectories(next http.Handler) http.Handler
- func New(next http.Handler, config Config) http.Handler
- func Prefix(r *http.Request) (prefix string)
- func ShiftPath(p string) (string, string)
- func StaticHandler(virtualRoot, fileSystemRoot string) http.Handler
- func StaticHandlerWithoutProbingProtection(virtualRoot, fileSystemRoot string) http.Handler
- func WithCompression(next http.Handler) http.Handler
- func WithGzip(next http.Handler) http.Handler
- func WithRequestDumper(flag *uint32, next http.Handler) http.Handler
- func XFrameOptions(value string, next http.Handler) http.Handler
- type BytesBufferPool
- type CORSConfig
- type Config
- type Counters
- func (c Counters) Get1xx() uint64
- func (c Counters) Get2xx() uint64
- func (c Counters) Get3xx() uint64
- func (c Counters) Get4xx() uint64
- func (c Counters) Get5xx() uint64
- func (c Counters) GetAll() uint64
- func (c *Counters) GetAndReset1xx() uint64
- func (c *Counters) GetAndReset2xx() uint64
- func (c *Counters) GetAndReset3xx() uint64
- func (c *Counters) GetAndReset4xx() uint64
- func (c *Counters) GetAndReset5xx() uint64
- func (c *Counters) GetAndResetAll() uint64
- type Statistics
Constants ¶
const ApacheCommonLogFormat = "{client-ip} - - [{begin}] \"{method} {uri} {proto}\" {status} {bytes}"
ApacheCommonLogFormat (CLF) is the default log line format for Apache Web Server. It is included here for users of this library that would like to easily specify log lines out to be emitted using the Apache Common Log Format (CLR), by setting `LogFormat` to `gohm.ApackeCommonLogFormat`.
const DefaultLogFormat = "{client-ip} [{begin-iso8601}] \"{method} {uri} {proto}\" {status} {bytes} {duration} {error}"
DefaultLogFormat is the default log line format used by this library.
const LogStatus1xx uint32 = 1
LogStatus1xx used to log HTTP requests which have a 1xx response
const LogStatus2xx uint32 = 2
LogStatus2xx used to log HTTP requests which have a 2xx response
const LogStatus3xx uint32 = 4
LogStatus3xx used to log HTTP requests which have a 3xx response
const LogStatus4xx uint32 = 8
LogStatus4xx used to log HTTP requests which have a 4xx response
const LogStatus5xx uint32 = 16
LogStatus5xx used to log HTTP requests which have a 5xx response
const LogStatusAll uint32 = 1 | 2 | 4 | 8 | 16
LogStatusAll used to log all HTTP requests
const LogStatusErrors uint32 = 8 | 16
LogStatusErrors used to log HTTP requests which have 4xx or 5xx response
Variables ¶
This section is empty.
Functions ¶
func AllowedMethodsHandler ¶
AllowedMethodsHandler returns a handler that only permits specified request methods, and responds with an error message when request method is not a member of the sorted list of allowed methods.
func CORSHandler ¶
func CORSHandler(config CORSConfig, next http.Handler) http.Handler
CORSHandler returns a handler that responds to OPTIONS request so that CORS requests from an origin that matches the specified allowed origins regular expression are permitted, while other origins are denied. If a request origin matches the specified regular expression, the handler responds with the specified allowOriginResponse value in the "Access-Control-Allow-Origin" HTTP response header.
func DefaultHandler ¶
DefaultHandler serves the specified file when request is for an empty path, "/", but serves a 404 Not Found for all other requests.
When most users first visit a website, they visit it with an empty URL path. This handler responds to such a request with the contents of the specified file. However, the standard library's HTTP multiplexer type, http.ServeMux, will cause all requests that have an URL path that do not match any other registered handler to match against "/", causing invalid URL path requests to be given the index page rather than a 404 Not Found. This handler corrects that behavior by differentiating whether the request URL path is "/". If so it serves the contents of the specified file; otherwise a 404 Not Found is returned to the user.
http.Handle("/", gohm.DefaultHandler(filepath.Join(staticPath, "index.html")))
func Error ¶
func Error(w http.ResponseWriter, text string, code int)
Error formats and emits the specified error message text and status code information to the http.ResponseWriter, to be consumed by the client of the service. This particular helper function has nothing to do with emitting log messages on the server side, and only creates a response for the client. However, if a handler that invokes gohm.Error is wrapped with logging functionality by gohm.New, then gohm will also emit a sensible log message based on the specified status code and message text. Typically handlers will call this method prior to invoking return to return to whichever handler invoked it.
// example function which guards downstream handlers to ensure only HTTP GET method used // to access resource. func onlyGet(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { gohm.Error(w, r.Method, http.StatusMethodNotAllowed) // 405 Method Not Allowed: POST return } next.ServeHTTP(w, r) }) }
func ForbidDirectories ¶
ForbidDirectories prevents clients from probing file servers.
ForbidDirectories will respond to all requests that have a "/" suffix with 403 Forbidden, but will forward all requests without the "/" suffix to the specified next handler.
The standard library's http.FileServer will enumerate a directory's contents when a client requests a directory. This function's purpose is to prevent clients from probing a static file server to see its resources by attempting to query directories.
http.Handle("/static/", gohm.ForbidDirectories(gohm.StaticHandler("/static/", staticPath)))
func New ¶
New returns a new http.Handler that calls the specified next http.Handler, and performs the requested operations before and after the downstream handler as specified by the gohm.Config structure passed to it.
It receives a gohm.Config struct rather than a pointer to one, so users less likely to consider modification after creating the http.Handler.
// Used to control how long it takes to serve a static file. const staticTimeout = time.Second var ( // Will store statistics counters for status codes 1xx, 2xx, 3xx, 4xx, // 5xx, as well as a counter for all responses counters gohm.Counters // Used to dynamically control log level of HTTP logging. After handler // created, this must be accessed using the sync/atomic package. logBitmask = gohm.LogStatusErrors // Determines HTTP log format logFormat = "{http-CLIENT-IP} {client-ip} [{end}] \"{method} {uri} {proto}\" {status} {bytes} {duration} {message}" ) func main() { h := http.StripPrefix("/static/", http.FileServer(http.Dir("static"))) h = gohm.WithGzip(h) // gzip response if client accepts gzip encoding // gohm was designed to wrap other http.Handler functions. h = gohm.New(h, gohm.Config{ Counters: &counters, // pointer given so counters can be collected and optionally reset LogBitmask: &logBitmask, // pointer given so bitmask can be updated using sync/atomic LogFormat: logFormat, LogWriter: os.Stderr, Timeout: staticTimeout, }) http.Handle("/static/", h) log.Fatal(http.ListenAndServe(":8080", nil)) }
func Prefix ¶ added in v2.15.0
Prefix strips the prefix from the start of the request's path and returns it, modifying the request's path by removing the stripped prefix. When the request path is for the empty URL, it returns an empty string and does not modify the request.
func ShiftPath ¶ added in v2.15.0
ShiftPath splits off the first component of p. The first return value will never contain a slash, while the second return value will always start with a slash, followed by all remaining path components, with the final slash removed when remaining path components are returned.
Example:
"/foo/bar/baz" -> "foo", "/bar/baz" "/foo/bar/baz/" -> "foo", "/bar/baz"
Inspired by:
https://blog.merovius.de/2017/06/18/how-not-to-use-an-http-router.html
Expects paths to be initially cleaned:
p = path.Clean("/" + p)[1:] head, tail := shiftPath(p)
func StaticHandler ¶
StaticHandler serves static files, preventing clients from probing file servers.
The specified virtual root will be stripped from the prefix, such that when "$virtualRoot/foo/bar" is requested, "$fileSystemRoot/foo/bar" will be served.
This handler will respond to all requests that have a "/" suffix with 403 Forbidden, but will attempt to serve all requests without the "/" suffix by serving the corresponding file.
The standard library's http.FileServer will enumerate a directory's contents when a client requests a directory. This function's purpose is to prevent clients from probing a static file server to see its resources by attempting to query directories.
http.Handle("/static/", gohm.StaticHandler("/static/", staticPath))
func StaticHandlerWithoutProbingProtection ¶
StaticHandlerWithoutProbingProtection serves static files, and when a directory is requested, will serve a representation of the directory's contents.
The specified virtual root will be stripped from the prefix, such that when "$virtualRoot/foo/bar" is requested, "$fileSystemRoot/foo/bar" will be served.
Please use the StaticHandler function rather than this function, unless your application specifically benefits from clients probing your file server's contents.
func WithCompression ¶
WithCompression returns a new http.Handler that optionally compresses the response text using either the gzip or deflate compression algorithm when the HTTP request's `Accept-Encoding` header includes the string `gzip` or `deflate`. To prevent the downstream http.Handler from also seeing the `Accept-Encoding` request header, and possibly also compressing the data a second time, this function removes that header from the request.
NOTE: The specified next http.Handler ought not set `Content-Length` header, or the reported length value will be wrong. As a matter of fact, all HTTP response handlers ought to allow net/http library to set `Content-Length` response header or not based on a handful of RFCs.
mux := http.NewServeMux() mux.Handle("/example/path", gohm.WithCompression(someHandler))
func WithGzip ¶
WithGzip returns a new http.Handler that optionally compresses the response text using the gzip compression algorithm when the HTTP request's `Accept-Encoding` header includes the string `gzip`.
NOTE: The specified next http.Handler ought not set `Content-Length` header, or the reported length value will be wrong. As a matter of fact, all HTTP response handlers ought to allow net/http library to set `Content-Length` response header or not based on a handful of RFCs.
mux := http.NewServeMux() mux.Handle("/example/path", gohm.WithGzip(someHandler))
func WithRequestDumper ¶
WithRequestDumper wraps http.Handler and optionally dumps the request when the specified flag is non-zero. It uses atomic.LoadUnit32 to read the flag. When 0, requests will not be dumped. When 1, all but the body will be dumped. When 2, the entire request including the body will be dumped.
func XFrameOptions ¶
XFrameOptions sets the X-Frame-Options response header to the specified value, then serves the request by the specified next handler.
The X-Frame-Options HTTP response header is frequently used to block against clickjacking attacks. See https://tools.ietf.org/html/rfc7034 for more information.
someHandler = gohm.XFrameOptions("SAMEORIGIN", someHandler)
Types ¶
type BytesBufferPool ¶ added in v2.12.0
BytesBufferPool specifies any structure that can provide bytes.Buffer instances from a pool. One such performant and well-tested implementation for a free-list of buffers is https://github.com/karrick/gobp
type CORSConfig ¶
type CORSConfig struct { // OriginsFilter is a regular expression that acts as a filter against the // "Origin" header value for pre-flight checks. OriginsFilter *regexp.Regexp // AllowHeaders is a list of HTTP header names which are allowed to be sent // to this handler. AllowHeaders []string // AllowMethods is a list of HTTP method names which are allowed for this // handler. AllowMethods []string // MaxAgeSeconds is the number of seconds used to fill the // "Access-Control-Max-Age" header in pre-flight check responses. MaxAgeSeconds int }
CORSConfig holds parameters for configuring a CORSHandler.
type Config ¶
type Config struct { // AllowPanics, when set to true, causes panics to propagate from downstream // handlers. When set to false, also the default value, panics will be // converted into Internal Server Errors (status code 500). You cannot // change this setting after creating the http.Handler. AllowPanics bool // BufPool, when not nil, specifies a free-list pool of buffers to be used // to reduce garbage collection by reusing bytes.Buffer instances. BufPool BytesBufferPool // Callback, when not nil, is called after completion of each request with a // structure holding request and response information. Callback func(*Statistics) // Counters, if not nil, tracks counts of handler response status codes. Counters *Counters // EscrowReader specifies whether the middleware handler ought to provide an // escrow reader for the request body. The escrow reader reads the body // exactly once, storing the payload in a buffer, from which the downstream // request handler reads the data as it normally would, but also where an // optional Callback function might be able to have access to the request // body payload. When false, the specified Callback will return a nil for // the Statistics.ResponseBody. EscrowReader bool // LogBitmask, if not nil, specifies a bitmask to use to determine which // HTTP status classes ought to be logged. If not set, all HTTP requests // will be logged. This value may be changed using sync/atomic package even // after creating the http.Handler. // // The following bitmask values are supported: // // LogStatus1xx : LogStatus1xx used to log HTTP requests which have a 1xx response // LogStatus2xx : LogStatus2xx used to log HTTP requests which have a 2xx response // LogStatus3xx : LogStatus3xx used to log HTTP requests which have a 3xx response // LogStatus4xx : LogStatus4xx used to log HTTP requests which have a 4xx response // LogStatus5xx : LogStatus5xx used to log HTTP requests which have a 5xx response // LogStatusAll : LogStatusAll used to log all HTTP requests // LogStatusErrors : LogStatusAll used to log HTTP requests which have 4xx or 5xx response LogBitmask *uint32 // LogFormat specifies the format for log lines. When left empty, // gohm.DefaultLogFormat is used. You cannot change the log format after // creating the http.Handler. // // The following format directives are supported: // // begin-epoch : time request received (epoch) // begin-iso8601 : time request received (ISO-8601 time format) // begin : time request received (apache log time format) // bytes : response size // client-ip : client IP address // client-port : client port // client : client-ip:client-port // duration : duration of request from beginning to end, (seconds with millisecond precision) // end-epoch : time request completed (epoch) // end-iso8601 : time request completed (ISO-8601 time format) // end : time request completed (apache log time format) // error : error message associated with attempting to serve the query // method : request method, e.g., GET or POST // proto : request protocol, e.g., HTTP/1.1 // status : response status code // status-text : response status text // uri : request URI LogFormat string // LogWriter, if not nil, specifies that log lines ought to be written to // the specified io.Writer. You cannot change the io.Writer to which logs // are written after creating the http.Handler. LogWriter io.Writer // `Timeout`, when not 0, specifies the amount of time allotted to wait for // downstream `http.Handler` response. You cannot change the handler // timeout after creating the `http.Handler`. The zero value for Timeout // elides timeout protection, and `gohm` will wait forever for a downstream // `http.Handler` to return. It is recommended that a sensible timeout // always be chosen for all production servers. Timeout time.Duration }
Config specifies the parameters used for the wrapping the downstream http.Handler.
type Counters ¶
type Counters struct {
// contains filtered or unexported fields
}
Counters structure store status counters used to track number of HTTP responses resulted in various status classes.
var counters gohm.Counters mux := http.NewServeMux() mux.Handle("/example/path", gohm.New(someHandler, gohm.Config{Counters: &counters})) // later on... countOf1xx := counters.Get1xx() countOf2xx := counters.Get2xx() countOf3xx := counters.Get3xx() countOf4xx := counters.Get4xx() countOf5xx := counters.Get5xx() countTotal := counters.GetAll()
func (*Counters) GetAndReset1xx ¶
GetAndReset1xx returns number of HTTP responses resulting in a 1xx status code, and resets the counter to 0.
func (*Counters) GetAndReset2xx ¶
GetAndReset2xx returns number of HTTP responses resulting in a 2xx status code, and resets the counter to 0.
func (*Counters) GetAndReset3xx ¶
GetAndReset3xx returns number of HTTP responses resulting in a 3xx status code, and resets the counter to 0.
func (*Counters) GetAndReset4xx ¶
GetAndReset4xx returns number of HTTP responses resulting in a 4xx status code, and resets the counter to 0.
func (*Counters) GetAndReset5xx ¶
GetAndReset5xx returns number of HTTP responses resulting in a 5xx status code, and resets the counter to 0.
func (*Counters) GetAndResetAll ¶
GetAndResetAll returns number of HTTP responses resulting in a All status code, and resets the counter to 0.
type Statistics ¶ added in v2.12.0
type Statistics struct { // RequestBegin is the time the request handling began. RequestBegin time.Time // RequestBody is the byte slice of the request body, if applicable. RequestBody []byte // ResponseStatus is the status code of the response. ResponseStatus int // ResponseEnd is the time response writing completed. ResponseEnd time.Time // contains filtered or unexported fields }
Statistics structures are passed to callback functions after the downstream handler has completed services the request.
func (*Statistics) Log ¶ added in v2.13.0
func (stats *Statistics) Log()
Log causes the gohm handler to emit the log line for this request.