📖 Net/ HTTP underlying implementation principles.

Understand the ability to use NET/HTTP external simple extension, a deeper understanding of later analysis.

📜 a simple use of the NET/HTTP library.

func main(a) {
	
	http.HandleFunc("/test".func(response http.ResponseWriter, request *http.Request) {
		/ / request.
		str := "Hello Word"
		fmt.Println(str)
		response.Write([]byte(str))
	})
	// If no handler is passed, the default handler will be used, while the top is directly handled by the package name such as HandleFunc and the bottom is injected into DefaultServeMux.
	http.ListenAndServe(": 8080".nil)}Copy the code

The result: You can see how much easier it is to start a Web application in Java, but that’s not the point of this article.

📜 2. Analysis of the underlying implementation principle.

We’ll start with the HTTP.HandlerFunc injection Handler.

The net result is that the Handler that it injected will be injected into the structure below.

type ServeMux struct {
	mu    sync.RWMutex
	m     map[string]muxEntry  // Entry Stores the Handler for a specific request. It consists of a request path and a specific Handler.
	es    []muxEntry // slice of entries sorted from longest to shortest.
	hosts bool       // whether any patterns contain hostnames
}
Copy the code

Although it is said that it will eventually be stored in the modified structure, but we need to know how it is actually stored? And how it gets called once it’s saved, so keep looking.

When we click on the code we have written, we see that it converts our own Handler to HandlerFunc, which implements the NET\ HTTP Handler interface and rewrites the ServerHTTP method to call our own Handler directly inside that method.

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	if handler == nil {
		panic("http: nil handler")}// Convert the handler represented by func to HandlerFunc in NET/HTTP and call the handler in its interface ServerHTTP.
	mux.Handle(pattern, HandlerFunc(handler))
}

// HandlerFunc definition.
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}
Copy the code

The next step is to look at how ServerMux saves handlers.

You can see that the Handler is registered with M and ES in the ServeMux.

// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
	mux.mu.Lock()
	defer mux.mu.Unlock()

	if mux.m == nil {
		mux.m = make(map[string]muxEntry)
	}
	e := muxEntry{h: handler, pattern: pattern}
	mux.m[pattern] = e
	if pattern[len(pattern)- 1] = ='/' {
		mux.es = appendSorted(mux.es, e)
	}

	if pattern[0] != '/' {
		mux.hosts = true}}Copy the code

At this point, we’ll see that all of our handlers will eventually be converted to handlers in NET/HTTP and stored in a Map in ServerMux.

** Now let’s take a look at how the request is distributed: ** It ends up in the following code: I’m going to delete some basic code here. Comments are also important.

func (srv *Server) Serve(l net.Listener) error {

	defer srv.trackListener(&l, false)

	var tempDelay time.Duration // how long to sleep on accept failure
	
	ctx := context.WithValue(baseCtx, ServerContextKey, srv)
	for {
        // Important: accept requests through the for loop.
		rw, err := l.Accept()
		connCtx := ctx
		tempDelay = 0
		// Construct a new CONN and turn on goroutine to execute the request.
		c := srv.newConn(rw)
		c.setState(c.rwc, StateNew, runHooks) // before Serve can return
		go c.serve(connCtx)
	}
}
Copy the code

It will eventually be executed

// TODO is an important method.
serverHandler{c.server}.ServeHTTP(w, w.req)

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
	handler := sh.srv.Handler
	// If the Handler in the current Server is empty, the default HTTP library Handler is used.
	// We can replace this part with our own implementation.
	if handler == nil {
        // Use the default.
        // We can construct this ourselves. And then when the code gets executed here it's going to be implemented according to our own intentions.
		handler = DefaultServeMux
	}
    
	if req.RequestURI == "*" && req.Method == "OPTIONS" {
		handler = globalOptionsHandler{}
	}

	// handler--->ServerMux.
  	// Let's look at the implementation of this method.
	handler.ServeHTTP(rw, req)
}

// This is to follow the request and then go to ServeMux to find the Handler for the current request.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
	if r.RequestURI == "*" {
		if r.ProtoAtLeast(1.1) {
			w.Header().Set("Connection"."close")
		}
		w.WriteHeader(StatusBadRequest)
		return
	}
	h, _ := mux.Handler(r)
	h.ServeHTTP(w, r)
}
Copy the code

The following code goes from the ServeMux to the Handler belonging to the current Request based on the Request.

If we want to develop our own framework, we can reuse the net/ HTTP request parsing process, directly use the process after the request parsing, just like reuse Java Servlet, I implemented my own HTTP request parsing, finally only achieve Get and POST requests, but not images. Failed to parse the image. So if I wanted to implement the Web framework myself, I would reuse the NET/HTTP underlying library.

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {

	// CONNECT requests are not canonicalized.
	if r.Method == "CONNECT" {
		// If r.URL.Path is /tree and its handler is not registered,
		// the /tree -> /tree/ redirect applies to CONNECT requests
		// but the path canonicalization does not.
		if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
			return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
		}

		return mux.handler(r.Host, r.URL.Path)
	}

	// All other requests have any port stripped and path cleaned
	// before passing to mux.handler.
	host := stripHostPort(r.Host)
	path := cleanPath(r.URL.Path)

	// If the given path is /tree and its handler is not registered,
	// redirect for /tree/.
	if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
		return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
	}

	ifpath ! = r.URL.Path { _, pattern = mux.handler(host, path) u := &url.URL{Path: path, RawQuery: r.URL.RawQuery}return RedirectHandler(u.String(), StatusMovedPermanently), pattern
	}

    // Retrieve the corresponding handler directly from the map.
	return mux.handler(host, r.URL.Path)
}
Copy the code

📜 3. Summary.

Net/HTTP is basically registering our own HandleFunc with the DefaultServeMux, DefaultServeMux, listening for ports to build a Server, ServerHandler {c. Server}.ServeHTTP(w, w.eq) is called directly after the request is resolved. If the Server Handler is Nil, So we’re going to use the default DefaultServerMux as the Handler for the current Server, and then ServerHTTP, and if we implement our own framework, we can implement a Handler of our own, And when we create the Server, we do this by assigning a Handler to the Server. Welcome to the public account: Pony is writing a Bug.