Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(576)

Delta Between Two Patch Sets: src/pkg/net/http/request.go

Issue 76540043: code review 76540043: net/http: add BasicAuth method to *http.Request
Left Patch Set: diff -r cfbe0887d23b https://code.google.com/p/go Created 10 years ago
Right Patch Set: diff -r e5c87cefb57fffc5767209542be5c4e58123e3c6 https://code.google.com/p/go Created 9 years, 7 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | src/pkg/net/http/request_test.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 // Copyright 2009 The Go Authors. All rights reserved. 1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 // HTTP Request reading and parsing. 5 // HTTP Request reading and parsing.
6 6
7 package http 7 package http
8 8
9 import ( 9 import (
10 "bufio" 10 "bufio"
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
63 var reqWriteExcludeHeader = map[string]bool{ 63 var reqWriteExcludeHeader = map[string]bool{
64 "Host": true, // not in Header map anyway 64 "Host": true, // not in Header map anyway
65 "User-Agent": true, 65 "User-Agent": true,
66 "Content-Length": true, 66 "Content-Length": true,
67 "Transfer-Encoding": true, 67 "Transfer-Encoding": true,
68 "Trailer": true, 68 "Trailer": true,
69 } 69 }
70 70
71 // A Request represents an HTTP request received by a server 71 // A Request represents an HTTP request received by a server
72 // or to be sent by a client. 72 // or to be sent by a client.
73 //
74 // The field semantics differ slightly between client and server
75 // usage. In addition to the notes on the fields below, see the
76 // documentation for Request.Write and RoundTripper.
73 type Request struct { 77 type Request struct {
74 » Method string // GET, POST, PUT, etc. 78 » // Method specifies the HTTP method (GET, POST, PUT, etc.).
75 79 » // For client requests an empty string means GET.
76 » // URL is created from the URI supplied on the Request-Line 80 » Method string
77 » // as stored in RequestURI. 81
78 » // 82 » // URL specifies either the URI being requested (for server
79 » // For most requests, fields other than Path and RawQuery 83 » // requests) or the URL to access (for client requests).
80 » // will be empty. (See RFC 2616, Section 5.1.2) 84 » //
85 » // For server requests the URL is parsed from the URI
86 » // supplied on the Request-Line as stored in RequestURI. For
87 » // most requests, fields other than Path and RawQuery will be
88 » // empty. (See RFC 2616, Section 5.1.2)
89 » //
90 » // For client requests, the URL's Host specifies the server to
91 » // connect to, while the Request's Host field optionally
92 » // specifies the Host header value to send in the HTTP
93 » // request.
81 URL *url.URL 94 URL *url.URL
82 95
83 // The protocol version for incoming requests. 96 // The protocol version for incoming requests.
84 » // Outgoing requests always use HTTP/1.1. 97 » // Client requests always use HTTP/1.1.
85 Proto string // "HTTP/1.0" 98 Proto string // "HTTP/1.0"
86 ProtoMajor int // 1 99 ProtoMajor int // 1
87 ProtoMinor int // 0 100 ProtoMinor int // 0
88 101
89 // A header maps request lines to their values. 102 // A header maps request lines to their values.
90 // If the header says 103 // If the header says
91 // 104 //
92 // accept-encoding: gzip, deflate 105 // accept-encoding: gzip, deflate
93 // Accept-Language: en-us 106 // Accept-Language: en-us
94 // Connection: keep-alive 107 // Connection: keep-alive
95 // 108 //
96 // then 109 // then
97 // 110 //
98 // Header = map[string][]string{ 111 // Header = map[string][]string{
99 // "Accept-Encoding": {"gzip, deflate"}, 112 // "Accept-Encoding": {"gzip, deflate"},
100 // "Accept-Language": {"en-us"}, 113 // "Accept-Language": {"en-us"},
101 // "Connection": {"keep-alive"}, 114 // "Connection": {"keep-alive"},
102 // } 115 // }
103 // 116 //
104 // HTTP defines that header names are case-insensitive. 117 // HTTP defines that header names are case-insensitive.
105 // The request parser implements this by canonicalizing the 118 // The request parser implements this by canonicalizing the
106 // name, making the first character and any characters 119 // name, making the first character and any characters
107 // following a hyphen uppercase and the rest lowercase. 120 // following a hyphen uppercase and the rest lowercase.
121 //
122 // For client requests certain headers are automatically
123 // added and may override values in Header.
124 //
125 // See the documentation for the Request.Write method.
108 Header Header 126 Header Header
109 127
110 // Body is the request's body. 128 // Body is the request's body.
111 // 129 //
112 » // For client requests, a nil body means the request has no 130 » // For client requests a nil body means the request has no
113 // body, such as a GET request. The HTTP Client's Transport 131 // body, such as a GET request. The HTTP Client's Transport
114 // is responsible for calling the Close method. 132 // is responsible for calling the Close method.
115 // 133 //
116 » // For server requests, the Request Body is always non-nil 134 » // For server requests the Request Body is always non-nil
117 // but will return EOF immediately when no body is present. 135 // but will return EOF immediately when no body is present.
118 // The Server will close the request body. The ServeHTTP 136 // The Server will close the request body. The ServeHTTP
119 // Handler does not need to. 137 // Handler does not need to.
120 Body io.ReadCloser 138 Body io.ReadCloser
121 139
122 // ContentLength records the length of the associated content. 140 // ContentLength records the length of the associated content.
123 // The value -1 indicates that the length is unknown. 141 // The value -1 indicates that the length is unknown.
124 // Values >= 0 indicate that the given number of bytes may 142 // Values >= 0 indicate that the given number of bytes may
125 // be read from Body. 143 // be read from Body.
126 » // For outgoing requests, a value of 0 means unknown if Body is not nil. 144 » // For client requests, a value of 0 means unknown if Body is not nil.
127 ContentLength int64 145 ContentLength int64
128 146
129 // TransferEncoding lists the transfer encodings from outermost to 147 // TransferEncoding lists the transfer encodings from outermost to
130 // innermost. An empty list denotes the "identity" encoding. 148 // innermost. An empty list denotes the "identity" encoding.
131 // TransferEncoding can usually be ignored; chunked encoding is 149 // TransferEncoding can usually be ignored; chunked encoding is
132 // automatically added and removed as necessary when sending and 150 // automatically added and removed as necessary when sending and
133 // receiving requests. 151 // receiving requests.
134 TransferEncoding []string 152 TransferEncoding []string
135 153
136 // Close indicates whether to close the connection after 154 // Close indicates whether to close the connection after
137 » // replying to this request. 155 » // replying to this request (for servers) or after sending
156 » // the request (for clients).
138 Close bool 157 Close bool
139 158
140 » // The host on which the URL is sought. 159 » // For server requests Host specifies the host on which the
141 » // Per RFC 2616, this is either the value of the Host: header 160 » // URL is sought. Per RFC 2616, this is either the value of
142 » // or the host name given in the URL itself. 161 » // the "Host" header or the host name given in the URL itself.
143 // It may be of the form "host:port". 162 // It may be of the form "host:port".
163 //
164 // For client requests Host optionally overrides the Host
165 // header to send. If empty, the Request.Write method uses
166 // the value of URL.Host.
144 Host string 167 Host string
145 168
146 // Form contains the parsed form data, including both the URL 169 // Form contains the parsed form data, including both the URL
147 // field's query parameters and the POST or PUT form data. 170 // field's query parameters and the POST or PUT form data.
148 // This field is only available after ParseForm is called. 171 // This field is only available after ParseForm is called.
149 // The HTTP client ignores Form and uses Body instead. 172 // The HTTP client ignores Form and uses Body instead.
150 Form url.Values 173 Form url.Values
151 174
152 // PostForm contains the parsed form data from POST or PUT 175 // PostForm contains the parsed form data from POST or PUT
153 // body parameters. 176 // body parameters.
154 // This field is only available after ParseForm is called. 177 // This field is only available after ParseForm is called.
155 // The HTTP client ignores PostForm and uses Body instead. 178 // The HTTP client ignores PostForm and uses Body instead.
156 PostForm url.Values 179 PostForm url.Values
157 180
158 // MultipartForm is the parsed multipart form, including file uploads. 181 // MultipartForm is the parsed multipart form, including file uploads.
159 // This field is only available after ParseMultipartForm is called. 182 // This field is only available after ParseMultipartForm is called.
160 // The HTTP client ignores MultipartForm and uses Body instead. 183 // The HTTP client ignores MultipartForm and uses Body instead.
161 MultipartForm *multipart.Form 184 MultipartForm *multipart.Form
162 185
163 » // Trailer maps trailer keys to values. Like for Header, if the 186 » // Trailer specifies additional headers that are sent after the request
164 » // response has multiple trailer lines with the same key, they will be 187 » // body.
165 » // concatenated, delimited by commas. 188 » //
166 » // For server requests, Trailer is only populated after Body has been 189 » // For server requests the Trailer map initially contains only the
167 » // closed or fully consumed. 190 » // trailer keys, with nil values. (The client declares which trailers it
168 » // Trailer support is only partially complete. 191 » // will later send.) While the handler is reading from Body, it must
192 » // not reference Trailer. After reading from Body returns EOF, Trailer
193 » // can be read again and will contain non-nil values, if they were sent
194 » // by the client.
195 » //
196 » // For client requests Trailer must be initialized to a map containing
197 » // the trailer keys to later send. The values may be nil or their final
198 » // values. The ContentLength must be 0 or -1, to send a chunked request.
199 » // After the HTTP request is sent the map values can be updated while
200 » // the request body is read. Once the body returns EOF, the caller must
201 » // not mutate Trailer.
202 » //
203 » // Few HTTP clients, servers, or proxies support HTTP trailers.
169 Trailer Header 204 Trailer Header
170 205
171 // RemoteAddr allows HTTP servers and other software to record 206 // RemoteAddr allows HTTP servers and other software to record
172 // the network address that sent the request, usually for 207 // the network address that sent the request, usually for
173 // logging. This field is not filled in by ReadRequest and 208 // logging. This field is not filled in by ReadRequest and
174 // has no defined format. The HTTP server in this package 209 // has no defined format. The HTTP server in this package
175 // sets RemoteAddr to an "IP:port" address before invoking a 210 // sets RemoteAddr to an "IP:port" address before invoking a
176 // handler. 211 // handler.
177 // This field is ignored by the HTTP client. 212 // This field is ignored by the HTTP client.
178 RemoteAddr string 213 RemoteAddr string
(...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after
349 // Wrap the writer in a bufio Writer if it's not already buffered. 384 // Wrap the writer in a bufio Writer if it's not already buffered.
350 // Don't always call NewWriter, as that forces a bytes.Buffer 385 // Don't always call NewWriter, as that forces a bytes.Buffer
351 // and other small bufio Writers to have a minimum 4k buffer 386 // and other small bufio Writers to have a minimum 4k buffer
352 // size. 387 // size.
353 var bw *bufio.Writer 388 var bw *bufio.Writer
354 if _, ok := w.(io.ByteWriter); !ok { 389 if _, ok := w.(io.ByteWriter); !ok {
355 bw = bufio.NewWriter(w) 390 bw = bufio.NewWriter(w)
356 w = bw 391 w = bw
357 } 392 }
358 393
359 » fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), ruri) 394 » _, err := fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method , "GET"), ruri)
395 » if err != nil {
396 » » return err
397 » }
360 398
361 // Header lines 399 // Header lines
362 » fmt.Fprintf(w, "Host: %s\r\n", host) 400 » _, err = fmt.Fprintf(w, "Host: %s\r\n", host)
401 » if err != nil {
402 » » return err
403 » }
363 404
364 // Use the defaultUserAgent unless the Header contains one, which 405 // Use the defaultUserAgent unless the Header contains one, which
365 // may be blank to not send the header. 406 // may be blank to not send the header.
366 userAgent := defaultUserAgent 407 userAgent := defaultUserAgent
367 if req.Header != nil { 408 if req.Header != nil {
368 if ua := req.Header["User-Agent"]; len(ua) > 0 { 409 if ua := req.Header["User-Agent"]; len(ua) > 0 {
369 userAgent = ua[0] 410 userAgent = ua[0]
370 } 411 }
371 } 412 }
372 if userAgent != "" { 413 if userAgent != "" {
373 » » fmt.Fprintf(w, "User-Agent: %s\r\n", userAgent) 414 » » _, err = fmt.Fprintf(w, "User-Agent: %s\r\n", userAgent)
415 » » if err != nil {
416 » » » return err
417 » » }
374 } 418 }
375 419
376 // Process Body,ContentLength,Close,Trailer 420 // Process Body,ContentLength,Close,Trailer
377 tw, err := newTransferWriter(req) 421 tw, err := newTransferWriter(req)
378 if err != nil { 422 if err != nil {
379 return err 423 return err
380 } 424 }
381 err = tw.WriteHeader(w) 425 err = tw.WriteHeader(w)
382 if err != nil { 426 if err != nil {
383 return err 427 return err
384 } 428 }
385 429
386 // TODO: split long values? (If so, should share code with Conn.Write)
387 err = req.Header.WriteSubset(w, reqWriteExcludeHeader) 430 err = req.Header.WriteSubset(w, reqWriteExcludeHeader)
388 if err != nil { 431 if err != nil {
389 return err 432 return err
390 } 433 }
391 434
392 if extraHeaders != nil { 435 if extraHeaders != nil {
393 err = extraHeaders.Write(w) 436 err = extraHeaders.Write(w)
394 if err != nil { 437 if err != nil {
395 return err 438 return err
396 } 439 }
397 } 440 }
398 441
399 » io.WriteString(w, "\r\n") 442 » _, err = io.WriteString(w, "\r\n")
443 » if err != nil {
444 » » return err
445 » }
400 446
401 // Write body and trailer 447 // Write body and trailer
402 err = tw.WriteBody(w) 448 err = tw.WriteBody(w)
403 if err != nil { 449 if err != nil {
404 return err 450 return err
405 } 451 }
406 452
407 if bw != nil { 453 if bw != nil {
408 return bw.Flush() 454 return bw.Flush()
409 } 455 }
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
469 case *bytes.Reader: 515 case *bytes.Reader:
470 req.ContentLength = int64(v.Len()) 516 req.ContentLength = int64(v.Len())
471 case *strings.Reader: 517 case *strings.Reader:
472 req.ContentLength = int64(v.Len()) 518 req.ContentLength = int64(v.Len())
473 } 519 }
474 } 520 }
475 521
476 return req, nil 522 return req, nil
477 } 523 }
478 524
479 // BasicAuth returns the request's username and password if this is 525 // BasicAuth returns the username and password provided in the request's
josharian 2014/03/16 18:51:16 Maybe something like: // BasicAuth returns the us
480 // an authenticated request using the basic authentication scheme. 526 // Authorization header, if the request uses HTTP Basic Authentication.
481 // (See RFC 2617, Section 2) 527 // See RFC 2617, Section 2.
482 func (r *Request) BasicAuth() (username, password string, ok bool) { 528 func (r *Request) BasicAuth() (username, password string, ok bool) {
josharian 2014/03/16 18:51:16 The RFC uses "userid" instead of "username" in sec
483 » return parseBasicAuth(r.Header.Get("Authorization")) 529 » auth := r.Header.Get("Authorization")
josharian 2014/03/16 18:51:16 Why the indirection? I assumed that it was for eas
484 } 530 » if auth == "" {
485 531 » » return
486 // parseBasicAuth parses a HTTP Basic Authentication string. 532 » }
josharian 2014/03/16 18:51:16 s/a/an/
533 » return parseBasicAuth(auth)
534 }
535
536 // parseBasicAuth parses an HTTP Basic Authentication string.
487 // "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true) . 537 // "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true) .
488 func parseBasicAuth(auth string) (username, password string, ok bool) { 538 func parseBasicAuth(auth string) (username, password string, ok bool) {
489 » s1 := strings.SplitN(auth, " ", 2) 539 » if !strings.HasPrefix(auth, "Basic ") {
490 » if len(s1) != 2 || s1[0] != "Basic" { 540 » » return
491 » » return "", "", false 541 » }
492 » } 542 » c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(auth, "Basi c "))
493 » b, err := base64.StdEncoding.DecodeString(s1[1]) 543 » if err != nil {
494 » if err != nil { 544 » » return
495 » » return "", "", false 545 » }
496 » } 546 » cs := string(c)
497 » s2 := strings.SplitN(string(b), ":", 2) 547 » s := strings.IndexByte(cs, ':')
498 » if len(s2) != 2 { 548 » if s < 0 {
josharian 2014/03/16 18:51:16 What if the password contains ":"? The RFC only sp
499 » » return "", "", false 549 » » return
500 » } 550 » }
501 » return s2[0], s2[1], true 551 » return cs[:s], cs[s+1:], true
josharian 2014/03/16 18:51:16 I find the numbered local variables here impede re
502 } 552 }
503 553
504 // SetBasicAuth sets the request's Authorization header to use HTTP 554 // SetBasicAuth sets the request's Authorization header to use HTTP
505 // Basic Authentication with the provided username and password. 555 // Basic Authentication with the provided username and password.
506 // 556 //
507 // With HTTP Basic Authentication the provided username and password 557 // With HTTP Basic Authentication the provided username and password
508 // are not encrypted. 558 // are not encrypted.
509 func (r *Request) SetBasicAuth(username, password string) { 559 func (r *Request) SetBasicAuth(username, password string) {
510 r.Header.Set("Authorization", "Basic "+basicAuth(username, password)) 560 r.Header.Set("Authorization", "Basic "+basicAuth(username, password))
511 } 561 }
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
603 // Host: doesntmatter 653 // Host: doesntmatter
604 // the same. In the second case, any Host line is ignored. 654 // the same. In the second case, any Host line is ignored.
605 req.Host = req.URL.Host 655 req.Host = req.URL.Host
606 if req.Host == "" { 656 if req.Host == "" {
607 req.Host = req.Header.get("Host") 657 req.Host = req.Header.get("Host")
608 } 658 }
609 delete(req.Header, "Host") 659 delete(req.Header, "Host")
610 660
611 fixPragmaCacheControl(req.Header) 661 fixPragmaCacheControl(req.Header)
612 662
613 // TODO: Parse specific header values:
614 // Accept
615 // Accept-Encoding
616 // Accept-Language
617 // Authorization
618 // Cache-Control
619 // Connection
620 // Date
621 // Expect
622 // From
623 // If-Match
624 // If-Modified-Since
625 // If-None-Match
626 // If-Range
627 // If-Unmodified-Since
628 // Max-Forwards
629 // Proxy-Authorization
630 // Referer [sic]
631 // TE (transfer-codings)
632 // Trailer
633 // Transfer-Encoding
634 // Upgrade
635 // User-Agent
636 // Via
637 // Warning
638
639 err = readTransfer(req, b) 663 err = readTransfer(req, b)
640 if err != nil { 664 if err != nil {
641 return nil, err 665 return nil, err
642 } 666 }
643 667
668 req.Close = shouldClose(req.ProtoMajor, req.ProtoMinor, req.Header, fals e)
644 return req, nil 669 return req, nil
645 } 670 }
646 671
647 // MaxBytesReader is similar to io.LimitReader but is intended for 672 // MaxBytesReader is similar to io.LimitReader but is intended for
648 // limiting the size of incoming request bodies. In contrast to 673 // limiting the size of incoming request bodies. In contrast to
649 // io.LimitReader, MaxBytesReader's result is a ReadCloser, returns a 674 // io.LimitReader, MaxBytesReader's result is a ReadCloser, returns a
650 // non-EOF error for a Read beyond the limit, and Closes the 675 // non-EOF error for a Read beyond the limit, and Closes the
651 // underlying reader when its Close method is called. 676 // underlying reader when its Close method is called.
652 // 677 //
653 // MaxBytesReader prevents clients from accidentally or maliciously 678 // MaxBytesReader prevents clients from accidentally or maliciously
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
800 err := r.ParseForm() 825 err := r.ParseForm()
801 if err != nil { 826 if err != nil {
802 return err 827 return err
803 } 828 }
804 } 829 }
805 if r.MultipartForm != nil { 830 if r.MultipartForm != nil {
806 return nil 831 return nil
807 } 832 }
808 833
809 mr, err := r.multipartReader() 834 mr, err := r.multipartReader()
810 » if err == ErrNotMultipart { 835 » if err != nil {
811 » » return nil
812 » } else if err != nil {
813 return err 836 return err
814 } 837 }
815 838
816 f, err := mr.ReadForm(maxMemory) 839 f, err := mr.ReadForm(maxMemory)
817 if err != nil { 840 if err != nil {
818 return err 841 return err
819 } 842 }
820 for k, v := range f.Value { 843 for k, v := range f.Value {
821 r.Form[k] = append(r.Form[k], v...) 844 r.Form[k] = append(r.Form[k], v...)
822 } 845 }
823 r.MultipartForm = f 846 r.MultipartForm = f
824 847
825 return nil 848 return nil
826 } 849 }
827 850
828 // FormValue returns the first value for the named component of the query. 851 // FormValue returns the first value for the named component of the query.
829 // POST and PUT body parameters take precedence over URL query string values. 852 // POST and PUT body parameters take precedence over URL query string values.
830 // FormValue calls ParseMultipartForm and ParseForm if necessary. 853 // FormValue calls ParseMultipartForm and ParseForm if necessary and ignores
854 // any errors returned by these functions.
831 // To access multiple values of the same key use ParseForm. 855 // To access multiple values of the same key use ParseForm.
832 func (r *Request) FormValue(key string) string { 856 func (r *Request) FormValue(key string) string {
833 if r.Form == nil { 857 if r.Form == nil {
834 r.ParseMultipartForm(defaultMaxMemory) 858 r.ParseMultipartForm(defaultMaxMemory)
835 } 859 }
836 if vs := r.Form[key]; len(vs) > 0 { 860 if vs := r.Form[key]; len(vs) > 0 {
837 return vs[0] 861 return vs[0]
838 } 862 }
839 return "" 863 return ""
840 } 864 }
841 865
842 // PostFormValue returns the first value for the named component of the POST 866 // PostFormValue returns the first value for the named component of the POST
843 // or PUT request body. URL query parameters are ignored. 867 // or PUT request body. URL query parameters are ignored.
844 // PostFormValue calls ParseMultipartForm and ParseForm if necessary. 868 // PostFormValue calls ParseMultipartForm and ParseForm if necessary and ignores
869 // any errors returned by these functions.
845 func (r *Request) PostFormValue(key string) string { 870 func (r *Request) PostFormValue(key string) string {
846 if r.PostForm == nil { 871 if r.PostForm == nil {
847 r.ParseMultipartForm(defaultMaxMemory) 872 r.ParseMultipartForm(defaultMaxMemory)
848 } 873 }
849 if vs := r.PostForm[key]; len(vs) > 0 { 874 if vs := r.PostForm[key]; len(vs) > 0 {
850 return vs[0] 875 return vs[0]
851 } 876 }
852 return "" 877 return ""
853 } 878 }
854 879
(...skipping 25 matching lines...) Expand all
880 func (r *Request) wantsHttp10KeepAlive() bool { 905 func (r *Request) wantsHttp10KeepAlive() bool {
881 if r.ProtoMajor != 1 || r.ProtoMinor != 0 { 906 if r.ProtoMajor != 1 || r.ProtoMinor != 0 {
882 return false 907 return false
883 } 908 }
884 return hasToken(r.Header.get("Connection"), "keep-alive") 909 return hasToken(r.Header.get("Connection"), "keep-alive")
885 } 910 }
886 911
887 func (r *Request) wantsClose() bool { 912 func (r *Request) wantsClose() bool {
888 return hasToken(r.Header.get("Connection"), "close") 913 return hasToken(r.Header.get("Connection"), "close")
889 } 914 }
915
916 func (r *Request) closeBody() {
917 if r.Body != nil {
918 r.Body.Close()
919 }
920 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b