Understanding Go Standard Http Libraries : ServeMux, Handler, Handle and HandleFunc

Go language has great built in standard library to deal with http, although they are pretty basic, we can build complete web application with it.

The libraries reside at net/http package where we can find functions, interfaces, types and built in structs from Go that we can use for building web application.

Let’s jump to see minimal code to build simplest web application:

package main

import (
 "fmt"
 "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
 fmt.Fprint(w, "Welcome to Golang Web App.")
}

func main() {
 http.HandleFunc("/", handler)
 http.ListenAndServe(":8080", nil)
}

From the code above we can see http.HandleFunc, http.ListenAndServe as functions that we call to implement basic “hello world” web application. But in the background there is ServeMux as main component to map the url to handler.

I will explain one by one the components that I think are important to understand for building web application.

When we call function http.ListenAndServe, we tell the server to listen into specified port as first parameter, then the second parameter, we should pass the Handler, but because we use DefaultServeMux (read my explanation in this article) so we can pass “nil” to it.

First, what is ServeMux ?

ServeMux is special Go struct that act as HTTP request multiplexer (kind of router) that map request to handler base on the URL pattern. On sample code above ServeMux definition doesn’t appear but there when we call HandleFunc, it actually call function of ServeMux implementation.

Inside http package there is default ServeMux implementation that is stored as variable DefaultServeMux. Using this DefaultServeMux we can skip to define our own ServeMux implementation and directly utilize it by calling function Handle or HandleFunc inside http package.

Then what is Handler?

Handler is interface that define ServeHTTP function that will be called by ServeMux to process http request and send http response.

Here is Handler source code:

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

Source: https://golang.org/src/net/http/server.go

About HandleFunc function

In sample code above, we don’t define ServeMux and there is no any explicit Handler implementation in the code (no ServeHTTP  implementation), so how can the ServeMux accept handler func in the sample code? Because It doesn’t define ServeHTTP function?

Let’s see the code of HandleFunc from the source :

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
     DefaultServeMux.HandleFunc(pattern, handler)
}

Inside this function ,  it call  DefaultServeMux.HandleFunc that accept pattern string and the function.

But we  still not see the ServeHTTP implementation. Now let go deeper by checking HandleFunc code from the source:

func (mux *ServeMux) HandleFunc(pattern string, handler     func(ResponseWriter, *Request)) {
    mux.Handle(pattern, HandlerFunc(handler))
}

From the code above, we can see that the ServeMux pass the handler (the function) to HandlerFunc(handler). But we still don’t see ServeHTTP implementation, so let’s see the HandlerFunc source code:

type HandlerFunc func(ResponseWriter, *Request)

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
      f(w, r)
}

Finally we find the implementor of ServeHTTP, which is HandlerFunc type that define ServeHTTP, so officially it implements Handler interface and can be passed to ServeMux.

Inside HandlerFunc ServeHTTP function, it call itself and pass the response writer and request parameter that it get from ServeHTTP call. So ServeMux will call this ServeHttp to process the request and deliver the response.

About Handle function

To understand how to use Handle function in http package, let see sample code below:

package main

import (
 "fmt"
 "net/http"
)

type CounterHandler struct {
 counter int
}

func (ct *CounterHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 fmt.Println(ct.counter)
 ct.counter++
 fmt.Fprint(w, "Counter:", ct.counter)
}

func main() {
 th := &CounterHandler{counter: 0}
 http.Handle("/", th)
 http.ListenAndServe(":8080", nil)
}

I don’t use HandleFunc but use Handle in the code above, but what is the difference between Handle and HandleFunc?

Function Handle accepts Handler type as parameter, differ with HandleFunc which accepts “function” with 2 arguments which are ResponseWriter and http.Request.

So we need to define Handler Interface implementation to be passed into Handle function, which in the code above I define CounterHandler struct and also implementation of ServeHTTP from the struct.

Then I create object instance of CounterHandler struct and initialize the counter with zero value.

How to use http.Handle without struct

We also can use http.Handle without define struct first by passing our function to http.HandlerFunc like below:

package main

import (
 "fmt"
 "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
 fmt.Fprint(w, "Welcome to Go lang Web App.")
}

func main() {
 http.Handle("/", http.HandlerFunc(handler))
 http.ListenAndServe(":8080", nil)
}

Maybe code above is not useful, because we can just use http.HandleFunc for simpler code and pass the function directly to http.HandleFunc. But in order to clatify more, I use code above as example so it can explains more about difference of Handle and HandleFunc.

Leave a comment

Your email address will not be published. Required fields are marked *