golang Http Request

前端之家收集整理的这篇文章主要介绍了golang Http Request前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

一起看一下golang的HTTP包怎么write Request信息

先看一下看golang http Request的struct,不解释,慢慢看(HTTP权威指南,RFC文档)

typeRequeststruct{
	//MethodspecifiestheHTTPmethod(GET,POST,PUT,etc.).
	//ForclientrequestsanemptystringmeansGET.
	Methodstring

	//URLspecifieseithertheURIbeingrequested(forserver
	//requests)ortheURLtoaccess(forclientrequests).
	//
	//ForserverrequeststheURLisparsedfromtheURI
	//suppliedontheRequest-LineasstoredinRequestURI.For
	//mostrequests,fieldsotherthanPathandRawQuerywillbe
	//empty.(SeeRFC2616,Section5.1.2)
	//
	//Forclientrequests,theURL'sHostspecifiestheserverto
	//connectto,whiletheRequest'sHostfieldoptionally
	//specifiestheHostheadervaluetosendintheHTTP
	//request.
	URL*url.URL

	//Theprotocolversionforincomingrequests.
	//ClientrequestsalwaysuseHTTP/1.1.
	Protostring//"HTTP/1.0"
	ProtoMajorint//1
	ProtoMinorint//0

	//Aheadermapsrequestlinestotheirvalues.
	//Iftheheadersays
	//
	//	accept-encoding:gzip,deflate
	//	Accept-Language:en-us
	//	Connection:keep-alive
	//
	//then
	//
	//	Header=map[string][]string{
	//		"Accept-Encoding":{"gzip,deflate"},//		"Accept-Language":{"en-us"},//		"Connection":{"keep-alive"},//	}
	//
	//HTTPdefinesthatheadernamesarecase-insensitive.
	//Therequestparserimplementsthisbycanonicalizingthe
	//name,makingthefirstcharacterandanycharacters
	//followingahyphenuppercaseandtherestlowercase.
	//
	//Forclientrequestscertainheadersareautomatically
	//addedandmayoverridevaluesinHeader.
	//
	//SeethedocumentationfortheRequest.Writemethod.
	HeaderHeader

	//Bodyistherequest'sbody.
	//
	//Forclientrequestsanilbodymeanstherequesthasno
	//body,suchasaGETrequest.TheHTTPClient'sTransport
	//isresponsibleforcallingtheClosemethod.
	//
	//ForserverrequeststheRequestBodyisalwaysnon-nil
	//butwillreturnEOFimmediatelywhennobodyispresent.
	//TheServerwillclosetherequestbody.TheServeHTTP
	//Handlerdoesnotneedto.
	Bodyio.ReadCloser

	//ContentLengthrecordsthelengthoftheassociatedcontent.
	//Thevalue-1indicatesthatthelengthisunknown.
	//Values>=0indicatethatthegivennumberofbytesmay
	//bereadfromBody.
	//Forclientrequests,avalueof0meansunknownifBodyisnotnil.
	ContentLengthint64

	//TransferEncodingliststhetransferencodingsfromoutermostto
	//innermost.Anemptylistdenotesthe"identity"encoding.
	//TransferEncodingcanusuallybeignored;chunkedencodingis
	//automaticallyaddedandremovedasnecessarywhensendingand
	//receivingrequests.
	TransferEncoding[]string

	//Closeindicateswhethertoclosetheconnectionafter
	//replyingtothisrequest(forservers)oraftersending
	//therequest(forclients).
	Closebool

	//ForserverrequestsHostspecifiesthehostonwhichthe
	//URLissought.PerRFC2616,thisiseitherthevalueof
	//the"Host"headerorthehostnamegivenintheURLitself.
	//Itmaybeoftheform"host:port".
	//
	//ForclientrequestsHostoptionallyoverridestheHost
	//headertosend.Ifempty,theRequest.Writemethoduses
	//thevalueofURL.Host.
	Hoststring

	//Formcontainstheparsedformdata,includingboththeURL
	//field'squeryparametersandthePOSTorPUTformdata.
	//ThisfieldisonlyavailableafterParseFormiscalled.
	//TheHTTPclientignoresFormandusesBodyinstead.
	Formurl.Values

	//PostFormcontainstheparsedformdatafromPOSTorPUT
	//bodyparameters.
	//ThisfieldisonlyavailableafterParseFormiscalled.
	//TheHTTPclientignoresPostFormandusesBodyinstead.
	PostFormurl.Values

	//MultipartFormistheparsedmultipartform,includingfileuploads.
	//ThisfieldisonlyavailableafterParseMultipartFormiscalled.
	//TheHTTPclientignoresMultipartFormandusesBodyinstead.
	MultipartForm*multipart.Form

	//Trailerspecifiesadditionalheadersthataresentaftertherequest
	//body.
	//
	//ForserverrequeststheTrailermapinitiallycontainsonlythe
	//trailerkeys,withnilvalues.(Theclientdeclareswhichtrailersit
	//willlatersend.)WhilethehandlerisreadingfromBody,itmust
	//notreferenceTrailer.AfterreadingfromBodyreturnSEOF,Trailer
	//canbereadagainandwillcontainnon-nilvalues,iftheyweresent
	//bytheclient.
	//
	//ForclientrequestsTrailermustbeinitializedtoamapcontaining
	//thetrailerkeystolatersend.Thevaluesmaybenilortheirfinal
	//values.TheContentLengthmustbe0or-1,tosendachunkedrequest.
	//AftertheHTTPrequestissentthemapvaluescanbeupdatedwhile
	//therequestbodyisread.OncethebodyreturnSEOF,thecallermust
	//notmutateTrailer.
	//
	//FewHTTPclients,servers,orproxiessupportHTTPtrailers.
	TrailerHeader

	//RemoteAddrallowsHTTPserversandothersoftwaretorecord
	//thenetworkaddressthatsenttherequest,usuallyfor
	//logging.ThisfieldisnotfilledinbyReadRequestand
	//hasnodefinedformat.TheHTTPserverinthispackage
	//setsRemoteAddrtoan"IP:port"addressbeforeinvokinga
	//handler.
	//ThisfieldisignoredbytheHTTPclient.
	RemoteAddrstring

	//RequestURIistheunmodifiedRequest-URIofthe
	//Request-Line(RFC2616,Section5.1)assentbytheclient
	//toaserver.UsuallytheURLfieldshouldbeusedinstead.
	//ItisanerrortosetthisfieldinanHTTPclientrequest.
	RequestURIstring

	//TLSallowsHTTPserversandothersoftwaretorecord
	//informationabouttheTLSconnectiononwhichtherequest
	//wasreceived.ThisfieldisnotfilledinbyReadRequest.
	//TheHTTPserverinthispackagesetsthefieldfor
	//TLS-enabledconnectionsbeforeinvokingahandler;
	//otherwiseitleavesthefieldnil.
	//ThisfieldisignoredbytheHTTPclient.
	TLS*tls.ConnectionState
}

