golang net之http server
本文内容纲要:
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.Handle和http.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函数进行处理,如下:
/*
<HTTP/1.1301MovedPermanently
<Content-Type:text/html;charset=utf-8
<Location:/test/
<Date:Fri,06Dec201903:35:59GMT
<Content-Length:41
<
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,soit'snot
//possibletomodifytheconfigurationwithmethodslike
//tls.Config.SetSessionTicketKeys.Touse
//SetSessionTicketKeys,useServer.ServewithaTLSListener
//instead.
TLSConfig*tls.Config
//ReadTimeoutisthemaximumdurationforreadingtheentire
//request,includingthebody.
//
//BecauseReadTimeoutdoesnotletHandlersmakeper-request
//decisionsoneachrequestbody'sacceptabledeadlineor
//uploadrate,mostuserswillprefertouse
//ReadHeaderTimeout.Itisvalidtousethemboth.
ReadTimeouttime.Duration
//ReadHeaderTimeoutistheamountoftimeallowedtoread
//requestheaders.Theconnection'sreaddeadlineisreset
//afterreadingtheheadersandtheHandlercandecidewhat
//isconsideredtooslowforthebody.IfReadHeaderTimeout
//iszero,thevalueofReadTimeoutisused.Ifbothare
//zero,thereisnotimeout.
ReadHeaderTimeouttime.Duration
//WriteTimeoutisthemaximumdurationbeforetimingout
//writesoftheresponse.Itisresetwheneveranew
//request'sheaderisread.LikeReadTimeout,itdoesnot
//letHandlersmakedecisionsonaper-requestbasis.
WriteTimeouttime.Duration
//IdleTimeoutisthemaximumamountoftimetowaitforthe
//nextrequestwhenkeep-alivesareenabled.IfIdleTimeout
//iszero,thevalueofReadTimeoutisused.Ifbothare
//zero,thereisnotimeout.
IdleTimeouttime.Duration
//MaxHeaderBytescontrolsthemaximumnumberofbytesthe
//serverwillreadparsingtherequestheader'skeysand
//values,includingtherequestline.Itdoesnotlimitthe
//sizeoftherequestbody.
//Ifzero,DefaultMaxHeaderBytesisused.
MaxHeaderBytesint
//TLSNextProtooptionallyspecifiesafunctiontotakeover
//ownershipoftheprovidedTLSconnectionwhenanNPN/ALPN
//protocolupgradehasoccurred.Themapkeyistheprotocol
//namenegotiated.TheHandlerargumentshouldbeusedto
//handleHTTPrequestsandwillinitializetheRequest'sTLS
//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,loggingisdoneviathelogpackage'sstandardlogger.
ErrorLog*log.Logger
//BaseContextoptionallyspecifiesafunctionthatreturns
//thebasecontextforincomingrequestsonthisserver.
//TheprovidedListeneristhespecificListenerthat's
//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-zeromeanswe'reinShutdown)
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
//insidetheListener'sAcceptgoroutine,assomeimplementationsblock.
//Itispopulatedimmediatelyinsidethe(*conn).servegoroutine.
//ThisisthevalueofaHandler's(*Request).RemoteAddr.
remoteAddrstring
//tlsStateistheTLSconnectionstatewhenusingTLS.
//nilmeansnotTLS.
tlsState*tls.ConnectionState
//werrissettothefirstwriteerrortorwc.
//ItissetviacheckConnErrorWriter{w},wherebufwwrites.
werrerror
//risbufr'sreadsource.It'sawrapperaroundrwcthatprovides
//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,assumethey'respeakingplaintextHTTPandwritea
//400responseontheTLSconn'sunderlyingnet.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,we'reactive.
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
//abletoreadthisifwe're
//respondingtothemandhangingup
//whilethey'restillwritingtheir
//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
//Wepurposefullyaren'techoingbackthetransfer-encoding'svalue,
//soastomitigatetheriskofcrosssidescriptingbyanattacker.
fmt.Fprintf(c.rwc,"HTTP/1.1%d%s%sUnsupportedtransferencoding",code,StatusText(code),errorHeaders)
return
caseisCommonNetReadError(err):
return//don'treply
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,itcan'treadanother,
//sowemightaswellrunthehandlerinthisgoroutine.
//[*]Notstrictlytrue:HTTPpipelining.Wecouldletthemallprocess
//inparalleleveniftheirresponsesneedtobeserialized.
//Butwe'renotgoingtoimplementHTTPpipeliningbecauseit
//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(){
//We'reinshutdownmode.Wemight'vereplied
//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),
//Wepopulatetheseaheadoftimesowe'renot
//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(){
//Becauseit'sneitherchunked,nordeclared:
req.ContentLength=-1
//Wewanttogivehandlersachancetohijackthe
//connection,butweneedtopreventtheServerfrom
//dealingwiththeconnectionfurtherifit'snot
//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),don'tallowaContent-Lengthheaderfor
//methodswhichdon'tpermitbodies.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)
//ifthere'snomentionofabody.
//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
//Wewerepasttheendofthepreviousrequest'sbodyalready
//(sincewewouldn'tbeinabackgroundreadotherwise),so
//thisisapipelinedHTTPrequest.PriortoGo1.11weusedto
//sendontheCloseNotifychannelandcancelthecontexthere,
//butthebehaviorwasdocumentedasonly"may",andweonly
//didthatbecausethat'showCloseNotifyaccidentallybehaved
//inveryearlyGoreleasespriortocontextsupport.Oncewe
//addedcontextsupport,peopleusedaHandler's
//Request.Context()andpasseditalong.Havingthatcontext
//cancelonpipelinedHTTPrequestscausedproblems.
//Fortunately,almostnothingusesHTTP/1.xpipelining.
//Unfortunately,apt-getdoes,orsometimesdoes.
//NewGo1.11behavior:don'tfireCloseNotifyorcancel
//contextsonpipelinedrequests.Shouldn'taffectpeople,but
//fixescaseslikeIssue23921.Thisdoesmeanthataclient
//closingtheirTCPconnectionaftersendingapipelined
//requestwon'tcancelthecontext,butwe'llcatchthatonany
//writefailure(incheckConnErrorWriter.Write).
//Iftheserverneverwrites,yes,therearestillcontrived
//server&clientbehaviorswherethisfailstoevercancelthe
//context,butthat'skindawhyHTTP/1.xpipeliningdied
//anyway.
}
ifne,ok:=err.(net.Error);ok&&cr.aborted&&ne.Timeout(){
//Ignorethiserror.It'stheexpectederrorfrom
//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
//handlerindicatedweshouldn'treusethis
//connection.
returnfalse
}
//写入数据与"content-length"不匹配,为避免不同步,不重用连接
ifw.req.Method!="HEAD"&&w.contentLength!=-1&&w.bodyAllowed()&&w.contentLength!=w.written{
//Didnotwriteenough.Avoidgettingoutofsync.
returnfalse
}
//Therewassomeerrorwritingtotheunderlyingconnection
//duringtherequest,sodon'tre-usethisconn.
//底层连接出现错误,不可重用
ifw.conn.werr!=nil{
returnfalse
}
//判断是否在读取完数据前执行关闭
ifw.closedRequestBodyEarly(){
returnfalse
}
returntrue
}
//closeWriteflushesanyoutstandingdataandsendsaFINpacket(if
//clientisconnectedviaTCP),signallingthatwe'redone.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