Skip to content

Latest commit

 

History

History
85 lines (64 loc) · 3.61 KB

README.md

File metadata and controls

85 lines (64 loc) · 3.61 KB

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.