This is a creation in Article, where the information may have evolved or changed.
Take a look at Golang's HTTP packet. How to write Request information
Let's take a look at the struct of Golang HTTP request, do not explain, take a slow look (HTTP authoritative guide, RFC document)
type request struct {// method specifies the http method (GET, post, put, etc.). for client requests an empty string means get. method string// url specifies either the uri being requested (for server// requests) or the URL to access (for client Requests).//// for server requests the url is parsed from the uri// supplied on the request-line as stored in requesturi. for// most requests, fields other than path and rawquery will be// empty. (see rfc 2616, section 5.1.2)//// for Client requests, the url ' S host specifies the server to// connect to, while&nbSp;the request ' S host field optionally// specifies the host header value to send in the http// request. Url *url. Url// the protocol version for incoming requests.// client requests always use HTTP/1.1.Proto string // "http/1.0" protomajor int // 1protominor int // 0// A header maps request lines to their values.// If the header says////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 "},//}//// http Defines that header names are case-insensitive.// the request parser implements this by canonicalizing the// name, making the first character and any characters// following a hyphen uppercase and the rest lowercase.//// for Client requests certain headers are automatically// added and may override values in header.//// see the documentation for the request.write method. Header header// body is the request ' s body.//// for client Requests a nil body means the request has no// body, such as a get request. the http client ' s transport// is Responsible for calling the close method.//// for server requests the request body is always non-nil// but will return eof immediately when no body is present.// The Server will close the Request body. the servehttp// handler does not need to. Body io. readcloser// contentlength records the length of the associated content.// the value -1 indicates that the length is unknown.// values >= 0 indicate that the given number of bytes may// be read from body.// for client requests, a value of 0 means unknown if body is not nil. Contentlength int64// transferencoding lists the transfer encodings from outermost to// innermost. an empty list denotes the "Identity" encoding.// transferencoding can usually be ignored; chunked encoding is// automatically added and Removed as necessary when sending and// receiving requests. transferencoding []string// close indicates whether to close the connection after// replying to this request (for servers) or after sending// the request (for clients). close bool// for server requests host specifies the host on Which the// url is sought. per rfc 2616, this is either the value of// the "Host" header or the host name Given in the url itself.// it may be of the form "Host:port".//// for client requests host optionally overrides the Host// header to send. If empty, the Request.Write method Uses// the value of url. host.host string// form contains the parsed form data, including Both the url// field ' S query parameters and the post or put form data.// This field is only available after ParseForm is called.// the http client ignores form and uses body instead. Form url. Values// postform contains the parsed form data from post or put// body parameters.// this field is only available after parseform is called.// the http client Ignores postform and uses body instead. Postform url. Values// multipartform is the parsed multipart form, including file uploads.// this field is only available after parsemultipartform is called.// the http client ignores multipartform and uses body instead. Multipartform *multipart. form// trailer specifies additional headers that are sent after the request// body.//// for server requests the trailer map initially contains only the// trailer keys, with nil values. (The client declares which trailers it// will later send.) while the handler is reading from body, it must// not reference&nbSp Trailer. after reading from body returns eof, trailer// can be read again and will contain non-nil values, if they were sent// by the client.//// For client requests Trailer must be initialized to a map containing// the trailer keys to Later send. the values may be nil or their final// values. the contentlength must be 0 or -1, to send a chunked request.// after the http request is sent the map values can be updated while// the request body is read. Once the body returns eof, the caller must// not mutate trailer.//// Few HTTP Clients, servers, or proxies support http trailers. Trailer header// remoteaddr allows http servers and other software to record// the network address that sent the request, usually for// logging. this field is not filled in by readrequest and// has no defined format. the http server in this package// sets remoteaddr to an "Ip:port" address before invoking a// handler.// this field is ignored by the http Client. remoteaddr string// requesturi is the unmodified request-uri of the// Request-Line (rfc 2616, section 5.1) as sent by the client/ / to a server. usually the  url field should be used instead.// it is an error to Set this field in an http client request. Requesturi string// tls allows http servers and other software to record// information about the TLS connection on which the request// was received. this field is not filled in by Readrequest.// the http server in this package sets the field for// tls-enabled connections before invoking a handler;// otherwise it leaves the field nil.// This field is ignored by The http client. Tls *tls. ConnectionState}
to specifically analyze the specific execution flow of HTTP request write
func (req *request) write (W io. Writer, usingproxy bool, extraheaders header) error {host := req. hostif host == "" {if req. Url == nil {return errors. New ("Http: request.write on request with no host or url set")} Host = req. Url. Host}ruri := req. Url. RequestUri ()//proxy mode when Ruri need to add protocol name Http/https such as If usingproxy && req. Url. scheme != "" && req. Url. opaque == "" {ruri = req. Url. scheme + "://" + host + ruri} else if req. method == "CONNECT" && req. Url. path == "" {// connect requests normally give just the host and port, not a full url.ruri = host}// todo (Bradfitz): Escape at least newlines in ruri?// Wrap the writer in a bufio Writer If it ' S not already buffered.// don ' T always call newwriter, as that forces a bytes. buffer// and other small bufio writers to have a minimum 4k buffer// size.//creates a writer that writes content Var bw *bufio. Writerif _, ok := w. (IO. Bytewriter); !ok {bw = bufio. Newwriter (W) w = bw} //writes the HTTP start data _, err := fmt. fprintf (w, "%s %s http/1.1\r\n", valueordefault (req. method, "GET"), ruri) if err != nil {return err}// header lines Write host content _, err = fmt. fprintf (w, "host: %s\r\n", host) If err != nil {return err}// use the defaultuseragent unless the header contains one, which// may be blank to not send the header.//The data for this thing are as follows:/* const defaultUserAgent = "Go 1.1 package http" */useragent := defaultuseragentif req. Header != nil {if ua := req. header["User-agent"]; len (UA) > 0 {userAgent = ua[0]}}if userAgent != "" {_, err = fmt. fprintf (w, "user-agent: %s\r\n", useragent) If err != nil {return err}} The transferwriter structure of the process body,contentlength,close,trailer//package tw, err := Newtransferwriter (req) if err != nil {return err}err = tw. Writeheader (W) if err != nil {return err}err = req. Header.writesubset (W, reqwriteexcludeheAder) if err != nil {return err}if extraheaders != nil {err = extraheaders.write (W) if err != nil {return err}}_, err = io. WriteString (w, "\ r \ n") If err != nil {return err}// write body and trailererr = tw. Writebody (W) if err != nil {return err}if bw != nil {return Bw. Flush ()}return nil}
Let's take a look at the Transferwriter structure-related operations:
The Body,contentlength,close,trailertype transferwriter struct {Method string Body io that is used primarily for writing HTTP. Readerbodycloser io. Closerresponsetohead boolcontentlength Int64//-1 means unknown, 0 means exactly noneclose booltransferen coding []stringtrailer Header}
To create a transferwriter process:
Func newtransferwriter (r interface{}) (T *transferwriter, err error) {t = &transferWriter{}// Extract relevant fieldsatLeastHTTP11 := Falseswitch rr := r. (type) {case *request:if rr. Contentlength != 0 && rr. Body == nil {return nil, fmt. Errorf ("Http: request.contentlength=%d with nil body", rr. ContentLength)}t.method = rr. Methodt.body = rr. Bodyt.bodycloser = rr. Bodyt.contentlength = rr. Contentlengtht.close = rr. Closet.transferencoding = rr. Transferencodingt.trailer = rr. Traileratleasthttp11 = rr. Protoatleast (1, 1) If t.body != nil && len (t.TransferEncoding) == 0 && atleasthttp11 {if t.contentlength == 0 {// test to see if it ' s actually zero or just unset.var buf [1]byten, rerr := io. Readfull (t.body, buf[:]) If rerr != nil && rerr != io. Eof {t.contentlength = -1t. Body = &errorreader{rerr}} else if n == 1 {// oh, guess there is data in this Body Reader after all.// The Contentlength field just wasn ' t set.// stich the body back Together again, re-attaching our// consumed byte.t.contentlength = -1t. Body = io. Multireader (bytes. Newreader (buf[:]), t.body)} else {// body is actually empty.t.body = nilt. Bodycloser = nil}}if t.contentlength < 0 {t.transferencoding = [] string{"chunked"}}}case *response:if rR.request != nil {t.method = rr. Request.method}t.body = rr. Bodyt.bodycloser = rr. Bodyt.contentlength = rr. Contentlengtht.close = rr. Closet.transferencoding = rr. Transferencodingt.trailer = rr. Traileratleasthttp11 = rr. Protoatleast (1, 1) t.responsetohead = nobodyexpected (T.method)}// sanitize body, Contentlength,transferencodingif t.responsetohead {t.body = nilif chunked (T. transferencoding) {t.contentlength = -1}} else {if !atleasthttp11 | | t.body == nil {t.transferencoding = nil}if chunked (t.TransferEncoding) {t.contentlength = -1} else if t.body == nil { // no chunking, no bodyt. Contentlength = 0}}// sanitize trailerif !chunked (t.transferencoding) {t.Trailer = nil}retUrn t, nil}
Finally, take a look at Writebody's operation:
func (T *transferwriter) writebody (W io. Writer) error {var err errorvar ncopy int64// Write body Write Body operation here if t.body != nil { if chunked (t.transferencoding) {cw := internal. Newchunkedwriter (W) _, err = io. Copy (cw, t.body) IF&NBSP;ERR&NBSP;==&NBSP;NIL&NBSP;{ERR&NBSP;=&NBSP;CW. Close ()}} else if t.contentlength == -1 {ncopy, err = io. Copy (w, t.body)} else {ncopy, err = io. Copy (W, io. Limitreader (t.body, t.contentlength)) If err != nil {return err}var nextra int64nextra, err = io. Copy (Ioutil. Discard, t.body) Ncopy += nextra}if err != nil {return err}if err = t.bodycloser.close (); err != nil {return err}}if !t.responsetohead && t.conTentlength != -1 && t.contentlength != ncopy {return fmt. Errorf ("http: contentlength=%d with body length %d", T.contentlength, ncopy)}// todo (Petar): place trailer writer code here.if chunked (t.TransferEncoding) {// write trailer headerif t.trailer != nil {if err := t.trailer.write (w); err != nil {return err}}// last chunk, empty trailer_, err = io. WriteString (w, "\ r \ n")}return err}
implementing the HTTP server itself can be used for reference here code