A collection of useful middleware for Go HTTP services & web applications πŸ›ƒ

gorilla/handlers

GoDoc CircleCI Sourcegraph

Package handlers is a collection of handlers (aka "HTTP middleware") for use with Go's net/http package (or any framework supporting http.Handler), including:

Other handlers are documented on the Gorilla website.

Example

A simple example using handlers.LoggingHandler and handlers.CompressHandler:

import (
    "net/http"
    "github.com/gorilla/handlers"
)

func main() {
    r := http.NewServeMux()

    // Only log requests to our admin dashboard to stdout
    r.Handle("/admin", handlers.LoggingHandler(os.Stdout, http.HandlerFunc(ShowAdminDashboard)))
    r.HandleFunc("/", ShowIndex)

    // Wrap our server with our gzip handler to gzip compress all responses.
    http.ListenAndServe(":8000", handlers.CompressHandler(r))
}

License

BSD licensed. See the included LICENSE file for details.

Owner
Gorilla Web Toolkit
Gorilla is a web toolkit for the Go programming language that provides useful, composable packages for writing HTTP-based applications.
Gorilla Web Toolkit
Comments
  • [bug] COmpressHandler is broken

    [bug] COmpressHandler is broken

    Everything was working till yesterday. it starts failing today. I am using the google cloud build , so it takes the latest mux from repo. Local mux which is old is working fine.

    I am using the compress handler like this

    http.ListenAndServe(":8080", handlers.CompressHandler(r))

    and serving static files.

    but all the browser are not able to read the content. I tested with Mozzila , chrome and Microsoft EDGE.

    Following error is shown

    image

    When I use without compress , things works but now my server slow without compression

    http.ListenAndServe(":8080",r)

  • Using handler.CompressHandler with mux.NotFoundHandler

    Using handler.CompressHandler with mux.NotFoundHandler

    Hey,

    I seem to be getting errors when using a CompressHandler in conjunction with a NotFoundHandler. In the following example my 404's don't work at all and the browser's just dumping a "This site can’t be reached" error to the screen. No error messages or stack traces coming from Go either :/

    If I comment out r.NotFoundHandler or h2 := handlers.CompressHandler(h1) then it works.

    package main
    
    import (
        "fmt"
        "net/http"
        "os"
    
        "github.com/gorilla/handlers"
        "github.com/gorilla/mux"
    )
    
    func main() {
        r := mux.NewRouter()
    
        r.HandleFunc("/", IndexHandler).Methods("GET")
        r.HandleFunc("/test", TestHandler).Methods("GET")
        r.NotFoundHandler = http.HandlerFunc(NotFoundHandler)
    
        h1 := handlers.CombinedLoggingHandler(os.Stdout, r)
        h2 := handlers.CompressHandler(h1)
    
        http.ListenAndServe("localhost:"+os.Getenv("PORT"), h2)
    }
    
    func NotFoundHandler(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusNotFound)
        fmt.Fprintf(w, "404 - Not Found")
    }
    
    func IndexHandler(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, World!")
    }
    
    func TestHandler(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Looks good!")
    }
    
  • Loggers don't support the http.Flusher interface

    Loggers don't support the http.Flusher interface

    Spotted this goofing around trying to replace go-restful with a more minimal stack, noticed that here https://github.com/gorilla/handlers/blob/master/handlers.go#L66-L90 there's no support for flushing the buffer if it's supported, would it be accepted as a PR if I would send it ?

  • [bug] Request URL Host not set in ProxyHeaders handler

    [bug] Request URL Host not set in ProxyHeaders handler

    When using X-Forwarded-Host header from a reverse proxy, the handler correctly sets r.Host (r is the http.Request), but leaves r.URL.Host unset. This causes some issues with some libraries (eg httprouter) which expects to do something like r.URL.String() to recover the original HTTP(S) request URL.

    Versions Go version: 1.13.4 package version: v1.4.2 (8a3748a)

    Steps to Reproduce Set up a reverse proxy which sends X-Forwarded-Host (such as Traefik) and try to print r.URL.String(). The URL that is built is missing the host part.

    Expected behavior The ProxyHeaders handler should set r.URL.Host as already does for r.Host

    Code Snippets

    // Setup an HTTP server with Go and ProxyHeaders handler
    // Assume a request like https://example.org/page
    // Assume that the reverse proxy is sending headers:
    // X-Forwarded-Host: example.org
    // X-Forwarded-Proto: https
    
    func pageHandler(w http.ResponseWriter, r *http.Request) {
       // Inside an handler function
       fmt.Println(r.URL.String())
       // this should print https://example.org/page
       // instead it prints https:///page
    }
    
  • [feature] Ability to print stack trace as log

    [feature] Ability to print stack trace as log

    Is your feature request related to a problem? Please describe. We are using recovery handler with stack trace. However, as per https://github.com/gorilla/handlers/blob/master/recovery.go#L89, stack trace is only printed to stdout.

    Describe the solution you'd like As we are logging everything as json, having raw stdout for stack trace is not ideal. It will be great to have ability to print stack trace as log.

    Describe alternatives you've considered

  • Proposed enhancements

    Proposed enhancements

    CORSHandler CacheHandler

    and

    GZipHandler come to mind. For the later there is an example here: https://github.com/the42/schoolcalc/blob/master/webzapfen/webzapfen.go#L611

    However, ideally, Handlers would be stackable.

  • Bugfix for reusing CORS() configuration with multiple handlers

    Bugfix for reusing CORS() configuration with multiple handlers

    I was running into a weird issue where my re-using of the CORS handler would lead to the wrong handler being called by my mux, and tracked it down to this bug. I noted that in comparably-configured middleware, like CSRF, the factory should set the handler inside the closure.

    This branch very simply does just that (+ I added a test, which might be overkill).

  • Added RecoveryHandler

    Added RecoveryHandler

    Good Afternoon! This handler provides the capability to recover from a panic while logging and sending an HTTP 500 error. This follows closely with the negroni recovery middleware.

    Take care, Nicholas

  • [feature] Configure CORS middleware to allow all headers

    [feature] Configure CORS middleware to allow all headers

    Is your feature request related to a problem? Please describe.

    I would like the ability to configure the CORS middleware to allow all headers.

    Describe the solution you'd like

    The handlers.AllowedHeaders() function could support "*", which would signal that all headers should be allowed. This is how AllowedOrigins() works, so it would look exactly the same (i.e. handlers.AllowedOrigins([]string{"*"}), and handlers.AllowedHeaders([]string{"*"}))

    I'd be happy to make a pull request if this is something you'd accept.

  • CORS 403 Forbidden

    CORS 403 Forbidden

    Hi there,

    I've an issue using CORS, I always get 403 forbidden back on OPTIONS requests.

    router := mux.NewRouter()
    router.HandleFunc("/omniview", rapi.omniview)
    
    http.ListenAndServe(":8000", handlers.CORS()(router))
    

    the corresponding CURL call I used to simulate the webcall of my web ui:

    $ curl 'http://dtstest:8000/omniview' -X OPTIONS -H 'Access-Control-Request-Method: POST' -H 'Origin: http://dtstest:8080' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7' -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36' -H 'Accept: */*' -H 'Connection: keep-alive' -H 'Access-Control-Request-Headers: content-type' --compressed --verbose
    * STATE: INIT => CONNECT handle 0x600057538; line 1429 (connection #-5000)
    * Added connection 0. The cache now contains 1 members
    * STATE: CONNECT => WAITRESOLVE handle 0x600057538; line 1465 (connection #0)
    *   Trying 10.200.225.24...
    * TCP_NODELAY set
    * STATE: WAITRESOLVE => WAITCONNECT handle 0x600057538; line 1546 (connection #0)
    * Connected to dtstest (10.200.225.24) port 8000 (#0)
    * STATE: WAITCONNECT => SENDPROTOCONNECT handle 0x600057538; line 1598 (connection #0)
    * Marked for [keep alive]: HTTP default
    * STATE: SENDPROTOCONNECT => DO handle 0x600057538; line 1616 (connection #0)
    > OPTIONS /omniview HTTP/1.1
    > Host: dtstest:8000
    > Access-Control-Request-Method: POST
    > Origin: http://dtstest:8080
    > Accept-Encoding: gzip, deflate
    > Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
    > User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
    > Accept: */*
    > Connection: keep-alive
    > Access-Control-Request-Headers: content-type
    >
    * STATE: DO => DO_DONE handle 0x600057538; line 1695 (connection #0)
    * STATE: DO_DONE => WAITPERFORM handle 0x600057538; line 1822 (connection #0)
    * STATE: WAITPERFORM => PERFORM handle 0x600057538; line 1837 (connection #0)
    * HTTP 1.1 or later with persistent connection, pipelining supported
    < HTTP/1.1 403 Forbidden
    < Date: Thu, 30 Aug 2018 10:23:55 GMT
    < Content-Length: 0
    < Content-Type: text/plain; charset=utf-8
    <
    * STATE: PERFORM => DONE handle 0x600057538; line 2008 (connection #0)
    * multi_done
    * Connection #0 to host dtstest left intact
    * Expire cleared
    

    What am I doing wrong there? If I do handlers.CORS()(router) before actually setting the routes the requests work (though, r.Body seems to be empty then)

    router := mux.NewRouter()
     handlers.CORS()(router)
    router.HandleFunc("/omniview", rapi.omniview)
    
    http.ListenAndServe(":8000", router)
    
  • CompressHandler: Write on hijacked connection error

    CompressHandler: Write on hijacked connection error

    If the ResponseWriter I pass to handlers.CompressHandler support http.Hijacker gets hijacked, I'll often see this error come out of my server:

    2015/08/25 09:20:41 server.go:1934: http: response.Write on hijacked connection
    

    I believe this is caused by the deferred Close calls on the gzip/flat Writer's. The documentation for both says that calling Close will flush any pending data, which might generate calls to Write. I believe the fix is to set a flag if Hijack is called on the connection, and only call Call on the gzip/flat Writer if the connection was not hijacked.

  • The ProxyHeaders middleware is misleading and dangerous

    The ProxyHeaders middleware is misleading and dangerous

    There's no universal standard for what the proxy headers mean or what order IP addresses are in. Having an open source package that makes it look like you can "just add" support for detecting the IP of the client correctly is misleading.

    You can learn more about the topic here https://adam-p.ca/blog/2022/03/x-forwarded-for/

    It's also dangerous because the particular configuration that I found this used in was incorrectly taking a client controlled header as the "real" ip.

    IMO the most correct thing to do is to either split the handler into 10 or so for different proxy configurations or just delete it entirely because it's much easier for the user to look up what their proxy is doing and write the 5 lines of code needed to parse the end user's IP address.

  • OPTIONS in Allow for MethodHandler

    OPTIONS in Allow for MethodHandler

    MethodHandler supports OPTIONS, but it's not included in Allow.

    https://github.com/gorilla/handlers/blob/3e030244b4ba0480763356fc8ca0ade6222e2da0/handlers.go#L32

    allow := []string{http.MethodOptions}

    In case of 405, client should know OPTIONS is an option.

  • [question] upgrade httpsnoop

    [question] upgrade httpsnoop

    Describe the problem you're having

    I am using multiple dependencies, some using github.com/felixge/[email protected]. Would it be possible to upgrade httpsnoop to 1.0.3? As far as I can tell, there are no breaking changes. …

    Versions

    Go version: go version go1.19 linux/amd64

    package version: 3e030244b4ba0480763356fc8ca0ade6222e2da0

    …

    "Show me the code!"

    go work sync
    go: version constraints conflict:
    	github.com/honeycombio/[email protected] requires github.com/felixge/[email protected], but github.com/felixge/[email protected] is requested
    	github.com/letsencrypt/[email protected] requires github.com/felixge/[email protected], but github.com/felixge/[email protected] is requested
    	github.com/sigstore/[email protected] requires github.com/felixge/[email protected], but github.com/felixge/[email protected] is requested
    

    …

    "Possible solution"

    Upgrade httpsnoop using go get -u github.com/felixge/httpsnoop.

  • Incorrect regex in `forRegex`

    Incorrect regex in `forRegex`

    While auditing some internal code for a common mistake made in regex patterns, we discovered a vendored copy of this line:

    https://github.com/gorilla/handlers/blob/3e030244b4ba0480763356fc8ca0ade6222e2da0/proxy_headers.go#L25

    The line currently reads

    	forRegex = regexp.MustCompile(`(?i)(?:for=)([^(;|,| )]+)`)
    

    It was likely intended to read

    	forRegex = regexp.MustCompile(`(?i)(?:for=)([^;, ]+)`)
    

    however, even that is not correct according to rfc7239. If we wish to follow the RFC, then the forRegex should be defined as

    	forRegex = regexp.MustCompile(`(?i)(?:for=)(?:([-!#$%&'*+.^`|~\w]+)|"((?:[\t \x21-\x27\x2A-\x5B\x5D-\x7E\x80-\xFF]|\\[\t -~\x80-\xFF])+)")`)
    

    and in that case processing that uses forRegex will need to decide whether the first or second capture group matched and, in the case where the second capture group matched, do the appropriate replacement to remove backslashes.

  • [feature] Basic Auth handler

    [feature] Basic Auth handler

    How about to add a basic auth handler? This is still a good option for 90% of APIs. Unfortunately the golang doesn't provide it out of the box. This leads to problems because developers starts to write their own which may not be not secure and fast. Or devs may start to use some auth library which may be configured not properly and again become vulnerable. Even while there may be a separate libraries for this I think it may be good option to have it in the library so users will have it with less dependencies.

    It's questionable how feature rich it should be. I think it must be plain simple and focused on performance. Just as a starter but also because other more feature rich (and slow) libraries exists. E.g. no password hashing: for API credentials this not needed because you can simply reset credentials.

    Alternatives:

    • Here I wrote myself but I don't like it anymore for few minor reasons: https://github.com/stokito/go-http-server-basic-auth/blob/master/auth_handler.go
    • Most popular library https://github.com/abbot/go-http-auth
    • Some popular gist that many users just copy and paste https://gist.github.com/elithrar/9146306 It's mentioned in the first googled StackOverflow topic :) It's written by the @elithrar
    • Many others libs but none seems good to me https://pkg.go.dev/search?q=basic+auth
    • One of them looks easy to use https://github.com/99designs/basicauth-go/blob/2a93ba0f464d/basicauth.go
    • Prometheus also has basic auth handler with BCrypt support https://github.com/prometheus/exporter-toolkit/blob/master/web/handler.go#L105

    If you think that it may be useful to add the basic auth handler then I may rework my version to be more useful but still easy to use and send a PR.

A Go middleware that stores various information about your web application (response time, status code count, etc.)

Go stats handler stats is a net/http handler in golang reporting various metrics about your web application. This middleware has been developed and re

Dec 10, 2022
Go http.Hander based middleware stack with context sharing

wrap Package wrap creates a fast and flexible middleware stack for http.Handlers. Features small; core is only 13 LOC based on http.Handler interface;

Apr 5, 2022
Minimalist net/http middleware for golang

interpose Interpose is a minimalist net/http middleware framework for golang. It uses http.Handler as its core unit of functionality, minimizing compl

Sep 27, 2022
Lightweight Middleware for net/http

MuxChain MuxChain is a small package designed to complement net/http for specifying chains of handlers. With it, you can succinctly compose layers of

Dec 10, 2022
Idiomatic HTTP Middleware for Golang

Negroni Notice: This is the library formerly known as github.com/codegangsta/negroni -- Github will automatically redirect requests to this repository

Jan 2, 2023
A tiny http middleware for Golang with added handlers for common needs.

rye A simple library to support http services. Currently, rye provides a middleware handler which can be used to chain http handlers together while pr

Jan 4, 2023
Simple middleware to rate-limit HTTP requests.

Tollbooth This is a generic middleware to rate-limit HTTP requests. NOTE 1: This library is considered finished. NOTE 2: Major version changes are bac

Dec 28, 2022
OpenID Connect (OIDC) http middleware for Go

Go OpenID Connect (OIDC) HTTP Middleware Introduction This is a middleware for http to make it easy to use OpenID Connect. Currently Supported framewo

Jan 1, 2023
Go HTTP middleware to filter clients by IP

Go HTTP middleware to filter clients by IP

Oct 30, 2022
Chi ip banner is a chi middleware that bans some ips from your Chi http server.

Chi Ip Banner Chi ip banner is a chi middleware that bans some ips from your Chi http server. It reads a .txt file in your project's root, called bani

Jan 4, 2022
Painless middleware chaining for Go

Alice Alice provides a convenient way to chain your HTTP middleware functions and the app handler. In short, it transforms Middleware1(Middleware2(Mid

Dec 26, 2022
URL Rewrite middleware for gin

Url Rewrite middleware for gin Example In this exable these urls use the same route http://localhost:1234/test-me http://localhost:1234/index.php/test

Sep 15, 2022
A customized middleware of DAPR.

A customized middleware of DAPR.

Dec 24, 2021
Gin middleware for session.

wsession Gin middleware for session management with multi-backend support: cookie-based Redis memstore Usage Start using it Download and install it: g

Jan 9, 2022
Fiber middleware for server-timing

Server Timing This is a Fiber middleware for the [W3C Server-Timing API] based on mitchellh/go-server-timing

Feb 6, 2022
Go package for rate limiter collection

rlc A rate limiter collection for Go. Pick up one of the rate limiters to throttle requests and control quota. RLC Slider TokenBucket RLC RLC is a rat

Jul 6, 2021
echo-http - Echo http service

echo-http - Echo http service Responds with json-formatted echo of the incoming request and with a predefined message. Can be install directly (go get

Dec 4, 2022
Composable chains of nested http.Handler instances.

chain go get github.com/codemodus/chain Package chain aids the composition of nested http.Handler instances. Nesting functions is a simple concept. I

Sep 27, 2022
Add interceptors to GO http.Client

mediary Add interceptors to http.Client and you will be able to Dump request and/or response to a Log Alter your requests before they are sent or resp

Nov 17, 2022