golang Http Request

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

一起看一下golang的HTTP包怎麼write Request資訊

先看一下看golang http Request的struct,不解釋,慢慢看(HTTP權威指南,RFC文檔)

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 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 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}

再來具體分析一下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()//代理模式的時候ruri需要加上協議名http/https等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.//建立一個Writer,往裡面寫內容var bw *bufio.Writerif _, ok := w.(io.ByteWriter); !ok {bw = bufio.NewWriter(w)w = bw}                //寫http最開始資料_, err := fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), ruri)if err != nil {return err}// Header lines 寫Host內容_, 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.//這東西的資料如下:/* 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}}// Process Body,ContentLength,Close,Trailer//封裝的transferWriter結構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}

再來看看transferWriter結構相關的操作:

//主要用於寫HTTP的Body,ContentLength,Close,Trailertype transferWriter struct {Method           string Body             io.ReaderBodyCloser       io.CloserResponseToHEAD   boolContentLength    int64 // -1 means unknown, 0 means exactly noneClose            boolTransferEncoding []stringTrailer          Header}

建立transferWriter的過程:

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}

最後再看看WriteBody的操作:

func (t *transferWriter) WriteBody(w io.Writer) error {var err errorvar ncopy int64// Write body 寫body的操作在這裡if t.Body != nil { if chunked(t.TransferEncoding) {cw := internal.NewChunkedWriter(w)_, err = io.Copy(cw, t.Body)if err == nil {err = 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}

自己實現HTTP伺服器可以借鑒一下此處代碼

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.