再来具体分析一下http request write的具体执行流程

func(req*Request)write(wio.Writer,usingProxybool,extraHeadersHeader)error{
	host:=req.Host
	ifhost==""{
		ifreq.URL==nil{
			returnerrors.New("http:Request.WriteonRequestwithnoHostorURLset")
		}
		host=req.URL.Host
	}

	ruri:=req.URL.RequestURI()
	//代理模式的时候ruri需要加上协议名http/https等
	ifusingProxy&&req.URL.Scheme!=""&&req.URL.Opaque==""{
		ruri=req.URL.Scheme+"://"+host+ruri
	}elseifreq.Method=="CONNECT"&&req.URL.Path==""{
		//CONNECTrequestsnormallygivejustthehostandport,notafullURL.
		ruri=host
	}
	//TODO(bradfitz):escapeatleastnewlinesinruri?

	//WrapthewriterinabufioWriterifit'snotalreadybuffered.
	//Don'talwayscallNewWriter,asthatforcesabytes.Buffer
	//andothersmallbufioWriterstohaveaminimum4kbuffer
	//size.
	
	//创建一个Writer,往里面写内容
	varbw*bufio.Writer
	if_,ok:=w.(io.ByteWriter);!ok{
		bw=bufio.NewWriter(w)
		w=bw
	}

//写http最开始数据
	_,err:=fmt.Fprintf(w,"%s%sHTTP/1.1\r\n",valueOrDefault(req.Method,"GET"),ruri)
	iferr!=nil{
		returnerr
	}

	//Headerlines写Host内容
	_,err=fmt.Fprintf(w,"Host:%s\r\n",host)
	iferr!=nil{
		returnerr
	}

	//UsethedefaultUserAgentunlesstheHeadercontainsone,which
	//maybeblanktonotsendtheheader.
	
	//这东西的数据如下:
	/*constdefaultUserAgent="Go1.1packagehttp"*/
	
	userAgent:=defaultUserAgent
	ifreq.Header!=nil{
		ifua:=req.Header["User-Agent"];len(ua)>0{
			userAgent=ua[0]
		}
	}
	ifuserAgent!=""{
		_,"User-Agent:%s\r\n",userAgent)
		iferr!=nil{
			returnerr
		}
	}

	//ProcessBody,ContentLength,Close,Trailer
	//封装的transferWriter结构
	tw,err:=newTransferWriter(req)
	iferr!=nil{
		returnerr
	}
	err=tw.WriteHeader(w)
	iferr!=nil{
		returnerr
	}

	err=req.Header.WriteSubset(w,reqWriteExcludeHeader)
	iferr!=nil{
		returnerr
	}

	ifextraHeaders!=nil{
		err=extraHeaders.Write(w)
		iferr!=nil{
			returnerr
		}
	}

	_,err=io.WriteString(w,"\r\n")
	iferr!=nil{
		returnerr
	}

	//Writebodyandtrailer
	err=tw.WriteBody(w)
	iferr!=nil{
		returnerr
	}

	ifbw!=nil{
		returnbw.Flush()
	}
	returnnil
}

