func hello(w http.ResponseWriter, req *http.Request) {
  fmt.Fprintf(w, "hello\\n")
}

func headers(w http.ResponseWriter, req *http.Request) {
  for name, headers := range req.Header {
    for _, h := range headers {
      fmt.Fprintf(w, "%v: %v\\n", name, h)
    }
  }
}

func main() {
  http.HandleFunc("/hello", hello)
  http.HandleFunc("/headers", headers)

  http.ListenAndServe(":8090", nil)
}

Untitled

The main flow: ListenAndServe listens on the TCP port, and then loops accepting new connections. For each new connection it spins a goroutine to serve it. Serving the connection involves a loop of:

A handler is anything that implements the http.Handler interface:

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

The default handler

type serverHandler struct {
  srv *Server
}

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
  handler := sh.srv.Handler
  if handler == nil {
    handler = DefaultServeMux
  }
  if req.RequestURI == "*" && req.Method == "OPTIONS" {
    handler = globalOptionsHandler{}
  }
  handler.ServeHTTP(rw, req)
}

if handler == nil, then http.DefaultServeMux is used as the handler. This is the default server mux - a global instance of the http.ServeMux type held in the http package.

Incidentally, when our sample code registers handlers with http.HandleFunc, it registers them on this very same default mux. We could rewrite our sample server as follows, without using the default mux. The default mux is somewhat of a security risk; as a global it can be modified by any package your project imports.

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/hello", hello)
  mux.HandleFunc("/headers", headers)

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

A ServeMux is just a Handler

what ListenAndServe takes is a value implementing the http.Handler interface. We could write the following server without any mux

type PoliteServer struct {
}
func (ms *PoliteServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  fmt.Fprintf(w, "Welcome! Thanks for visiting!\\n")
}
func main() {
  ps := &PoliteServer{}
  log.Fatal(http.ListenAndServe(":8090", ps))
}

We can simplify our polite server even more by using http.HandlerFunc:

func politeGreeting(w http.ResponseWriter, req *http.Request) {
  fmt.Fprintf(w, "Welcome! Thanks for visiting!\\n")
}
func main() {
  log.Fatal(http.ListenAndServe(":8090", http.HandlerFunc(politeGreeting)))
}

Just like PolitServer, http.ServeMux is a type implementing the http.Handler interface. Here’s an outline: