Skip to content

Instantly share code, notes, and snippets.

@amitsaha
Last active July 16, 2021 23:13
Show Gist options
  • Save amitsaha/ec7ce96e47bbfca0e7a52d5bff53485b to your computer and use it in GitHub Desktop.
Save amitsaha/ec7ce96e47bbfca0e7a52d5bff53485b to your computer and use it in GitHub Desktop.
Go's net/http Server Behavior When a handler panics

Consider a test HTTP server in Go:

package main

import "net/http"

func myHandler1(w http.ResponseWriter, r *http.Request) {
	panic("I panicked")
}

func myHandler2(w http.ResponseWriter, r *http.Request) {
	panic(http.ErrAbortHandler)
}

func main() {

	mux := http.NewServeMux()
	mux.HandleFunc("/1", myHandler1)
	mux.HandleFunc("/2", myHandler2)

	http.ListenAndServe(":8080", mux)
}

When i make a request to the /1 endpoint, on the server i see this:

2021/07/17 09:04:07 http: panic serving [::1]:52336: I panicked
goroutine 4 [running]:
net/http.(*conn).serve.func1(0x1400010aa00)
	/usr/local/go/src/net/http/server.go:1824 +0x108
panic(0x102672ca0, 0x1026c3590)
	/usr/local/go/src/runtime/panic.go:971 +0x3f4
main.myHandler1(0x1026cbb30, 0x140001420e0, 0x1400014e000)
	/Users/echorand/work/github.com/amitsaha/tmp/server.go:6 +0x38
net/http.HandlerFunc.ServeHTTP(0x1026c2790, 0x1026cbb30, 0x140001420e0, 0x1400014e000)
	/usr/local/go/src/net/http/server.go:2069 +0x40
net/http.(*ServeMux).ServeHTTP(0x14000024200, 0x1026cbb30, 0x140001420e0, 0x1400014e000)
	/usr/local/go/src/net/http/server.go:2448 +0x190
net/http.serverHandler.ServeHTTP(0x14000142000, 0x1026cbb30, 0x140001420e0, 0x1400014e000)
	/usr/local/go/src/net/http/server.go:2887 +0xbc
net/http.(*conn).serve(0x1400010aa00, 0x1026cc000, 0x14000024280)
	/usr/local/go/src/net/http/server.go:1952 +0x710
created by net/http.(*Server).Serve
	/usr/local/go/src/net/http/server.go:3013 +0x308

The recovery is happening here in the stdlib's code:

// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
	c.remoteAddr = c.rwc.RemoteAddr().String()
	ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
	defer func() {
		if err := recover(); err != nil && err != ErrAbortHandler {
			const size = 64 << 10
			buf := make([]byte, size)
			buf = buf[:runtime.Stack(buf, false)]
			c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
		}
		if !c.hijacked() {
			c.close()
			c.setState(c.rwc, StateClosed, runHooks)
		}
	}()

However, when I make a request to the /2 endpoint, there is no log of the panic as explained by the documentation of ErrAbortHandler.

Due to the recovery mechanism setup, the server process doesn't get aborted and the panic details are logged.

You can also write a middleware to setup your own recovery mechanism so that the stdlib's recovery mechanism is not triggered at all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment