Fun fact: The HTTP server implementation in Go is under 4000 lines of code (including comments).
If you are a Go developer in 2019, you are in luck as you no longer need to read tons of cryptic documentation. Instead, you can focus on
net/http package in the standard library and understand (almost) everything about HTTP at the server side.
An impressive feature of Go is the inbuilt HTTP server. It is production grade, asynchronous and packed with everything you need to spin up an HTTP server. This is a huge contrast in comparison to many other programming languages, where the server specs and implementation are external to the language.
net/http package would be too much to cover in a single blog post. In this blog post, we will focus on understanding the mechanics of handling incoming HTTP requests.
Where to start⌗
In my opinion, myriads of functionalities for the HTTP request handling in Go can be summarized by the
http.Handler interface. The construct of the Handler interface is pretty simple.
The core responsibility of the Handler is to write response headers and body. In order to process an incoming request, all you need is a type that implements this interface. The standard library provides some default types that implement the Handler interface and can be used out of the box. For example:
- RedirectHandler for handling HTTP redirects (3XX).
- FileServer to serve static file content.
- TimeoutHandler to run timebound requests.
- NotFoundHandler for replying to each request with 404. (Useful during maintenance)
What is ServeMux⌗
It is a router, that holds the mapping of URL path + HTTP method to the corresponding handler. It simply routes the incoming request to the correct handler. The standard library defines it as:
ServeMux is an HTTP request multiplexer. It matches the URL of each incoming request against a list of registered patterns and calls the handler for the pattern that most closely matches the URL.
It has a simple construct
As evident from line 3, ServeMux holds a map of URL patterns and corresponding request handlers. This provides an easy way to collect all your requests and handlers at one place. The corresponding handler will be executed based on the incoming request pattern.
Now there are two choices that you should make.
- Do you want to implement Handler for each request? This could be a viable option when you really want to take control of how you want to handle the incoming request. Just remember that you have to implement ServeHTTP for each handler that you register. If you decide to do this, you should implement your handler and register it using ServeMux.Handler.
- Do you have a simple case which can be served using HandlerFunc?
This typically would cover 99% of use cases. All you need to do is to create a function with signature
(http.ResponseWriter, *http.Request)containing business logic and register it using ServeMux.HandleFunc
In the next few sections, let put what we have learned so far into practice.
Example : ServeMux.HandleFunc⌗
What are http.Handle() and http.HandleFunc() ?⌗
A lot of examples on internet use http.Handle and http.HandleFunc. How does that fit into the whole equation with
http.Handler interface? For example you would see examples like below
Note that in this case:
- We do not create a ServeMux. We directly use
- We don’t pass any mux to
http.ListenAndServe, instead we use nil
The reason is pretty simple,
net/http provides a default implementation for ServeMux as DefaultServeMux. The
http.HandleFunc, internally use the DefaultServeMux to provide routing capabilities.
My suggestion would be to always use a ServeMux if possible. That way you have more control on your routes and you are not dependent on the default mux provided by the standard library.
The standard library provides all you need to set up an HTTP Server. However, at times it could be too verbose to use inbuilt ServeMux. If you ever come across such a situation, you should have a look into the available frameworks within the community. Some of the popular router frameworks are:
Go provides a lot of powerful tools as part of the standard library that is easy to reason with. In this blog, we barely scratched the surface of what it has to offer. Watch this space for more content on Golang.
Note : All the code in this repository can be found here