首页 文章资讯内容详情

golang net之http server

2026-06-01 2 花语

本文内容纲要:

golang版本:1.12.9

简单的HTTP服务器代码:

packagemain import( "net/http" ) typeTestHandlerstruct{ strstring } func(th*TestHandler)ServeHTTP(whttp.ResponseWriter,r*http.Request){ w.Write([]byte(string(th.str+",welcome"))) } funcmain(){ http.Handle("/",&TestHandler{"Hi,Stranger"}) http.HandleFunc("/test",func(whttp.ResponseWriter,r*http.Request){ w.Write([]byte("Hi,Tester")) }) http.ListenAndServe(":8000",nil)}

在浏览器输入“http://127.0.0.1:8000”得到输出“Hi,Stranger,welcome”;输入“http://127.0.0.1:8000/test”得到输出“Hi,Tester”

handler的注册

handler的相关方法如下:

funcNewServeMux()*ServeMux func(mux*ServeMux)Handle(patternstring,handlerHandler)//注册handler func(mux*ServeMux)HandleFunc(patternstring,handlerfunc(ResponseWriter,*Request))//注册handler func(mux*ServeMux)Handler(r*Request)(hHandler,patternstring)//在mux.m中根据pattern查找handler func(mux*ServeMux)ServeHTTP(wResponseWriter,r*Request)//handler的具体实现

http使用handler定义请求的路径以及请求的处理。每个handler都必须实现ServeHTTP方法,该方法将请求分发到不同的handler进行处理,每个handler处理一条请求路径。有两种注册handler的方式:http.Handlehttp.HandleFunc,两种实现本质上是一致的,前者需要明确写出ServeHTTP方法的实现,后者由内置方法实现(见下文)。

Handler的接口定义如下:

//net/http/server.go typeHandlerinterface{ ServeHTTP(ResponseWriter,*Request) }

http.HandleFunc的第二个参数被定义为HandlerFunc,实现了Handler接口。

//net/http/server.go typeHandlerFuncfunc(ResponseWriter,*Request) //ServeHTTPcallsf(w,r). func(fHandlerFunc)ServeHTTP(wResponseWriter,r*Request){ f(w,r) }

当http.ListenAndServe的第二个参数为nil,则使用http.Handle和http.HandleFunc方法注册的handler,默认保存在http.DefaultServeMux.m中(注册方法为ServeMux.Handle/ServeMux.HandleFunc)。当httpserver接收到一个request时,会在serverHandler.ServeHTTP中调用DefaultServeMux.ServeHTTP来处理接收到的request,分为两步:

调用ServeMux.Handler函数,在ServeMux.m中根据pattern遍历查合适的handler 调用handler的ServeHTTP方法

serverHandler.ServeHTTP的源码如下:

//net/http/server.go func(shserverHandler)ServeHTTP(rwResponseWriter,req*Request){ //如果有自注册的handler则使用自注册的,否则使用默认的handler处理请求 handler:=sh.srv.Handler ifhandler==nil{ handler=DefaultServeMux } ifreq.RequestURI=="*"&&req.Method=="OPTIONS"{ handler=globalOptionsHandler{} } handler.ServeHTTP(rw,req) }

DefaultServeMux的结构体定义如下:

varDefaultServeMux=&defaultServeMux vardefaultServeMuxServeMux //net/http/server.go typeServeMuxstruct{ musync.RWMutex mmap[string]muxEntry es[]muxEntry//sliceofentriessortedfromlongesttoshortest. hostsbool//whetheranypatternscontainhostnames }

默认的handler的ServeHTTP方法实现如下,主要实现查找handler并处理请求

func(mux*ServeMux)ServeHTTP(wResponseWriter,r*Request){ ifr.RequestURI=="*"{ ifr.ProtoAtLeast(1,1){ w.Header().Set("Connection","close") } w.WriteHeader(StatusBadRequest) return } //根据请求的路径查找注册的handler h,_:=mux.Handler(r) //调用注册的handler处理请求,对应上面例子的 //http.HandleFunc("/test",func(whttp.ResponseWriter,r*http.Request){w.Write([]byte("Hi,Tester"))}) h.ServeHTTP(w,r) } //本函数根据请求中的路径找到合适的handler或者重定向(请求路径格式不正确) func(mux*ServeMux)Handler(r*Request)(hHandler,patternstring){ //CONNECTrequestsarenotcanonicalized. //对CONNECT请求的处理,代理场景 ifr.Method=="CONNECT"{ //Ifr.URL.Pathis/treeanditshandlerisnotregistered, //the/tree->/tree/redirectappliestoCONNECTrequests //butthepathcanonicalizationdoesnot. //redirectToPathSlash函数主要用于自动检测是否重定向URL并修改重定向URL路径,当注册的URL路径为/tree/,而请求URL路径为/tree, //redirectToPathSlash函数无法在mux.m中查找注册的handler,则将设请求URL设置为/tree/ ifu,ok:=mux.redirectToPathSlash(r.URL.Host,r.URL.Path,r.URL);ok{ returnRedirectHandler(u.String(),StatusMovedPermanently),u.Path } returnmux.handler(r.Host,r.URL.Path) } //Allotherrequestshaveanyportstrippedandpathcleaned //beforepassingtomux.handler. host:=stripHostPort(r.Host) path:=cleanPath(r.URL.Path) //非代理场景重定向的处理,与"CONNECT"逻辑相同 ifu,ok:=mux.redirectToPathSlash(host,path,r.URL);ok{ returnRedirectHandler(u.String(),StatusMovedPermanently),u.Path } //如果请求路径不等于处理后的路径,如请求路径为"//test/",处理后的路径为"/test/",执行重定向并返回URL路径,重定向 //通过http.redirectHandler.ServeHTTP函数进行处理,如下: /*

<

MovedPermanently.

*/

ifpath!=r.URL.Path{ _,pattern=mux.handler(host,path) url:=*r.URL url.Path=path returnRedirectHandler(url.String(),StatusMovedPermanently),pattern } //在mux.m和mux.es中根据host/url.path找到对应的handler returnmux.handler(host,r.URL.Path) } func(rh*redirectHandler)ServeHTTP(wResponseWriter,r*Request){ Redirect(w,r,rh.url,rh.code) }

通常使用http.HandleFunc注册handler,使用DefaultServeMux的方法分发处理请求即可。也可以通过http.NewServeMux()创建一个自定义的serverHandler,并实现ServeHTTP方法。

import( "net/http" ) typeTestHandlerstruct{ strstring } func(th*TestHandler)ServeHTTP(whttp.ResponseWriter,r*http.Request){ w.Write([]byte(string(th.str+",welcome"))) } funcmain(){ serverHandler:=http.NewServeMux() serverHandler.Handle("/",&TestHandler{"Hi,Stranger"}) serverHandler.HandleFunc("/test",func(whttp.ResponseWriter,r*http.Request){ w.Write([]byte("Hi,Tester")) }) http.ListenAndServe(":8000",serverHandler) }

http.server

调用下面函数进行监听,主要创建监听socket并接收该socket上的连接。通常调用如下接口即可:

funcListenAndServe(addrstring,handlerHandler)error{ server:=&Server{Addr:addr,Handler:handler} returnserver.ListenAndServe() }

一个Server结构体表示一个启用监听端口的真实服务

typeServerstruct{ Addrstring//TCPaddresstolistenon,":http"ifempty HandlerHandler//handlertoinvoke,http.DefaultServeMuxifnil //TLSConfigoptionallyprovidesaTLSconfigurationforuse //byServeTLSandListenAndServeTLS.Notethatthisvalueis //clonedbyServeTLSandListenAndServeTLS,soitsnot //possibletomodifytheconfigurationwithmethodslike //tls.Config.SetSessionTicketKeys.Touse //SetSessionTicketKeys,useServer.ServewithaTLSListener //instead. TLSConfig*tls.Config //ReadTimeoutisthemaximumdurationforreadingtheentire //request,includingthebody. // //BecauseReadTimeoutdoesnotletHandlersmakeper-request //decisionsoneachrequestbodysacceptabledeadlineor //uploadrate,mostuserswillprefertouse //ReadHeaderTimeout.Itisvalidtousethemboth. ReadTimeouttime.Duration //ReadHeaderTimeoutistheamountoftimeallowedtoread //requestheaders.Theconnectionsreaddeadlineisreset //afterreadingtheheadersandtheHandlercandecidewhat //isconsideredtooslowforthebody.IfReadHeaderTimeout //iszero,thevalueofReadTimeoutisused.Ifbothare //zero,thereisnotimeout. ReadHeaderTimeouttime.Duration //WriteTimeoutisthemaximumdurationbeforetimingout //writesoftheresponse.Itisresetwheneveranew //requestsheaderisread.LikeReadTimeout,itdoesnot //letHandlersmakedecisionsonaper-requestbasis. WriteTimeouttime.Duration //IdleTimeoutisthemaximumamountoftimetowaitforthe //nextrequestwhenkeep-alivesareenabled.IfIdleTimeout //iszero,thevalueofReadTimeoutisused.Ifbothare //zero,thereisnotimeout. IdleTimeouttime.Duration //MaxHeaderBytescontrolsthemaximumnumberofbytesthe //serverwillreadparsingtherequestheaderskeysand //values,includingtherequestline.Itdoesnotlimitthe //sizeoftherequestbody. //Ifzero,DefaultMaxHeaderBytesisused. MaxHeaderBytesint //TLSNextProtooptionallyspecifiesafunctiontotakeover //ownershipoftheprovidedTLSconnectionwhenanNPN/ALPN //protocolupgradehasoccurred.Themapkeyistheprotocol //namenegotiated.TheHandlerargumentshouldbeusedto //handleHTTPrequestsandwillinitializetheRequestsTLS //andRemoteAddrifnotalreadyset.Theconnectionis //automaticallyclosedwhenthefunctionreturns. //IfTLSNextProtoisnotnil,HTTP/2supportisnotenabled //automatically. TLSNextProtomap[string]func(*Server,*tls.Conn,Handler) //ConnStatespecifiesanoptionalcallbackfunctionthatis //calledwhenaclientconnectionchangesstate.Seethe //ConnStatetypeandassociatedconstantsfordetails. ConnStatefunc(net.Conn,ConnState) //ErrorLogspecifiesanoptionalloggerforerrorsaccepting //connections,unexpectedbehaviorfromhandlers,and //underlyingFileSystemerrors. //Ifnil,loggingisdoneviathelogpackagesstandardlogger. ErrorLog*log.Logger //BaseContextoptionallyspecifiesafunctionthatreturns //thebasecontextforincomingrequestsonthisserver. //TheprovidedListeneristhespecificListenerthats //abouttostartacceptingrequests. //IfBaseContextisnil,thedefaultiscontext.Background(). //Ifnon-nil,itmustreturnanon-nilcontext. BaseContextfunc(net.Listener)context.Context //ConnContextoptionallyspecifiesafunctionthatmodifies //thecontextusedforanewconnectionc.Theprovidedctx //isderivedfromthebasecontextandhasaServerContextKey //value. ConnContextfunc(ctxcontext.Context,cnet.Conn)context.Context disableKeepAlivesint32//accessedatomically. inShutdownint32//accessedatomically(non-zeromeanswereinShutdown) nextProtoOncesync.Once//guardssetupHTTP2_*init nextProtoErrerror//resultofhttp2.ConfigureServerifused musync.Mutex listenersmap[*net.Listener]struct{} activeConnmap[*conn]struct{} doneChanchanstruct{} onShutdown[]func() }

ListenAndServe在创建监听socket后调用Serve等待连接

func(srv*Server)ListenAndServe()error{ //服务器调用Server.Close或Server.Shutdown关闭连接时会设置shuttingDown为1,表示该服务正在停止,不可提供服务。 //Close会直接关闭底层tcp连接,Shutdown则会调用服务提供的函数Server.onShutdown平滑关闭。推荐使用Shutdown ifsrv.shuttingDown(){ returnErrServerClosed } addr:=srv.Addr ifaddr==""{ addr=":http" } ln,err:=net.Listen("tcp",addr) iferr!=nil{ returnerr } returnsrv.Serve(ln) }

ListenAndServeTLS与ListenAndServe类似,只是入参多了证书参数

func(srv*Server)ListenAndServeTLS(certFile,keyFilestring)error{ ifsrv.shuttingDown(){ returnErrServerClosed } addr:=srv.Addr ifaddr==""{ addr=":https"}ln,err:=net.Listen("tcp",addr)iferr!=nil{returnerr}deferln.Close()returnsrv.ServeTLS(ln,certFile,keyFile)}

ServeTLS函数中会调用tls.NewListener创建一个tls类型的监听socket,后续会调用tls的Accetp函数接收客户端连接

func(srv*Server)ServeTLS(lnet.Listener,certFile,keyFilestring)error{ //SetupHTTP/2beforesrv.Serve,toinitializesrv.TLSConfig //beforewecloneitandcreatetheTLSListener. iferr:=srv.setupHTTP2_ServeTLS();err!=nil{ returnerr } config:=cloneTLSConfig(srv.TLSConfig) if!strSliceContains(config.NextProtos,"http/1.1"){ config.NextProtos=append(config.NextProtos,"http/1.1")}configHasCert:=len(config.Certificates)>0||config.GetCertificate!=nilif!configHasCert||certFile!=""||keyFile!=""{varerrerrorconfig.Certificates=make([]tls.Certificate,1)config.Certificates[0],err=tls.LoadX509KeyPair(certFile,keyFile)iferr!=nil{returnerr}}tlsListener:=tls.NewListener(l,config)returnsrv.Serve(tlsListener)} //src/crypto/tls/tls.go //tls的Accept仅仅在处理Server函数是增加了证书相关的参数 func(l*listener)Accept()(net.Conn,error){ c,err:=l.Listener.Accept() iferr!=nil{ returnnil,err } returnServer(c,l.config),nil }

Serve主要实现如下。通过Accept与客户端创建连接后,通过newConn函数初始化一个HTTP连接,该连接包含HTTP的描述(监听地址,URL等)和一个TCP连接,然后处理来自客户的HTTP请求。

func(srv*Server)Serve(lnet.Listener)error{ ... ctx:=context.WithValue(baseCtx,ServerContextKey,srv) for{ //Accept()返回底层TCP的连接 rw,e:=l.Accept() ife!=nil{ select{ case<-srv.getDoneChan(): returnErrServerClosed default: } ifne,ok:=e.(net.Error);ok&&ne.Temporary(){ //处理accept因为网络失败之后的等待时间 iftempDelay==0{ tempDelay=5*time.Millisecond }else{ tempDelay*=2 } ifmax:=1*time.Second;tempDelay>max{ tempDelay=max } srv.logf("http:Accepterror:%v;retryingin%v",e,tempDelay) time.Sleep(tempDelay) continue } returne } ifcc:=srv.ConnContext;cc!=nil{ ctx=cc(ctx,rw) ifctx==nil{ panic("ConnContextreturnednil") } } tempDelay=0 //构造HTTP连接 c:=srv.newConn(rw) c.setState(c.rwc,StateNew)//beforeServecanreturn //在另外的goroutine中处理基于该TCP的HTTP请求,本goroutine可以继续acceptTCP连接 goc.serve(ctx) } }

Accept返回的底层的连接结构如下

typeConninterface{ //Readreadsdatafromtheconnection. //ReadcanbemadetotimeoutandreturnanErrorwithTimeout()==true //afterafixedtimelimit;seeSetDeadlineandSetReadDeadline. Read(b[]byte)(nint,errerror) //Writewritesdatatotheconnection. //WritecanbemadetotimeoutandreturnanErrorwithTimeout()==true //afterafixedtimelimit;seeSetDeadlineandSetWriteDeadline. Write(b[]byte)(nint,errerror) //Closeclosestheconnection. //AnyblockedReadorWriteoperationswillbeunblockedandreturnerrors. Close()error //LocalAddrreturnsthelocalnetworkaddress. LocalAddr()Addr //RemoteAddrreturnstheremotenetworkaddress. RemoteAddr()Addr //SetDeadlinesetsthereadandwritedeadlinesassociated //withtheconnection.Itisequivalenttocallingboth //SetReadDeadlineandSetWriteDeadline. // //AdeadlineisanabsolutetimeafterwhichI/Ooperations //failwithatimeout(seetypeError)insteadof //blocking.Thedeadlineappliestoallfutureandpending //I/O,notjusttheimmediatelyfollowingcalltoReador //Write.Afteradeadlinehasbeenexceeded,theconnection //canberefreshedbysettingadeadlineinthefuture. // //Anidletimeoutcanbeimplementedbyrepeatedlyextending //thedeadlineaftersuccessfulReadorWritecalls. // //AzerovaluefortmeansI/Ooperationswillnottimeout. // //NotethatifaTCPconnectionhaskeep-aliveturnedon, //whichisthedefaultunlessoverriddenbyDialer.KeepAlive //orListenConfig.KeepAlive,thenakeep-alivefailuremay //alsoreturnatimeouterror.OnUnixsystemsakeep-alive //failureonI/Ocanbedetectedusing //errors.Is(err,syscall.ETIMEDOUT). SetDeadline(ttime.Time)error //SetReadDeadlinesetsthedeadlineforfutureReadcalls //andanycurrently-blockedReadcall. //AzerovaluefortmeansReadwillnottimeout. SetReadDeadline(ttime.Time)error //SetWriteDeadlinesetsthedeadlineforfutureWritecalls //andanycurrently-blockedWritecall. //Evenifwritetimesout,itmayreturnn>0,indicatingthat //someofthedatawassuccessfullywritten. //AzerovaluefortmeansWritewillnottimeout. SetWriteDeadline(ttime.Time)error }

实现如上接口的有tcpsock的TCPConn以及unixsock的UnixConn,通常使用TCPConn

typeTCPConnstruct{ conn } typeUnixConnstruct{ conn }

newConn生成的HTTP结构体如下,它表示一条基于TCP的HTTP连接,封装了3个重要的数据结构:server表示HTTPserver的"server";rwc表示底层连接结构体rwcnet.Conn;r用于读取http数据的connReader(从rwc读取数据)。后续的request和response都基于该结构体

typeconnstruct{ //serveristheserveronwhichtheconnectionarrived. //Immutable;nevernil. server*Server //cancelCtxcancelstheconnection-levelcontext. cancelCtxcontext.CancelFunc //rwcistheunderlyingnetworkconnection. //Thisisneverwrappedbyothertypesandisthevaluegivenout //toCloseNotifiercallers.Itisusuallyoftype*net.TCPConnor //*tls.Conn. rwcnet.Conn //remoteAddrisrwc.RemoteAddr().String().Itisnotpopulatedsynchronously //insidetheListenersAcceptgoroutine,assomeimplementationsblock. //Itispopulatedimmediatelyinsidethe(*conn).servegoroutine. //ThisisthevalueofaHandlers(*Request).RemoteAddr. remoteAddrstring //tlsStateistheTLSconnectionstatewhenusingTLS. //nilmeansnotTLS. tlsState*tls.ConnectionState //werrissettothefirstwriteerrortorwc. //ItissetviacheckConnErrorWriter{w},wherebufwwrites. werrerror //risbufrsreadsource.Itsawrapperaroundrwcthatprovides //io.LimitedReader-stylelimiting(whilereadingrequestheaders) //andfunctionalitytosupportCloseNotifier.See*connReaderdocs. r*connReader //bufrreadsfromr. bufr*bufio.Reader //bufwwritestocheckConnErrorWriter{c},whichpopulateswerronerror. bufw*bufio.Writer //lastMethodisthemethodofthemostrecentrequest //onthisconnection,ifany. lastMethodstring curReqatomic.Value//of*response(whichhasaRequestinit) curStatestruct{atomicuint64}//packed(unixtime<<8|uint8(ConnState)) //muguardshijackedv musync.Mutex //hijackedviswhetherthisconnectionhasbeenhijacked //byaHandlerwiththeHijackerinterface. //Itisguardedbymu. hijackedvbool }

connReader中的conn就是上面表示http连接的结构体

typeconnReaderstruct{ conn*conn musync.Mutex//guardsfollowing hasBytebool byteBuf[1]byte cond*sync.Cond inReadbool abortedbool//settruebeforeconn.rwcdeadlineissettopast remainint64//bytesremaining }

在下面的server函数中处理请求并返回响应

func(c*conn)serve(ctxcontext.Context){ c.remoteAddr=c.rwc.RemoteAddr().String() ctx=context.WithValue(ctx,LocalAddrContextKey,c.rwc.LocalAddr()) deferfunc(){ iferr:=recover();err!=nil&&err!=ErrAbortHandler{ constsize=64<<10 buf:=make([]byte,size) buf=buf[:runtime.Stack(buf,false)] c.server.logf("http:panicserving%v:%v\n%s",c.remoteAddr,err,buf) } if!c.hijacked(){ c.close() c.setState(c.rwc,StateClosed) } }() //处理ServeTLSaccept的连接 iftlsConn,ok:=c.rwc.(*tls.Conn);ok{ ifd:=c.server.ReadTimeout;d!=0{ //设置TCP的读超时时间 c.rwc.SetReadDeadline(time.Now().Add(d)) } ifd:=c.server.WriteTimeout;d!=0{ //设置TCP的写超时时间 c.rwc.SetWriteDeadline(time.Now().Add(d)) } //tls协商并判断协商结果 iferr:=tlsConn.Handshake();err!=nil{ //Ifthehandshakefailedduetotheclientnotspeaking //TLS,assumetheyrespeakingplaintextHTTPandwritea //400responseontheTLSconnsunderlyingnet.Conn. ifre,ok:=err.(tls.RecordHeaderError);ok&&re.Conn!=nil&&tlsRecordHeaderLooksLikeHTTP(re.RecordHeader){ io.WriteString(re.Conn,"HTTP/1.0400BadRequest\r\n\r\nClientsentanHTTPrequesttoanHTTPSserver.\n") re.Conn.Close() return } c.server.logf("http:TLShandshakeerrorfrom%s:%v",c.rwc.RemoteAddr(),err) return } c.tlsState=new(tls.ConnectionState) *c.tlsState=tlsConn.ConnectionState() //用于判断是否使用TLS的NPN扩展协商出非http/1.1和http/1.0的上层协议,如果存在则使用server.TLSNextProto处理请求 ifproto:=c.tlsState.NegotiatedProtocol;validNPN(proto){ iffn:=c.server.TLSNextProto[proto];fn!=nil{ h:=initNPNRequest{ctx,tlsConn,serverHandler{c.server}} fn(c.server,tlsConn,h) } return } } //下面处理HTTP/1.x的请求 ctx,cancelCtx:=context.WithCancel(ctx) c.cancelCtx=cancelCtx defercancelCtx() //为c.bufr创建read源,使用sync.pool提高存取效率 c.r=&connReader{conn:c} //readbuf长度默认为4096,创建ioReader为c.r的bufio.Reader。用于读取HTTP的request c.bufr=newBufioReader(c.r) //c.bufw默认长度为4096,4<<10=4096,用于发送response c.bufw=newBufioWriterSize(checkConnErrorWriter{c},4<<10) //循环处理HTTP请求 for{ //处理请求并返回封装好的响应 w,err:=c.readRequest(ctx) //判断是否有读取过数据,如果读取过数据则设置TCP状态为active ifc.r.remain!=c.server.initialReadLimitSize(){ //Ifwereadanybytesoffthewire,wereactive. c.setState(c.rwc,StateActive) } //处理http请求错误 iferr!=nil{ consterrorHeaders="\r\nContent-Type:text/plain;charset=utf-8\r\nConnection:close\r\n\r\n" switch{ caseerr==errTooLarge: //TheirHTTPclientmayormaynotbe //abletoreadthisifwere //respondingtothemandhangingup //whiletheyrestillwritingtheir //request.Undefinedbehavior. constpublicErr="431RequestHeaderFieldsTooLarge" fmt.Fprintf(c.rwc,"HTTP/1.1"+publicErr+errorHeaders+publicErr) c.closeWriteAndWait() return //直接return会断开底层TCP连接(GC?) caseisUnsupportedTEError(err): //RespondasperRFC7230Section3.3.1whichsays, //Aserverthatreceivesarequestmessagewitha //transfercodingitdoesnotunderstandSHOULD //respondwith501(Unimplemented). code:=StatusNotImplemented //Wepurposefullyarentechoingbackthetransfer-encodingsvalue, //soastomitigatetheriskofcrosssidescriptingbyanattacker. fmt.Fprintf(c.rwc,"HTTP/1.1%d%s%sUnsupportedtransferencoding",code,StatusText(code),errorHeaders) return caseisCommonNetReadError(err): return//dontreply default: publicErr:="400BadRequest" ifv,ok:=err.(badRequestError);ok{ publicErr=publicErr+":"+string(v) } fmt.Fprintf(c.rwc,"HTTP/1.1"+publicErr+errorHeaders+publicErr) return } } //Expect100Continuesupport req:=w.req //如果http首部包含"100-continue"请求 ifreq.expectsContinue(){ //"100-continue"的首部要求http1.1版本以上,且http.body长度不为0 ifreq.ProtoAtLeast(1,1)&&req.ContentLength!=0{ //WraptheBodyreaderwithonethatrepliesontheconnection req.Body=&expectContinueReader{readCloser:req.Body,resp:w} } //非"100-continue"但首部包含"Expect"字段的请求为非法请求 }elseifreq.Header.get("Expect")!=""{ w.sendExpectationFailed() return } //curReq保存了当前的response,当前代码中主要用于在读失败后调用response中的closeNotifyCh传递信号,此时连接断开 c.curReq.Store(w) //判断是否有后续的数据,req.Body在http.readTransfer函数中设置为http.body类型,registerOnHitEOF注册的就是 //遇到EOF时执行的函数http.body.onHitEOF ifrequestBodyRemains(req.Body){ registerOnHitEOF(req.Body,w.conn.r.startBackgroundRead) }else{ //如果没有后续的数据,调用下面函数在新的goroutine中阻塞等待数据的到来,通知finishRequest w.conn.r.startBackgroundRead() } //HTTPcannothavemultiplesimultaneousactiverequests.[*] //Untiltheserverrepliestothisrequest,itcantreadanother, //sowemightaswellrunthehandlerinthisgoroutine. //[*]Notstrictlytrue:HTTPpipelining.Wecouldletthemallprocess //inparalleleveniftheirresponsesneedtobeserialized. //ButwerenotgoingtoimplementHTTPpipeliningbecauseit //wasneverdeployedinthewildandtheanswerisHTTP/2. //通过请求找到匹配的handler,然后处理请求并发送响应 serverHandler{c.server}.ServeHTTP(w,w.req) w.cancelCtx() ifc.hijacked(){ return } //该函数中会结束HTTP请求,发送response w.finishRequest() //判断是否需要重用底层TCP连接,即是否退出本函数的for循环,推出for循环将断开连接 if!w.shouldReuseConnection(){ //不可重用底层连接时,如果请求数据过大或设置提前取消读取数据,则调用closeWriteAndWait平滑关闭TCP连接 ifw.requestBodyLimitHit||w.closedRequestBodyEarly(){ c.closeWriteAndWait() } return } //重用连接,设置底层状态为idle c.setState(c.rwc,StateIdle) c.curReq.Store((*response)(nil)) //如果没有通过SetKeepAlivesEnabled设置HTTPkeepalive或底层连接已经通过如Server.Close关闭,则直接退出 if!w.conn.server.doKeepAlives(){ //Wereinshutdownmode.Wemightvereplied //totheuserwithout"Connection:close"and //theymightthinktheycansendanother //request,butsuchislifewithHTTP/1.1. return } ifd:=c.server.idleTimeout();d!=0{ //如果设置了idle状态超时时间,则调用SetReadDeadline设置底层连接deadline,并调用bufr.Peek等待请求 c.rwc.SetReadDeadline(time.Now().Add(d)) if_,err:=c.bufr.Peek(4);err!=nil{ return } } c.rwc.SetReadDeadline(time.Time{}) } }

readRequest函数处理http请求

func(c*conn)readRequest(ctxcontext.Context)(w*response,errerror){ ifc.hijacked(){ returnnil,ErrHijacked } var( wholeReqDeadlinetime.Time//orzeroifnone hdrDeadlinetime.Time//orzeroifnone ) t0:=time.Now() //设置读取HTTP的超时时间 ifd:=c.server.readHeaderTimeout();d!=0{ hdrDeadline=t0.Add(d) } //设置读取整个HTTP的超时时间 ifd:=c.server.ReadTimeout;d!=0{ wholeReqDeadline=t0.Add(d) } //通过SetReadDeadline设置TCP读超时时间 c.rwc.SetReadDeadline(hdrDeadline) ifd:=c.server.WriteTimeout;d!=0{ //通过defer设置TCP写超时时间,本函数主要处理读请求,在本函数处理完request之后再设置写超时时间 deferfunc(){ c.rwc.SetWriteDeadline(time.Now().Add(d)) }() } //设置读取请求的最大字节数,为DefaultMaxHeaderBytes+4096=1052672,用于防止超大报文攻击 c.r.setReadLimit(c.server.initialReadLimitSize()) //处理老设备的client ifc.lastMethod=="POST"{ //RFC7230section3.5MessageParsingRobustnesstoleranceforoldbuggyclients. peek,_:=c.bufr.Peek(4)//ReadRequestwillgeterrbelow c.bufr.Discard(numLeadingCRorLF(peek)) } //从bufr读取request,并返回结构体格式的请求 req,err:=readRequest(c.bufr,keepHostHeader) iferr!=nil{ //如果读取的报文超过限制,则返回错误 ifc.r.hitReadLimit(){ returnnil,errTooLarge } returnnil,err } //判断是否是go服务所支持的HTTP/1.x的请求 if!http1ServerSupportsRequest(req){ returnnil,badRequestError("unsupportedprotocolversion") } c.lastMethod=req.Method c.r.setInfiniteReadLimit() hosts,haveHost:=req.Header["Host"] isH2Upgrade:=req.isH2Upgrade() //判断是否需要Host首部字段 ifreq.ProtoAtLeast(1,1)&&(!haveHost||len(hosts)==0)&&!isH2Upgrade&&req.Method!="CONNECT"{ returnnil,badRequestError("missingrequiredHostheader") } //多个Host首部字段 iflen(hosts)>1{ returnnil,badRequestError("toomanyHostheaders") } //非法Host首部字段值 iflen(hosts)==1&&!httpguts.ValidHostHeader(hosts[0]){ returnnil,badRequestError("malformedHostheader") } //判断首部字段值是否有非法字符 fork,vv:=rangereq.Header{ if!httpguts.ValidHeaderFieldName(k){ returnnil,badRequestError("invalidheadername") } for_,v:=rangevv{ if!httpguts.ValidHeaderFieldValue(v){ returnnil,badRequestError("invalidheadervalue") } } } //响应报文中不包含Host字段 delete(req.Header,"Host") ctx,cancelCtx:=context.WithCancel(ctx) req.ctx=ctx req.RemoteAddr=c.remoteAddr req.TLS=c.tlsState ifbody,ok:=req.Body.(*body);ok{ body.doEarlyClose=true } //判断是否超过请求的最大值 if!hdrDeadline.Equal(wholeReqDeadline){ c.rwc.SetReadDeadline(wholeReqDeadline) } w=&response{ conn:c, cancelCtx:cancelCtx, req:req, reqBody:req.Body, handlerHeader:make(Header), contentLength:-1, closeNotifyCh:make(chanbool,1), //Wepopulatetheseaheadoftimesowerenot //readingfromreq.HeaderaftertheirHandlerstarts //andmaybemutatesit(Issue14940) wants10KeepAlive:req.wantsHttp10KeepAlive(), wantsClose:req.wantsClose(), } ifisH2Upgrade{ w.closeAfterReply=true } //w.cw.res中保存了response的信息,而response中又保存了底层连接conn,后续将通过w.cw.res.conn写数据 w.cw.res=w //创建2048字节的写bufio,用于发送response w.w=newBufioWriterSize(&w.cw,bufferBeforeChunkingSize) returnw,nil }

读取HTTP请求,并将其结构化为http.Request

funcreadRequest(b*bufio.Reader,deleteHostHeaderbool)(req*Request,errerror){ //封装为textproto.Reader,该结构体实现了读取HTTP的相关方法 tp:=newTextprotoReader(b) //初始化一个Request结构体,该函数后续工作就是填充该变量并返回 req=new(Request) //Firstline:GET/index.htmlHTTP/1.0 varsstring //ReadLine会调用<textproto.(*Reader).ReadLine->textproto.(*Reader).readLineSlice->bufio.(*Reader).ReadLine-> //bufio.(*Reader).ReadSlic->bufio.(*Reader).fill->http.(*connReader).Read>读取HTTP的请求并填充b.buf,并返回以"\n"作为 //分隔符的首行字符串 ifs,err=tp.ReadLine();err!=nil{ returnnil,err } //putTextprotoReader函数使用sync.pool来保存textproto.Reader变量,通过重用内存来提升在大量HTTP请求下执行效率。 //对应函数首部的newTextprotoReader deferfunc(){ putTextprotoReader(tp) iferr==io.EOF{ err=io.ErrUnexpectedEOF } }() varokbool //解析请求方法,请求URL,请求协议 req.Method,req.RequestURI,req.Proto,ok=parseRequestLine(s) if!ok{ returnnil,&badStringError{"malformedHTTPrequest",s} } //判断方法是否包含非法字符 if!validMethod(req.Method){ returnnil,&badStringError{"invalidmethod",req.Method} } //获取请求路径,如HTTP请求为"http://127.0.0.1:8000/test"时,rawurl为"/test" rawurl:=req.RequestURI //判断HTTP协议版本有效性,通常为支持HTTP/1.x ifreq.ProtoMajor,req.ProtoMinor,ok=ParseHTTPVersion(req.Proto);!ok{ returnnil,&badStringError{"malformedHTTPversion",req.Proto} } //CONNECTrequestsareusedtwodifferentways,andneitherusesafullURL: //ThestandarduseistotunnelHTTPSthroughanHTTPproxy. //Itlookslike"CONNECTwww.google.com:443HTTP/1.1",andtheparameteris //justtheauthoritysectionofaURL.Thisinformationshouldgoinreq.URL.Host. // //Thenet/rpcpackagealsousesCONNECT,buttheretheparameterisapath //thatstartswithaslash.ItcanbeparsedwiththeregularURLparser, //andthepathwillendupinreq.URL.Path,whereitneedstobeinorderfor //RPCtowork. //处理代理场景,使用"CONNECT"与代理建立连接时会使用完整的URL(带host) justAuthority:=req.Method=="CONNECT"&&!strings.HasPrefix(rawurl,"/") ifjustAuthority{ rawurl="http://"+rawurl } ifreq.URL,err=url.ParseRequestURI(rawurl);err!=nil{ returnnil,err } ifjustAuthority{ //Stripthebogus"http://"backoff. req.URL.Scheme="" } //解析request首部的key:value mimeHeader,err:=tp.ReadMIMEHeader() iferr!=nil{ returnnil,err } req.Header=Header(mimeHeader) //RFC7230,section5.3:Musttreat //GET/index.htmlHTTP/1.1 //Host:www.google.com //and //GEThttp://www.google.com/index.htmlHTTP/1.1 //Host:doesntmatter //thesame.Inthesecondcase,anyHostlineisignored. req.Host=req.URL.Host //如果是上面注释中的第一种需要从req.Header中获取"Host"字段 ifreq.Host==""{ req.Host=req.Header.get("Host") } //"Host"字段仅存在于request中,在接收到之后需要删除首部的Host字段,更多参见该变量注释 ifdeleteHostHeader{ delete(req.Header,"Host") } //处理"Cache-Control"首部 fixPragmaCacheControl(req.Header) //判断是否是长连接,如果是,则保持连接,反之则断开并删除"Connection"首部 req.Close=shouldClose(req.ProtoMajor,req.ProtoMinor,req.Header,false) //解析首部字段并填充req内容 err=readTransfer(req,b) iferr!=nil{ returnnil,err } //当HTTP1.1服务尝试解析HTTP2的消息时使用"PRI"方法 ifreq.isH2Upgrade(){ //Becauseitsneitherchunked,nordeclared: req.ContentLength=-1 //Wewanttogivehandlersachancetohijackthe //connection,butweneedtopreventtheServerfrom //dealingwiththeconnectionfurtherifitsnot //hijacked.SetClosetoensurethat: req.Close=true } returnreq,nil } funcshouldClose(major,minorint,headerHeader,removeCloseHeaderbool)bool{ //HTTP/1.x以下不支持"connection"指定长连接 ifmajor<1{ returntrue } conv:=header["Connection"] //如果首部包含"Connection:close"则断开连接 hasClose:=httpguts.HeaderValuesContainsToken(conv,"close") //使用HTTP/1.0时,如果包含"Connection:close"或不包含"Connection:keep-alive",则使用短连接; //HTTP/1.1中不指定"Connection",默认使用长连接 ifmajor==1&&minor==0{ returnhasClose||!httpguts.HeaderValuesContainsToken(conv,"keep-alive") } //如果使用非长连接,且需要删除首部中的Connection字段。在经过proxy或gateway时必须移除Connection首部字段 ifhasClose&&removeCloseHeader{ header.Del("Connection") } returnhasClose } funcreadTransfer(msginterface{},r*bufio.Reader)(errerror){ t:=&transferReader{RequestMethod:"GET"} //Unifyinput isResponse:=false switchrr:=msg.(type){ //消息为响应时的赋值 case*Response: t.Header=rr.Header t.StatusCode=rr.StatusCode t.ProtoMajor=rr.ProtoMajor t.ProtoMinor=rr.ProtoMinor //响应中不需要Connection首部字段,下面函数最后一个参数设置为true,删除该首部字段 t.Close=shouldClose(t.ProtoMajor,t.ProtoMinor,t.Header,true) isResponse=true ifrr.Request!=nil{ t.RequestMethod=rr.Request.Method } //消息为请求时的赋值 case*Request: t.Header=rr.Header t.RequestMethod=rr.Method t.ProtoMajor=rr.ProtoMajor t.ProtoMinor=rr.ProtoMinor //TransfersemanticsforRequestsareexactlylikethosefor //Responseswithstatuscode200,respondingtoaGETmethod t.StatusCode=200 t.Close=rr.Close default: panic("unexpectedtype") } //DefaulttoHTTP/1.1 ift.ProtoMajor==0&&t.ProtoMinor==0{ t.ProtoMajor,t.ProtoMinor=1,1 } //处理"Transfer-Encoding"首部 err=t.fixTransferEncoding() iferr!=nil{ returnerr } //处理"Content-Length"首部,注意此处返回的是真实的消息载体长度 realLength,err:=fixLength(isResponse,t.StatusCode,t.RequestMethod,t.Header,t.TransferEncoding) iferr!=nil{ returnerr } //如果该消息为响应且对应的请求方法为HEAD,如果响应首部包含Content-Length字段,则将此作为响应的ContentLength的值,表示server //可以接收到的数据的最大长度,由于该响应没有有效载体,此时不能使用fixLength返回的真实长度0 ifisResponse&&t.RequestMethod=="HEAD"{ ifn,err:=parseContentLength(t.Header.get("Content-Length"));err!=nil{ returnerr }else{ t.ContentLength=n } }else{ t.ContentLength=realLength } //处理Trailer首部字段,主要进行有消息校验 t.Trailer,err=fixTrailer(t.Header,t.TransferEncoding) iferr!=nil{ returnerr } //IfthereisnoContent-LengthorchunkedTransfer-Encodingona*Response //andthestatusisnot1xx,204or304,thenthebodyisunbounded. //SeeRFC7230,section3.3. //含body但不是chunked且不包含length字段的响应称为unbounded(无法衡量长度的消息)消息,根据RFC7230会被关闭 switchmsg.(type){ case*Response: ifrealLength==-1&& !chunked(t.TransferEncoding)&& bodyAllowedForStatus(t.StatusCode){ //Unboundedbody. t.Close=true } } //Preparebodyreader.ContentLength<0meanschunkedencoding //orcloseconnectionwhenfinished,sincemultipartisnotsupportedyet //给t.Body赋值 switch{ //chunked场景处理 casechunked(t.TransferEncoding): //如果请求为HEAD或响应状态码为1xx,204or304,则消息不包含有效载体 ifnoResponseBodyExpected(t.RequestMethod)||!bodyAllowedForStatus(t.StatusCode){ t.Body=NoBody }else{ //下面会创建chunkedReader t.Body=&body{src:internal.NewChunkedReader(r),hdr:msg,r:r,closing:t.Close} } caserealLength==0: t.Body=NoBody //非chunked且包含有效载体(对应Content-Length),创建limitReader caserealLength>0: t.Body=&body{src:io.LimitReader(r,realLength),closing:t.Close} default: //realLength<0,i.e."Content-Length"notmentionedinheader //此处对于消息有效载体unbounded场景,断开底层连接 ift.Close{ //Closesemantics(i.e.HTTP/1.0) t.Body=&body{src:r,closing:t.Close} }else{ //Persistentconnection(i.e.HTTP/1.1)好像走不到该分支。。。 t.Body=NoBody } } //为请求/响应结构体赋值并通过指针返回 switchrr:=msg.(type){ case*Request: rr.Body=t.Body rr.ContentLength=t.ContentLength rr.TransferEncoding=t.TransferEncoding rr.Close=t.Close rr.Trailer=t.Trailer case*Response: rr.Body=t.Body rr.ContentLength=t.ContentLength rr.TransferEncoding=t.TransferEncoding rr.Close=t.Close rr.Trailer=t.Trailer } returnnil } //1.13.3版本的本函数描述有误,下面代码来自最新master分支 func(t*transferReader)fixTransferEncoding()error{ //本函数主要处理"Transfer-Encoding"首部,如果不存在,则直接退出 raw,present:=t.Header["Transfer-Encoding"] if!present{ returnnil } delete(t.Header,"Transfer-Encoding") //Issue12785;ignoreTransfer-EncodingonHTTP/1.0requests. //HTTP/1.0不处理此首部 if!t.protoAtLeast(1,1){ returnnil } //"Transfer-Encoding"首部字段使用逗号分割 encodings:=strings.Split(raw[0],",") te:=make([]string,0,len(encodings)) //Whenaddingnewencodings,pleasemaintaintheinvariant: //ifchunkedencodingispresent,itmustalways //comelastanditmustbeappliedonlyonce. //SeeRFC7230Section3.3.1Transfer-Encoding. //循环处理各个传输编码,目前仅实现了"chunked" fori,encoding:=rangeencodings{ encoding=strings.ToLower(strings.TrimSpace(encoding)) ifencoding=="identity"{ //"identity"shouldnotbemixedwithothertransfer-encodings/compressions //becauseitmeans"nocompression,notransformation". iflen(encodings)!=1{ return&badStringError{`"identity"whenpresentmustbetheonlytransferencoding`,strings.Join(encodings,",")} } //"identity"isnotrecorded. break } switch{ caseencoding=="chunked": //"chunked"MUSTALWAYSbethelast //encodingaspertheloopinvariant. //Thatis: //Invalid:[chunked,gzip] //Valid:[gzip,chunked] ifi+1!=len(encodings){ return&badStringError{"chunkedmustbeappliedonlyonce,asthelastencoding",strings.Join(encodings,",")} } //Supportedotherwise. caseisGzipTransferEncoding(encoding): //Supported default: return&unsupportedTEError{fmt.Sprintf("unsupportedtransferencoding:%q",encoding)} } te=te[0:len(te)+1] te[len(te)-1]=encoding } iflen(te)>0{ //RFC72303.3.2says"AsenderMUSTNOTsenda //Content-Lengthheaderfieldinanymessagethat //containsaTransfer-Encodingheaderfield." // //butalso: //"Ifamessageisreceivedwithbotha //Transfer-EncodingandaContent-Lengthheader //field,theTransfer-Encodingoverridesthe //Content-Length.Suchamessagemightindicatean //attempttoperformrequestsmuggling(Section9.5) //orresponsesplitting(Section9.4)andoughttobe //handledasanerror.AsenderMUSTremovethe //receivedContent-Lengthfieldpriortoforwarding //suchamessagedownstream." // //Reportedly,theseappearinthewild. //"Transfer-Encoding"就是为了解决"Content-Length"不存在才出现了,因此当存在"Transfer-Encoding"时无需处理"Content-Length", //此处删除"Content-Length"首部,不在fixLength函数中处理 delete(t.Header,"Content-Length") t.TransferEncoding=te returnnil } returnnil } //本函数处理Content-Length首部,并返回真实的消息载体长度 funcfixLength(isResponsebool,statusint,requestMethodstring,headerHeader,te[]string)(int64,error){ isRequest:=!isResponse contentLens:=header["Content-Length"] //HardeningagainstHTTPrequestsmuggling iflen(contentLens)>1{ //PerRFC7230Section3.3.2,preventmultiple //Content-Lengthheadersiftheydifferinvalue. //Iftherearedupsofthevalue,removethedups. //SeeIssue16490. //下面按照RFC7230的建议进行处理,如果一个Content-Length包含多个不同的value,则认为该消息无效 first:=strings.TrimSpace(contentLens[0]) for_,ct:=rangecontentLens[1:]{ iffirst!=strings.TrimSpace(ct){ return0,fmt.Errorf("http:messagecannotcontainmultipleContent-Lengthheaders;got%q",contentLens) } } //如果一个Content-Length包含多个相同的value,则仅保留一个 header.Del("Content-Length") header.Add("Content-Length",first) contentLens=header["Content-Length"] } //处理HEAD请求 ifnoResponseBodyExpected(requestMethod){ //ForHTTPrequests,aspartofhardeningagainstrequest //smuggling(RFC7230),dontallowaContent-Lengthheaderfor //methodswhichdontpermitbodies.Asanexception,allow //exactlyoneContent-Lengthheaderifitsvalueis"0". //当HEAD请求中的Content-Length为0时允许存在该字段 ifisRequest&&len(contentLens)>0&&!(len(contentLens)==1&&contentLens[0]=="0"){ return0,fmt.Errorf("http:methodcannotcontainaContent-Length;got%q",contentLens) } return0,nil } //处理状态码为1xx的响应,不包含消息体 ifstatus/100==1{ return0,nil } //处理状态码为204和304的响应,不包含消息体 switchstatus{ case204,304: return0,nil } //包含Transfer-Encoding时无法衡量数据长度,以Transfer-Encoding为准,设置返回长度为-1,直接返回 ifchunked(te){ return-1,nil } varclstring //获取Content-Length字段值 iflen(contentLens)==1{ cl=strings.TrimSpace(contentLens[0]) } //对Content-Length字段的值进行有效性验证,如果有效则返回该值的整型,无效返回错误 ifcl!=""{ n,err:=parseContentLength(cl) iferr!=nil{ return-1,err } returnn,nil } //数值为空,删除该首部字段 header.Del("Content-Length") //请求中没有Content-Length且没有Transfer-Encoding字段的请求被认为没有有效载体 ifisRequest{ //RFC7230neitherexplicitlypermitsnorforbidsan //entity-bodyonaGETrequestsowepermitoneif //declared,butwedefaultto0here(not-1below) //iftheresnomentionofabody. //Likewise,allotherrequestmethodsareassumedtohave //nobodyifneitherTransfer-Encodingchunkednora //Content-Lengthareset. return0,nil } //Body-EOFlogicbasedonothermethods(likeclosing,orchunkedcoding) //消息为响应,该场景后续会在readTransfer被close处理 return-1,nil } func(cr*connReader)startBackgroundRead(){ cr.lock() defercr.unlock() //表示该连接正在被读取 ifcr.inRead{ panic("invalidconcurrentBody.Readcall") } //表示该连接上是否还有数据 ifcr.hasByte{ return } cr.inRead=true //设置底层连接deadline为1<<64-1 cr.conn.rwc.SetReadDeadline(time.Time{}) //在新的goroutine中等待数据 gocr.backgroundRead() } func(cr*connReader)backgroundRead(){ //阻塞等待读取一个字节的数 n,err:=cr.conn.rwc.Read(cr.byteBuf[:]) cr.lock() //如果存在数据则设置cr.hasByte为true,byteBuf容量为1 ifn==1{ cr.hasByte=true //Wewerepasttheendofthepreviousrequestsbodyalready //(sincewewouldntbeinabackgroundreadotherwise),so //thisisapipelinedHTTPrequest.PriortoGo1.11weusedto //sendontheCloseNotifychannelandcancelthecontexthere, //butthebehaviorwasdocumentedasonly"may",andweonly //didthatbecausethatshowCloseNotifyaccidentallybehaved //inveryearlyGoreleasespriortocontextsupport.Oncewe //addedcontextsupport,peopleusedaHandlers //Request.Context()andpasseditalong.Havingthatcontext //cancelonpipelinedHTTPrequestscausedproblems. //Fortunately,almostnothingusesHTTP/1.xpipelining. //Unfortunately,apt-getdoes,orsometimesdoes. //NewGo1.11behavior:dontfireCloseNotifyorcancel //contextsonpipelinedrequests.Shouldntaffectpeople,but //fixescaseslikeIssue23921.Thisdoesmeanthataclient //closingtheirTCPconnectionaftersendingapipelined //requestwontcancelthecontext,butwellcatchthatonany //writefailure(incheckConnErrorWriter.Write). //Iftheserverneverwrites,yes,therearestillcontrived //server&clientbehaviorswherethisfailstoevercancelthe //context,butthatskindawhyHTTP/1.xpipeliningdied //anyway. } ifne,ok:=err.(net.Error);ok&&cr.aborted&&ne.Timeout(){ //Ignorethiserror.Itstheexpectederrorfrom //anothergoroutinecallingabortPendingRead. }elseiferr!=nil{ cr.handleReadError(err) } cr.aborted=false cr.inRead=false cr.unlock() //当有数据时,通知cr.cond.Wait解锁 cr.cond.Broadcast() } func(w*response)finishRequest(){ w.handlerDone.setTrue() //wroteHeader表示是否已经将响应首部写入,没有则写入 if!w.wroteHeader{ w.WriteHeader(StatusOK) } //此处调用w.cw.write(checkConnErrorWriter)->c.rwc.write发送数据,即调用底层连接的write将buf中的数据发送出去 w.w.Flush() //将w.w重置并放入sync.pool中,待后续重用 putBufioWriter(w.w) //主要构造chunked的结束符:"0\r\n","\r\n",通过cw.chunking判断是否是chunked编码 w.cw.close() //发送bufw缓存的数据 w.conn.bufw.Flush() //用于等待处理未读取完的数据,与connReader.backgroundRead中的cr.cond.Broadcast()对应 w.conn.r.abortPendingRead() //Closethebody(regardlessofw.closeAfterReply)sowecan //re-useitsbufio.Readerlatersafely. w.reqBody.Close() ifw.req.MultipartForm!=nil{ w.req.MultipartForm.RemoveAll() } } func(w*response)shouldReuseConnection()bool{ //表示是否需要在响应之后关闭底层连接。requestTooLarge,isH2Upgrade或包含首部字段"Connection:close"时置位 ifw.closeAfterReply{ //Therequestorsomethingsetwhileexecutingthe //handlerindicatedweshouldntreusethis //connection. returnfalse } //写入数据与"content-length"不匹配,为避免不同步,不重用连接 ifw.req.Method!="HEAD"&&w.contentLength!=-1&&w.bodyAllowed()&&w.contentLength!=w.written{ //Didnotwriteenough.Avoidgettingoutofsync. returnfalse } //Therewassomeerrorwritingtotheunderlyingconnection //duringtherequest,sodontre-usethisconn. //底层连接出现错误,不可重用 ifw.conn.werr!=nil{ returnfalse } //判断是否在读取完数据前执行关闭 ifw.closedRequestBodyEarly(){ returnfalse } returntrue } //closeWriteflushesanyoutstandingdataandsendsaFINpacket(if //clientisconnectedviaTCP),signallingthatweredone.Wethen //pauseforabit,hopingtheclientprocessesitbeforeany //subsequentRST. // //Seehttps://golang.org/issue/3595 func(c*conn)closeWriteAndWait(){ //在关闭写之前将缓冲区中的数据发送出去 c.finalFlush() iftcp,ok:=c.rwc.(closeWriter);ok{ //执行tcpsock.go中的TCPConn.CloseWrite,调用SHUT_WR关闭写 tcp.CloseWrite() } time.Sleep(rstAvoidanceDelay) } func(c*conn)finalFlush(){ //本函数中如果c.bufr或c.bufw不为空,都会重置并重用这部分内存 ifc.bufr!=nil{ //Stealthebufio.Reader(~4KBworthofmemory)anditsassociated //readerforafutureconnection. putBufioReader(c.bufr) c.bufr=nil } ifc.bufw!=nil{ //将缓存区中的数据全部通过底层发送出去 //respose写数据调用为c.bufw.wr.Write->checkConnErrorWriter.write->c.rwc.write,最终通过底层write发送数据 c.bufw.Flush() //Stealthebufio.Writer(~4KBworthofmemory)anditsassociated //writerforafutureconnection. putBufioWriter(c.bufw) c.bufw=nil } }

http.transport

参见详解transport

NetPoll

参见详解golangnet之netpoll

参考:

https://golang.org/pkg/net/http/

https://lanre.wtf/blog/2017/07/24/roundtripper-go/

https://lanre.wtf/blog/2017/04/03/http-in-go/

本文内容总结:

原文链接:https://www.cnblogs.com/charlieroro/p/11331331.html