In this article, the second in a series of source code analyses for GIN, we focus on one question: how does a request received through a NET/HTTP socket get back to GIN for processing logic?

We’ll start with the NET/HTTP example again

func main(a) {
    http.HandleFunc("/".func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello World"))})if err := http.ListenAndServe(": 8000".nil); err ! =nil {
        fmt.Println("start http server fail:", err)
    }
}
Copy the code

In this example, if you look at the source code, you can see that the URI “/” is registered with DefaultServeMux.

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

Net/HTTP ServeHTTP functions

There is a very important Handler interface in NET/HTTP. Only when this method is implemented can the requested processing logic be introduced into its own processing flow.

// https://github.com/golang/go/blob/master/src/net/http/server.go#L86-L88
type Handler interface {
	  ServeHTTP(ResponseWriter, *Request)
}
Copy the code

The default DefaultServeMux implements this ServeHTTP

The request flow is as follows:

  1. Socket. accept After receiving the client request, start the go c. server (connCtx) [net/ HTTP server.go:L3013] line to process the request. The server continues to wait for the client to connect
  2. ServerHandler {c.server}.servehttp (w, w.eq) [net/ HTTP server.go:L1952]
  3. Jump to the real ServeHTTP to match the route and get the handler
  4. Net/HTTP default route is used because there is no custom route [net/ HTTP server.go: l2880-2887]
  5. So the final call to de-defaultServemux matches the route, and the output returns the corresponding result

Explore the implementation of GIN Server HTTP

Here is an official demo of GIN, starting an Echo Server in just a few lines of code.

package main

import "github.com/gin-gonic/gin"

func main(a) {
    r := gin.Default()
    r.GET("/ping".func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}
Copy the code

The general flow of this code:

  1. R := gin.Default() initializes the relevant parameters
  2. Registers the route /ping and the corresponding handler in the routing tree
  3. Start the server using r.run ()

The bottom layer of R.run is still HTTP.ListenAndServe

func (engine *Engine) Run(addr ...string) (err error) {
    defer func(a) { debugPrintError(err) }()

    trustedCIDRs, err := engine.prepareTrustedCIDRs()
    iferr ! =nil {
        return err
    }
    engine.trustedCIDRs = trustedCIDRs
    address := resolveAddress(addr)
    debugPrint("Listening and serving HTTP on %s\n", address)
    err = http.ListenAndServe(address, engine)
    return
}
Copy the code

Therefore, gin’s process of establishing sockets and accepting client requests is no different from net/ HTTP’s, and will repeat the above process. The only difference is where to fetch the ServeHTTP

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)
}
Copy the code

Since sh.srv.Handler is of interface type, but its real type is gin.Engine, interace’s dynamic forwarding features eventually redirect it to the gin.Engine.

Gin. ServeHTTP implementation

func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    c := engine.pool.Get().(*Context)
    c.writermem.reset(w)
    c.Request = req
    c.reset()

    engine.handleHTTPRequest(c)

    engine.pool.Put(c)
}
Copy the code

At this point, we finally see the full picture of gin.ServeHTTP

  1. Take a chunk of memory from sync.pool
  2. Initialize this block of memory to prevent data contamination
  3. Handle the request handleHTTPRequest
  4. After the request is processed, the memory is returned to sync.pool

This implementation now looks simple, but it is not. This is the first step in gin’s ability to process data, and only flows requests into GIN’s processing flow.

Here’s a conclusion: Net /http.ServeHTTP is a very important function, which can flow requests into the current Go framework. If you are interested, See how echo, Iris, Go-Zero, and other frameworks implement server HTTP.

For information on how GIN matches routes, see a subsequent article about getting a handler. If you think the article is good, welcome to like + read + forward.