再来看看transferWriter结构相关的操作:

//主要用于写HTTP的Body,Trailer
typetransferWriterstruct{
	Methodstring
	Bodyio.Reader
	BodyCloserio.Closer
	ResponseToHEADbool
	ContentLengthint64//-1meansunknown,0meansexactlynone
	Closebool
	TransferEncoding[]string
	TrailerHeader
}

创建transferWriter的过程:

funcnewTransferWriter(rinterface{})(t*transferWriter,errerror){
	t=&transferWriter{}

	//Extractrelevantfields
	atLeastHTTP11:=false
	switchrr:=r.(type){
	case*Request:
		ifrr.ContentLength!=0&&rr.Body==nil{
			returnnil,fmt.Errorf("http:Request.ContentLength=%dwithnilBody",rr.ContentLength)
		}
		t.Method=rr.Method
		t.Body=rr.Body
		t.BodyCloser=rr.Body
		t.ContentLength=rr.ContentLength
		t.Close=rr.Close
		t.TransferEncoding=rr.TransferEncoding
		t.Trailer=rr.Trailer
		atLeastHTTP11=rr.ProtoAtLeast(1,1)
		ift.Body!=nil&&len(t.TransferEncoding)==0&&atLeastHTTP11{
			ift.ContentLength==0{
				//Testtoseeifit'sactuallyzeroorjustunset.
				varbuf[1]byte
				n,rerr:=io.ReadFull(t.Body,buf[:])
				ifrerr!=nil&&rerr!=io.EOF{
					t.ContentLength=-1
					t.Body=&errorReader{rerr}
				}elseifn==1{
					//Oh,guessthereisdatainthisBodyReaderafterall.
					//TheContentLengthfieldjustwasn'tset.
					//StichtheBodybacktogetheragain,re-attachingour
					//consumedbyte.
					t.ContentLength=-1
					t.Body=io.MultiReader(bytes.NewReader(buf[:]),t.Body)
				}else{
					//Bodyisactuallyempty.
					t.Body=nil
					t.BodyCloser=nil
				}
			}
			ift.ContentLength<0{
				t.TransferEncoding=[]string{"chunked"}
			}
		}
	case*Response:
		ifrr.Request!=nil{
			t.Method=rr.Request.Method
		}
		t.Body=rr.Body
		t.BodyCloser=rr.Body
		t.ContentLength=rr.ContentLength
		t.Close=rr.Close
		t.TransferEncoding=rr.TransferEncoding
		t.Trailer=rr.Trailer
		atLeastHTTP11=rr.ProtoAtLeast(1,1)
		t.ResponseToHEAD=noBodyExpected(t.Method)
	}

	//SanitizeBody,TransferEncoding
	ift.ResponseToHEAD{
		t.Body=nil
		ifchunked(t.TransferEncoding){
			t.ContentLength=-1
		}
	}else{
		if!atLeastHTTP11||t.Body==nil{
			t.TransferEncoding=nil
		}
		ifchunked(t.TransferEncoding){
			t.ContentLength=-1
		}elseift.Body==nil{//nochunking,nobody
			t.ContentLength=0
		}
	}

	//SanitizeTrailer
	if!chunked(t.TransferEncoding){
		t.Trailer=nil
	}

	returnt,nil
}

最后再看看WriteBody的操作:

func(t*transferWriter)WriteBody(wio.Writer)error{
	varerrerror
	varncopyint64

	//Writebody写body的操作在这里
	ift.Body!=nil{
		ifchunked(t.TransferEncoding){
			cw:=internal.NewChunkedWriter(w)
			_,err=io.Copy(cw,t.Body)
			iferr==nil{
				err=cw.Close()
			}
		}elseift.ContentLength==-1{
			ncopy,err=io.Copy(w,t.Body)
		}else{
			ncopy,io.LimitReader(t.Body,t.ContentLength))
			iferr!=nil{
				returnerr
			}
			varnextraint64
			nextra,err=io.Copy(IoUtil.Discard,t.Body)
			ncopy+=nextra
		}
		iferr!=nil{
			returnerr
		}
		iferr=t.BodyCloser.Close();err!=nil{
			returnerr
		}
	}

	if!t.ResponseToHEAD&&t.ContentLength!=-1&&t.ContentLength!=ncopy{
		returnfmt.Errorf("http:ContentLength=%dwithBodylength%d",t.ContentLength,ncopy)
	}

	//TODO(petar):Placetrailerwritercodehere.
	ifchunked(t.TransferEncoding){
		//WriteTrailerheader
		ift.Trailer!=nil{
			iferr:=t.Trailer.Write(w);err!=nil{
				returnerr
			}
		}
		//Lastchunk,emptytrailer
		_,"\r\n")
	}
	returnerr
}

自己实现HTTP服务器可以借鉴一下此处代码

原文链接:https://www.f2er.com/go/190094.html

猜你在找的Go相关文章