This is the 31st day of my participation in the August More Text Challenge

Go’s HTTP has two core functions: Conn and ServeMux

Go provides a series of standard libraries for creating Web servers, and the steps to create a server through Go are simple, You simply call the ListenAndServe function using the NET/HTTP package and pass in the network address and the handler that handles the request as parameters.

If the network address parameter is an empty string, the server uses port 80 for network connection by default. If the processor argument is nil, then the server will use the default multiplexer DefaultServeMux,

The goroutine Conn

Unlike the typical HTTP server we write, Go uses Goroutines to handle Conn’s read and write events for high concurrency and performance, so that each request remains independent and does not block each other, and can efficiently respond to network events. This is the guarantee of Go’s efficiency.

Go is waiting for a client request.

c, err := srv.newConn(rw) if err ! = nil { continue } go c.serve()Copy the code

In this case, the client creates a Conn for each request. The Conn is then passed to the corresponding handler, which reads the corresponding header information, ensuring that each request is independent.

Customization of ServeMux

When we talked about Conn. server in the previous section, we actually called the HTTP packet default router internally and passed the request information to the backend handler. So how does this router work?

Its structure is as follows:

Type ServeMux struct {mu sync.RWMutex // lock, m map[string]muxEntry // Hosts bool indicates whether any rules contain host information}Copy the code

Now take a look at muxEntry

Type muxEntry struct {explicit bool explicit string {explicit string {explicit string {explicit string {explicit string {explicit string {explicit string {explicit string {explicit string {explicit string}Copy the code

Now let’s look at the definition of Handler

Type Handler interface {ResponseWriter (*Request)}Copy the code

Handler is an interface, but the sayhelloName function from the previous section does not implement ServeHTTP. The sayhelloName function is the result of a call to HandlerFunc. By default, this type implements the ServeHTTP interface. That is, we call HandlerFunc(f) and cast F to the HandlerFunc type, so that F has a ServeHTTP method.

type HandlerFunc func(ResponseWriter, *Request)

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

After the routing rules are stored in the router, how are the specific requests distributed? See the following code, the default router implements ServeHTTP:

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
	if r.RequestURI == "*" {
		w.Header().Set("Connection", "close")
		w.WriteHeader(StatusBadRequest)
		return
	}
	h, _ := mux.Handler(r)
	h.ServeHTTP(w, r)
}
Copy the code

As shown above, the router receives the request and closes the link if it is *, otherwise it calls mux.handler (r) to return the Handler that set the route, and then executes h.sever HTTP(w, r).

Mux.handler (r) is the ServerHTTP interface that calls the handler of the route.

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { if r.Method ! = "CONNECT" { if p := cleanPath(r.URL.Path); p ! = r.URL.Path { _, pattern = mux.handler(r.Host, p) return RedirectHandler(p, StatusMovedPermanently), pattern } } return mux.handler(r.Host, r.URL.Path) } func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) { mux.mu.RLock() defer mux.mu.RUnlock() // Host-specific pattern takes precedence over generic ones if mux.hosts { h, pattern = mux.match(host + path) } if h == nil { h, pattern = mux.match(path) } if h == nil { h, pattern = NotFoundHandler(), "" } return }Copy the code

ListenAndServe’s second parameter is used to configure the external router. It is a Handler interface. As long as the external router implements the Handler interface, we can implement the custom routing function in the ServeHTTP of our own router.

As shown in the code below, we implemented a simple router ourselves

package main import ( "fmt" "net/http" ) type MyMux struct { } func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { sayhelloName(w, r) return } http.NotFound(w, r) return } func sayhelloName(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello myroute!" ) } func main() { mux := &MyMux{} http.ListenAndServe(":9090", mux) }Copy the code

Go provides a series of standard libraries for creating Web servers, and the steps to create a server through Go are simple, You simply call the ListenAndServe function using the NET/HTTP package and pass in the network address and the handler that handles the request as parameters.

If the network address parameter is an empty string, the server uses port 80 for network connection by default. If the processor argument is nil, then the server will use the default multiplexer DefaultServeMux. Of course, we can also create a multiplexer by calling the NewServeMux function. After receiving the request, the multiplexer determines which processor to use to process the request according to the URL of the request, and then redirects to the corresponding processor to process the request

Using the default multiplexer (DefaultServeMux)

package main import ( "fmt" "net/http" ) type DefineServerMux struct{} func (dsm *DefineServerMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Create custom multiplexer defineServerMux")} func ServeHTTP(w http.responsewriter, r * http.request) {FMT.Fprintln(w, "HandleFunc wraps a normal function ServeHTTP as Handle")} func main() {defineServerMux := defineServerMux {} Http.handle ("/defineServerMux", &defineservermux) http.handlefunc ("/",ServeHTTP) // The second argument requires that a handle be passed, but why handle is set to nil HTTP.ListenAndServe(":8080", nil)}Copy the code

Use your own created multiplexer

When creating a server, we can also create a multiplexer through the NewServeMux method

func NewServeMux() *ServeMux
Copy the code

NewServeMux creates and returns a new *ServeMux

package main import ( "fmt" "net/http" ) type DefineServerMux struct{} func (dsm *DefineServerMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Create custom multiplexer defineServerMux")} func main() {defineServerMux := defineServerMux {} http.Handle("/defineServerMux", &defineServerMux) http.ListenAndServe(":8080", nil) }Copy the code

Structure ServeMux

Type ServeMux struct {// include hidden or unexported fields}Copy the code

The ServeMux type is a multiplexer for HTTP requests. It matches the URL of each received request with a list of registered patterns and invokes the handler of the pattern that best matches the URL.

Methods related to the structure ServeMux

func (*ServeMux) 

func (mux *ServeMux) Handle(pattern string, handler Handler)
Copy the code

Handle registers the HTTP handler and its corresponding pattern. If the mode has already registered a handler, Handle panic occurs.

func (*ServeMux)

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))
Copy the code

HandleFunc registers a handler function and its corresponding pattern.

func (*ServeMux) 

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string)
Copy the code

Handler returns the HTTP Handler that will be used to process the request based on data such as R.thod, R.host, and R.ul. Path. It always returns a non-nil handler. If the path is not in its canonical form, the built-in handler for redirecting to the equivalent canonical path is returned.

The Handler also returns the registered schema that matches the request; In the case of the built-in redirection processor, pattern is matched after redirection. If no registered pattern can be applied to the request, this method returns a built-in “404 Page Not Found “handler and an empty string pattern.

func (*ServeMux) 

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request)
Copy the code

ServeHTTP dispatches the request to the handler corresponding to the pattern that best matches the requested URL.

The request is processed using a customized handler of a type that implements ServerHTTP

package main import ( "fmt" "net/http" ) type MyHandlers struct{} func (m *MyHandlers) ServeHTTP(w http.ResponseWriter, R * http.request) {FMT.Fprintln(w, "Handle the Request through your own processor!" ) } func main() { myHandler := MyHandlers{} http.Handle("/myHandler", &myHandler) http.ListenAndServe(":8080", nil) }Copy the code

The Server structure is used to configure the Server in more detail

The Server architecture also defines methods for implementing HTTP

package main import ( "fmt" "net/http" "strings" "time" ) type MyDefinitionMux struct{} func (m *MyDefinitionMux)sayhelloName(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello afei!" } func (h *MyDefinitionMux) ServeHTTP(w http.responsewriter, r * http.request) {ftm.fprintln (w, "Testing Server details with Server structure ") // h.Sayhelloname (w,r)} func main() {myDefinitionMux := myDefinitionMux {} // For Server in net/HTTP package // Define handle Handler: &myDefinitionmux, ReadTimeout: // Define Handle Handler: &myDefinitionmux, ReadTimeout: 2 * time.Second, } server.ListenAndServe() }Copy the code