httpinfo

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Mar 23, 2020 License: MIT Imports: 4 Imported by: 1

README

httpinfo

godoc Licence Latest version

Build Status Code quality Code coverage

You don't need to write http.ResponseWriter wrappers anymore.

Motivation

It has become a common thing to write a wrapper of http.ResponseWriter because at some point it was a need to get request response information like the response status. In a complete request flow, a lot of middlewares required something (some requires the status, the number of bytes wrote, the route pattern used, ...). Moreover, some middlewares are also interacting with the response (like a panic or a timeout handler that sets the response status) causing unwanted behaviour (like a net/http log saying the response status should only we wrote one time). The naive approach of wrapping the http.ResponseWriter introduce some flaws and/or does not help to fix the already existing ones.

For example here:

type responseWriterWrapper struct{
    writer http.ResponseWriter
    status int
}

func (rww *responseWriterWrapper) WriteHeader(status int) {
    rww.status = status
    writer.WriteHeader(status)
}

// ...

If the original http.ResponseWriter was implementing http.Flusher, it is not the case anymore. To fix that we can add the Flush method:

func (rww *responseWriterWrapper) Flush() {
    if f, ok := (rww.writer).(http.Flusher); ok {
        f.Flush()
    }
}

but now our wrapper always implements the http.Flusher interface which can also cause unwanted behaviour.

For all these reasons I decided to write my last wrapper of http.ResponseWriter and it has the following features:

  • records the http response status, the number of bytes wrote, the execution time of the next handler, and helps to retrieve the route matching pattern.
  • writes the response status at the last possible time, to prevent the multiple status wrote error
  • keeps the same net/http interfaces implementation of the wrapped http.ResponseWriter
  • heavily tested
  • multi-thread safe
  • super simple to use

Usage / examples

// during the router setup...
router.Use(
    httpinfo.Record(),
    // other middlewares goes after, even the panic recover one
    myMiddleware,
)

func myMiddleware(next http.Handler) http.Handler {
    return func (rw http.ResponseWriter, r *http.Request ) {
        // call the next handler
        next.ServeHTTP(rw, r)

        // within any request handler, you're now able to get response info
        var (
            status        = httpinfo.Status(r)
            route         = httpinfo.Route(r)
            contentLength = httpinfo.ContentLength(r)
            latency       = httpinfo.ExecutionTime(r)
        )
        // ...
    }
}

More doc and examples in the httpinfo's godoc

License

This project is under the MIT licence, please see the LICENCE file.

Documentation

Overview

Package httpinfo is a http middleware that wraps and adds data ( as response status, response time, ...) and help the retrieval of these information.

Example
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/krostar/httpinfo"
)

func myMiddleware(next http.Handler) http.HandlerFunc {
	return func(rw http.ResponseWriter, r *http.Request) {
		next.ServeHTTP(rw, r)

		if httpinfo.IsUsed(r) {
			fmt.Printf("status      = %d\n", httpinfo.Status(r))
			fmt.Printf("bytes wrote = %d\n", httpinfo.ContentLength(r))
		}
	}
}

func myHandler(rw http.ResponseWriter, _ *http.Request) {
	rw.Write([]byte("Hello world")) // nolint: errcheck, gosec
	rw.WriteHeader(http.StatusAlreadyReported)
}

func main() {
	var srv = httptest.NewServer(
		httpinfo.Record()(
			myMiddleware(http.HandlerFunc(myHandler)),
		),
	)
	defer srv.Close()

	resp, err := http.DefaultClient.Get(srv.URL)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close() // nolint: errcheck, gosec

}
Output:

status      = 208
bytes wrote = 11

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func ContentLength

func ContentLength(r *http.Request) int

ContentLength returns the response content length. The value is likely to change throughout the execution of a request (each time ResponseWriter.Write is called). It is advised to only use this function after calling the next handler.

func ExecutionTime

func ExecutionTime(r *http.Request) time.Duration

ExecutionTime returns the duration since the request start. The value will only have sens after the next handler returned. It is advised to only use this function after calling the next handler.

func IsUsed

func IsUsed(r *http.Request) bool

IsUsed returns true if the Record middleware has been used and false otherwise.

func Record

func Record(opts ...Option) func(http.Handler) http.Handler

Record records the http response information and helps to reach them from any other middleware. See examples on how to use it.

func Route

func Route(r *http.Request) string

Route returns the route that matches the request. The value is returned thanks to the WithRouteGetterFunc option. It is advised to only use this function after calling the next handler.

func Status

func Status(r *http.Request) int

Status returns the response status. The value is likely to change throughout the execution of a request (each time ResponseWriter.WriteHeader is called). It is advised to only use this function after calling the next handler.

Types

type Option

type Option func(rr *responseRecorder)

Option defines the way to configure the response recorder.

func WithRouteGetterFunc

func WithRouteGetterFunc(routeGetter func(r *http.Request) string) Option

WithRouteGetterFunc overrides the default route getter function.

Jump to

Keyboard shortcuts

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