This is a creation in Article, where the information may have evolved or changed.
This article is the junior to the Net/http package of a simple understanding, the text if there are errors in the place please predecessors pointed out, so as not to mislead!
Transfer This article also please specify the source: Go Language Memo (3): Net/http package usage mode and source code analysis, thank you!
Directory:
- One, 3 key types of HTTP packets
- Second, the use of HTTP server mode
- Third, the HTTP server execution process
- Four, redirect
- V. Implementation of the Client
One, 3 key types of HTTP packets:
Handler interface:All the requested processors and routing Servemux are satisfied with the interface;
Type Handler Interface { servehttp (Responsewriter, *request)}
Servemux Structural Body:The multiplexer (route) of the HTTP request, which is responsible for matching each received request URL with a list of registered patterns, and invoking the processor with the most matching pattern of the URL. It internally uses a map to store all processors handler
- The HTTP packet has a package-level variable Defaultservemux, which represents the default route: var Defaultservemux = Newservemux (), using packet-level HTTP. Handle (), HTTP. The Handlefunc () method registers the processor when it is registered to the route;
- The Servemux struct has a servehttp () method (which satisfies the handler interface) and is primarily used to indirectly invoke the Servehttp () method of the processor it holds.
http. Handlerfunc function Type:It satisfies the handler interface
Type Handlerfunc func (responsewriter, *request)//Servehttp method for implementing the Handler interface Func (f Handlerfunc) Servehttp (w Responsewriter, R *request) { f (W, R)//call itself}
second, the use of HTTP Server mode:
handler function:As long as the function's signature is func (w http. Responsewriter, R *http. Request), can be used as a handler function, that is, it can be converted to HTTP. Handlerfunc function type;
mode One: Use the default route registration handler function:
var addr = flag. String ("addr", ": 8080", "HTTP server address")//1. No parameter handler func servehome (w http. Responsewriter, R *http. Request) { if r.url. Path! = "/" { http. NotFound (W, R) return } http. Servefile (W, R, "home.html")}//2. With the parameter handler function, the closure function is implicitly converted to HTTP. Handlerfunc function type func MyHandler (S string) http. Handlerfunc { return func (w http. Responsewriter, R *http. Request) { if r.url. Path! = "/" { http. NotFound (W, R) return } http. Servefile (W, R, s)//use parameter S }}func main () { flag. Parse () //Register the processor function HTTP to the default route . Handlefunc ("/", servehome)//or HTTP. Handle ("/", HTTP. Handlerfunc (servehome)) http. Handle ("/file", MyHandler ("Somefile")) err: = http. Listenandserve (*addr, nil)//start listening, the second parameter nil represents the processor that is registered with the default route Defaultservemux if err! = Nil { log. Fatalln ("Listenandserve:", Err) }}
pattern Two: Use a custom route registration handler function:
Func Main () { MUX: = http. Newservemux ()//Create a new custom route Mux. Handle ("/file", MyHandler ("Somefile")) Mux. Handlefunc ("/", Servehome) err: = http. Listenandserve (*ADDR,MUX)//Start monitoring if err! = Nil { log. Fatalln ("Listenandserve:", Err) }}
mode Three: Customize a server instance directly:This mode makes it easy to manage the behavior of the service side
MUX: = http. Newservemux () Mux. Handle ("/file", MyHandler ("Somefile")) Mux. Handlefunc ("/", Servehome) s: = &http. server{ Addr: ": 8080", handler:mux,//Specify a route or processor, nil when not specified, means use the default route Defaultservemux readtimeout:10 * Time. Second, writetimeout:10 * time. Second, maxheaderbytes:1 <<, connstate://Specify the handler function when the status of the connection conn is changed// .... log. Fatal (S.listenandserve ())
Next, we will follow the source code to carefully analyze the entire implementation process.
third, the HTTP server execution process:
1. Use HTTP. The Listenandserve () method starts the service, which constructs the server type based on the given parameters and then calls server. Listenandserve ()
Func listenandserve (addr string, handler handler) error { server: = &server{addr:addr, handler:handler} ret Urn server. Listenandserve ()}
2. and server. The Listenandserve () method internally calls net. Listen ("tcp", addr), this method internally calls net. Listentcp () Creates and returns a listener net. Listener, as in LN;
Func (SRV *server) listenandserve () error { addr: = srv. Addr If Addr = = "" { Addr = ": http" } ln, err: = Net. Listen ("tcp", addr) if err! = Nil { return err } return srv. Serve (Tcpkeepalivelistener{ln. ( *net. TcpListener)})}
3. Then converts the listener ln assertion to the TcpListener type and constructs a Tcpkeepalivelistener object from it and passes it to the server. Serve () method;
- Because TcpListener implements the listener interface, Tcpkeepalivelistener also implements the listener interface, and it overrides the Accept () method in order to invoke Setkeepalive (true). Let the operating system start sending keepalive messages (heartbeat, in order to keep the connection constantly open) for each connection received.
Type Tcpkeepalivelistener struct { *net. Tcplistener}func (ln tcpkeepalivelistener) Accept () (c net. Conn, err Error) { TC, err: = ln. ACCEPTTCP () if err! = Nil { return } TC. Setkeepalive (TRUE)//Send Heartbeat TC. Setkeepaliveperiod (3 * time. Minute)//Send cycle return TC, nil}
4.server. The Serve () method calls the Accept () method of the Tcpkeepalivelistener object to return a connection conn (the connection initiates a heartbeat) and creates a new go execution conn.server for each conn () Method: See the code in the comments I add to the description
Func (SRV *server) Serve (l net. Listener) Error {defer l.close () if fn: = Testhookserverserve; fn! = nil {fn (SRV, L)} var Tempdelay Time. Duration//Retry interval If err: = Srv.setuphttp2_serve (); Err! = Nil {return err} srv.tracklistener (L, True)//cache the Listener defer Srv.tracklistener (L, false)//delete current from cache Listener Basectx: = context. Background () CTX: = Context. Withvalue (Basectx, Servercontextkey, SRV)//Create a new context to manage each connection conn go for {rw, E: = L.accept ()//Call Tcpkeepali The Velistener object's Accept () method if E! = Nil {Select {case <-srv.getdonechan (): return errserverclosed//exit the serve method and execute a deferred call (remove the current listener from the cache) default:}//If a net has occurred. Error errors, the interval time will be retried once, each time intervals doubled, the maximum is 1 seconds if ne, ok: = E. (NET. ERROR); Ok && ne. Temporary () {if Tempdelay = = 0 {tempdelay = 5 * time. Millisecond} else {Tempdelay *= 2 If Max: = 1 * time. Second; Tempdelay > Max {tempdelay = max} srv.logf ("Http:accept error:%v; Retrying in%v ", E, Tempdelay) time. Sleep (Tempdelay) Continue} return e} tempdelay = 0 c: = srv.ne Wconn (rw)//This method is based on net. Conn, SRV constructs a new http.conn type C.setstate (C.RWC, statenew)//caches the state of the connection if method: Server.connstate (NET. Conn, connstate) is not nil, executes it according to the state of the current connection go C.serve (CTX)}}
5. While Conn.server ( ) method reads the request, and then constructs a Serverhandler type based on the server saved in Conn, and calls its Servehttp () method: Serverhandler{c.server}. Servehttp (W, w.req), the source code of the method is as follows:
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)}
6. As the source can see, when handler = = nil when using the default Defaultservemux route, otherwise use in the 1th step for serve specified handler; Then call the handler Servehttp method (The handler is generally set to the routing Servemux type);
7. The Servehttp method for routing Servemux will find the most matching handler (here) based on the information provided by the current request:
Func (Mux *servemux) servehttp (w responsewriter, R *request) { if R.requesturi = = "*" { if R.protoatleast (1, 1) { C2/>w.header (). Set ("Connection", "Close") } W.writeheader (statusbadrequest) return } h, _: = Mux. Handler (R)//normalize the path format of the request, find the most matching Handler h.servehttp (W, R)}
8. The above-found handler interface value h is our prior registration to the route to match the request handler, while the dynamic type of H is the Handlerfunc type (it also satisfies the handler interface); So, above H.servehttp (W, R) The actual call is the dynamic value held in the interface value H (i.e. the handler function we define)
Type Handlerfunc func (responsewriter, *request)//Servehttp method for implementing the Handler interface Func (f Handlerfunc) Servehttp (w Responsewriter, R *request) { f (W, R)//call itself}
At this point, the entire calling process is explained, and the processing logic of the business layer is implemented by each processing function.
Four, redirect:
The HTTP package comes with several functions for creating common processors: Fileserver,notfoundhandler, Redirecthandler, Stripprefix, Timeouthandler.
and RedirecthandlerThe function is used to redirect: It returns a request handler that uses the status code code redirect to the URL for each request
Func Main () { MUX: = http. Newservemux () mux. Handle ("/to", http. Redirecthandler ("http://example.org", 307)) err: = http. Listenandserve (*ADDR,MUX)//Start monitoring if err! = Nil { log. Fatalln ("Listenandserve:", Err) }}
OK, this article is about the HTTP packet on the HTTP server side of the thing, as for the client side of the simple reference to the official documentation, after all, the client is rarely used go implementation.
v. Implementation of the client:
The, post, and Postform functions make Http/https requests .
RESP, err: = http. Get ("http://example.com/") ... resp, err: = http. Post ("Http://example.com/upload", "Image/jpeg", &buf) ... resp, err: = http. Postform ("Http://example.com/form", url. values{"key": {"Value"}, "id": {"123"}})
The program must close the body of the reply after it has finished using the reply .
RESP, err: = http. Get ("http://example.com/") if err! = Nil {//handle Error}defer resp. Body.close () body, err: = Ioutil. ReadAll (resp. Body)//...
To manage the header domain, redirection policy, and other settings for the HTTP client, create a client:
Client: = &http. CLIENT{CHECKREDIRECT:REDIRECTPOLICYFUNC,}RESP, Err: = client. Get ("http://example.com")//... req, err: = http. Newrequest ("GET", "http://example.com", nil)//... req. Header.add ("If-none-match", ' w/' Wyzzy "') resp, err: = client. Do (req)//...
To manage agents, TLS configuration, keep-alive, compression, and other settings, Create a transport:
TR: = &http. Transport{tlsclientconfig: &tls. Config{rootcas:pool},disablecompression:true,}client: = &http. CLIENT{TRANSPORT:TR}RESP, Err: = client. Get ("https://example.com")
both the client and transport types can be safely used by multiple go-threads simultaneously. For efficiency reasons, it should be built at once and reused as much as possible .
if the above is misleading, please be sure to point out the predecessors!