Exploring Lesser-Known HTTP Handlers in Go

When working with Go’s net/http package, most of us are familiar with the basics: http.HandleFunc for routing, http.ServeMux for handling multiple routes, and http.FileServer for serving static files. But Go’s HTTP package has a few lesser-known handlers and functions that can be incredibly useful for more specific needs. Let’s dive into these often-overlooked features and see how they can make your life easier.

1. http.HandlerFunc

One of the simplest yet powerful tools is http.HandlerFunc. It allows you to create an HTTP handler from a regular function. If you have a small function that handles a request, you can use http.HandlerFunc to convert it into a handler.

Example:

http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello, World!")
})

In this example, any request to /hello will receive "Hello, World!" as a response.

2. http.ServeMux

When you need to handle multiple routes, http.ServeMux (or simply ServeMux) is your go-to. It matches the URL of incoming requests against a list of registered patterns and directs them to the appropriate handlers.

Example:

mux := http.NewServeMux()
mux.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Foo handler")
})
mux.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Bar handler")
})
http.ListenAndServe(":8080", mux)

Here, requests to /foo will be handled by one function, and requests to /bar by another.

3. http.NotFoundHandler

Sometimes, you want a specific handler for 404 errors (page not found). http.NotFoundHandler provides a simple way to handle these errors.

Example:

http.Handle("/404", http.NotFoundHandler())

This handler will return a 404 status code for any request to /404.

4. http.RedirectHandler

Need to redirect requests to a different URL? http.RedirectHandler is perfect for that. It helps set up URL redirections with the specified status code.

Example:

http.Handle("/redirect", http.RedirectHandler("https://example.com", http.StatusMovedPermanently))

Here, requests to /redirect will be permanently redirected to "https://example.com".

5. http.FileServer

Serving static files is a common requirement. http.FileServer makes it easy to serve files from a directory.

Example:

http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))

Requests to /static/ will be served with files from the ./static directory.

6. http.ServeFile

For serving a single file directly from a handler, use http.ServeFile. It’s straightforward and handy for specific file-serving needs.

Example:

func fileHandler(w http.ResponseWriter, r *http.Request) {
    http.ServeFile(w, r, "path/to/file")
}

This will serve the file located at "path/to/file" in response to any request handled by fileHandler.

7. http.ServeContent

If you need to serve content with specific headers, http.ServeContent is a versatile tool. It lets you serve content with additional headers like Content-Type or Content-Disposition.

Example:

func contentHandler(w http.ResponseWriter, r *http.Request) {
    content := []byte("This is some content")
    http.ServeContent(w, r, "filename.txt", time.Now(), bytes.NewReader(content))
}

This serves a piece of content with the filename "filename.txt" and current timestamp.

8. http.Handler

For more complex scenarios, you might implement the http.Handler interface. This interface requires a single method, ServeHTTP, which you can use to define custom request handling logic.

Example:

type myHandler struct{}

func (h *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Custom handler")
}

http.Handle("/custom", &myHandler{})

Requests to /custom will be handled by myHandler, which writes "Custom handler" to the response.

9. http.DefaultServeMux

The DefaultServeMux is a default multiplexer provided by Go. If you don’t provide a custom multiplexer, Go uses this one by default.

Example:

http.HandleFunc("/default", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Default handler")
})
http.ListenAndServe(":8080", nil) // Uses DefaultServeMux

This sets up a default handler for /default using Go’s built-in multiplexer.

10. http.TimeoutHandler

If you want to set a timeout for a handler, http.TimeoutHandler can wrap an existing handler and specify a timeout duration.

Example:

handler := http.TimeoutHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    time.Sleep(5 * time.Second) // Simulate a slow handler
    fmt.Fprintln(w, "Slow handler")
}), 2*time.Second, "Timeout")
http.Handle("/timeout", handler)

Here, if the handler takes more than 2 seconds, it will return a timeout error.

11. http.Pusher

For HTTP/2 applications, http.Pusher allows you to push resources to the client. This feature is not a handler itself but can be used to optimize loading times by preemptively sending resources.

Example:

func handler(w http.ResponseWriter, r *http.Request) {
    pusher, ok := w.(http.Pusher)
    if !ok {
        http.Error(w, "Push not supported", http.StatusInternalServerError)
        return
    }
    if err := pusher.Push("/styles.css", nil); err != nil {
        fmt.Printf("Failed to push: %v", err)
    }
    fmt.Fprintln(w, "Content with push")
}
http.HandleFunc("/", handler)

This example attempts to push a CSS file to the client along with the main content.

Wrapping Up

These lesser-known HTTP handlers in Go offer powerful ways to handle requests and responses. Whether you're managing static files, implementing custom request handling, or optimizing performance with HTTP/2 features, these tools can help you build more efficient and effective web applications. Exploring and using these features can add more flexibility and control to your Go web projects.