Source file src/pkg/net/http/request.go
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // HTTP Request reading and parsing.
6
7 package http
8
9 import (
10 "bufio"
11 "bytes"
12 "crypto/tls"
13 "encoding/base64"
14 "errors"
15 "fmt"
16 "io"
17 "io/ioutil"
18 "mime"
19 "mime/multipart"
20 "net/textproto"
21 "net/url"
22 "strings"
23 )
24
25 const (
26 maxValueLength = 4096
27 maxHeaderLines = 1024
28 chunkSize = 4 << 10 // 4 KB chunks
29 defaultMaxMemory = 32 << 20 // 32 MB
30 )
31
32 // ErrMissingFile is returned by FormFile when the provided file field name
33 // is either not present in the request or not a file field.
34 var ErrMissingFile = errors.New("http: no such file")
35
36 // HTTP request parsing errors.
37 type ProtocolError struct {
38 ErrorString string
39 }
40
41 func (err *ProtocolError) Error() string { return err.ErrorString }
42
43 var (
44 ErrHeaderTooLong = &ProtocolError{"header too long"}
45 ErrShortBody = &ProtocolError{"entity body too short"}
46 ErrNotSupported = &ProtocolError{"feature not supported"}
47 ErrUnexpectedTrailer = &ProtocolError{"trailer header without chunked transfer encoding"}
48 ErrMissingContentLength = &ProtocolError{"missing ContentLength in HEAD response"}
49 ErrNotMultipart = &ProtocolError{"request Content-Type isn't multipart/form-data"}
50 ErrMissingBoundary = &ProtocolError{"no multipart boundary param Content-Type"}
51 )
52
53 type badStringError struct {
54 what string
55 str string
56 }
57
58 func (e *badStringError) Error() string { return fmt.Sprintf("%s %q", e.what, e.str) }
59
60 // Headers that Request.Write handles itself and should be skipped.
61 var reqWriteExcludeHeader = map[string]bool{
62 "Host": true, // not in Header map anyway
63 "User-Agent": true,
64 "Content-Length": true,
65 "Transfer-Encoding": true,
66 "Trailer": true,
67 }
68
69 // A Request represents an HTTP request received by a server
70 // or to be sent by a client.
71 type Request struct {
72 Method string // GET, POST, PUT, etc.
73 URL *url.URL
74
75 // The protocol version for incoming requests.
76 // Outgoing requests always use HTTP/1.1.
77 Proto string // "HTTP/1.0"
78 ProtoMajor int // 1
79 ProtoMinor int // 0
80
81 // A header maps request lines to their values.
82 // If the header says
83 //
84 // accept-encoding: gzip, deflate
85 // Accept-Language: en-us
86 // Connection: keep-alive
87 //
88 // then
89 //
90 // Header = map[string][]string{
91 // "Accept-Encoding": {"gzip, deflate"},
92 // "Accept-Language": {"en-us"},
93 // "Connection": {"keep-alive"},
94 // }
95 //
96 // HTTP defines that header names are case-insensitive.
97 // The request parser implements this by canonicalizing the
98 // name, making the first character and any characters
99 // following a hyphen uppercase and the rest lowercase.
100 Header Header
101
102 // The message body.
103 Body io.ReadCloser
104
105 // ContentLength records the length of the associated content.
106 // The value -1 indicates that the length is unknown.
107 // Values >= 0 indicate that the given number of bytes may
108 // be read from Body.
109 // For outgoing requests, a value of 0 means unknown if Body is not nil.
110 ContentLength int64
111
112 // TransferEncoding lists the transfer encodings from outermost to
113 // innermost. An empty list denotes the "identity" encoding.
114 // TransferEncoding can usually be ignored; chunked encoding is
115 // automatically added and removed as necessary when sending and
116 // receiving requests.
117 TransferEncoding []string
118
119 // Close indicates whether to close the connection after
120 // replying to this request.
121 Close bool
122
123 // The host on which the URL is sought.
124 // Per RFC 2616, this is either the value of the Host: header
125 // or the host name given in the URL itself.
126 Host string
127
128 // Form contains the parsed form data, including both the URL
129 // field's query parameters and the POST or PUT form data.
130 // This field is only available after ParseForm is called.
131 // The HTTP client ignores Form and uses Body instead.
132 Form url.Values
133
134 // MultipartForm is the parsed multipart form, including file uploads.
135 // This field is only available after ParseMultipartForm is called.
136 // The HTTP client ignores MultipartForm and uses Body instead.
137 MultipartForm *multipart.Form
138
139 // Trailer maps trailer keys to values. Like for Header, if the
140 // response has multiple trailer lines with the same key, they will be
141 // concatenated, delimited by commas.
142 // For server requests, Trailer is only populated after Body has been
143 // closed or fully consumed.
144 // Trailer support is only partially complete.
145 Trailer Header
146
147 // RemoteAddr allows HTTP servers and other software to record
148 // the network address that sent the request, usually for
149 // logging. This field is not filled in by ReadRequest and
150 // has no defined format. The HTTP server in this package
151 // sets RemoteAddr to an "IP:port" address before invoking a
152 // handler.
153 // This field is ignored by the HTTP client.
154 RemoteAddr string
155
156 // RequestURI is the unmodified Request-URI of the
157 // Request-Line (RFC 2616, Section 5.1) as sent by the client
158 // to a server. Usually the URL field should be used instead.
159 // It is an error to set this field in an HTTP client request.
160 RequestURI string
161
162 // TLS allows HTTP servers and other software to record
163 // information about the TLS connection on which the request
164 // was received. This field is not filled in by ReadRequest.
165 // The HTTP server in this package sets the field for
166 // TLS-enabled connections before invoking a handler;
167 // otherwise it leaves the field nil.
168 // This field is ignored by the HTTP client.
169 TLS *tls.ConnectionState
170 }
171
172 // ProtoAtLeast returns whether the HTTP protocol used
173 // in the request is at least major.minor.
174 func (r *Request) ProtoAtLeast(major, minor int) bool {
175 return r.ProtoMajor > major ||
176 r.ProtoMajor == major && r.ProtoMinor >= minor
177 }
178
179 // UserAgent returns the client's User-Agent, if sent in the request.
180 func (r *Request) UserAgent() string {
181 return r.Header.Get("User-Agent")
182 }
183
184 // Cookies parses and returns the HTTP cookies sent with the request.
185 func (r *Request) Cookies() []*Cookie {
186 return readCookies(r.Header, "")
187 }
188
189 var ErrNoCookie = errors.New("http: named cookie not present")
190
191 // Cookie returns the named cookie provided in the request or
192 // ErrNoCookie if not found.
193 func (r *Request) Cookie(name string) (*Cookie, error) {
194 for _, c := range readCookies(r.Header, name) {
195 return c, nil
196 }
197 return nil, ErrNoCookie
198 }
199
200 // AddCookie adds a cookie to the request. Per RFC 6265 section 5.4,
201 // AddCookie does not attach more than one Cookie header field. That
202 // means all cookies, if any, are written into the same line,
203 // separated by semicolon.
204 func (r *Request) AddCookie(c *Cookie) {
205 s := fmt.Sprintf("%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value))
206 if c := r.Header.Get("Cookie"); c != "" {
207 r.Header.Set("Cookie", c+"; "+s)
208 } else {
209 r.Header.Set("Cookie", s)
210 }
211 }
212
213 // Referer returns the referring URL, if sent in the request.
214 //
215 // Referer is misspelled as in the request itself, a mistake from the
216 // earliest days of HTTP. This value can also be fetched from the
217 // Header map as Header["Referer"]; the benefit of making it available
218 // as a method is that the compiler can diagnose programs that use the
219 // alternate (correct English) spelling req.Referrer() but cannot
220 // diagnose programs that use Header["Referrer"].
221 func (r *Request) Referer() string {
222 return r.Header.Get("Referer")
223 }
224
225 // multipartByReader is a sentinel value.
226 // Its presence in Request.MultipartForm indicates that parsing of the request
227 // body has been handed off to a MultipartReader instead of ParseMultipartFrom.
228 var multipartByReader = &multipart.Form{
229 Value: make(map[string][]string),
230 File: make(map[string][]*multipart.FileHeader),
231 }
232
233 // MultipartReader returns a MIME multipart reader if this is a
234 // multipart/form-data POST request, else returns nil and an error.
235 // Use this function instead of ParseMultipartForm to
236 // process the request body as a stream.
237 func (r *Request) MultipartReader() (*multipart.Reader, error) {
238 if r.MultipartForm == multipartByReader {
239 return nil, errors.New("http: MultipartReader called twice")
240 }
241 if r.MultipartForm != nil {
242 return nil, errors.New("http: multipart handled by ParseMultipartForm")
243 }
244 r.MultipartForm = multipartByReader
245 return r.multipartReader()
246 }
247
248 func (r *Request) multipartReader() (*multipart.Reader, error) {
249 v := r.Header.Get("Content-Type")
250 if v == "" {
251 return nil, ErrNotMultipart
252 }
253 d, params, err := mime.ParseMediaType(v)
254 if err != nil || d != "multipart/form-data" {
255 return nil, ErrNotMultipart
256 }
257 boundary, ok := params["boundary"]
258 if !ok {
259 return nil, ErrMissingBoundary
260 }
261 return multipart.NewReader(r.Body, boundary), nil
262 }
263
264 // Return value if nonempty, def otherwise.
265 func valueOrDefault(value, def string) string {
266 if value != "" {
267 return value
268 }
269 return def
270 }
271
272 const defaultUserAgent = "Go http package"
273
274 // Write writes an HTTP/1.1 request -- header and body -- in wire format.
275 // This method consults the following fields of the request:
276 // Host
277 // URL
278 // Method (defaults to "GET")
279 // Header
280 // ContentLength
281 // TransferEncoding
282 // Body
283 //
284 // If Body is present, Content-Length is <= 0 and TransferEncoding
285 // hasn't been set to "identity", Write adds "Transfer-Encoding:
286 // chunked" to the header. Body is closed after it is sent.
287 func (r *Request) Write(w io.Writer) error {
288 return r.write(w, false, nil)
289 }
290
291 // WriteProxy is like Write but writes the request in the form
292 // expected by an HTTP proxy. In particular, WriteProxy writes the
293 // initial Request-URI line of the request with an absolute URI, per
294 // section 5.1.2 of RFC 2616, including the scheme and host.
295 // In either case, WriteProxy also writes a Host header, using
296 // either r.Host or r.URL.Host.
297 func (r *Request) WriteProxy(w io.Writer) error {
298 return r.write(w, true, nil)
299 }
300
301 // extraHeaders may be nil
302 func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) error {
303 host := req.Host
304 if host == "" {
305 if req.URL == nil {
306 return errors.New("http: Request.Write on Request with no Host or URL set")
307 }
308 host = req.URL.Host
309 }
310
311 ruri := req.URL.RequestURI()
312 if usingProxy && req.URL.Scheme != "" && req.URL.Opaque == "" {
313 ruri = req.URL.Scheme + "://" + host + ruri
314 } else if req.Method == "CONNECT" && req.URL.Path == "" {
315 // CONNECT requests normally give just the host and port, not a full URL.
316 ruri = host
317 }
318 // TODO(bradfitz): escape at least newlines in ruri?
319
320 bw := bufio.NewWriter(w)
321 fmt.Fprintf(bw, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), ruri)
322
323 // Header lines
324 fmt.Fprintf(bw, "Host: %s\r\n", host)
325
326 // Use the defaultUserAgent unless the Header contains one, which
327 // may be blank to not send the header.
328 userAgent := defaultUserAgent
329 if req.Header != nil {
330 if ua := req.Header["User-Agent"]; len(ua) > 0 {
331 userAgent = ua[0]
332 }
333 }
334 if userAgent != "" {
335 fmt.Fprintf(bw, "User-Agent: %s\r\n", userAgent)
336 }
337
338 // Process Body,ContentLength,Close,Trailer
339 tw, err := newTransferWriter(req)
340 if err != nil {
341 return err
342 }
343 err = tw.WriteHeader(bw)
344 if err != nil {
345 return err
346 }
347
348 // TODO: split long values? (If so, should share code with Conn.Write)
349 err = req.Header.WriteSubset(bw, reqWriteExcludeHeader)
350 if err != nil {
351 return err
352 }
353
354 if extraHeaders != nil {
355 err = extraHeaders.Write(bw)
356 if err != nil {
357 return err
358 }
359 }
360
361 io.WriteString(bw, "\r\n")
362
363 // Write body and trailer
364 err = tw.WriteBody(bw)
365 if err != nil {
366 return err
367 }
368
369 return bw.Flush()
370 }
371
372 // Convert decimal at s[i:len(s)] to integer,
373 // returning value, string position where the digits stopped,
374 // and whether there was a valid number (digits, not too big).
375 func atoi(s string, i int) (n, i1 int, ok bool) {
376 const Big = 1000000
377 if i >= len(s) || s[i] < '0' || s[i] > '9' {
378 return 0, 0, false
379 }
380 n = 0
381 for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
382 n = n*10 + int(s[i]-'0')
383 if n > Big {
384 return 0, 0, false
385 }
386 }
387 return n, i, true
388 }
389
390 // ParseHTTPVersion parses a HTTP version string.
391 // "HTTP/1.0" returns (1, 0, true).
392 func ParseHTTPVersion(vers string) (major, minor int, ok bool) {
393 if len(vers) < 5 || vers[0:5] != "HTTP/" {
394 return 0, 0, false
395 }
396 major, i, ok := atoi(vers, 5)
397 if !ok || i >= len(vers) || vers[i] != '.' {
398 return 0, 0, false
399 }
400 minor, i, ok = atoi(vers, i+1)
401 if !ok || i != len(vers) {
402 return 0, 0, false
403 }
404 return major, minor, true
405 }
406
407 // NewRequest returns a new Request given a method, URL, and optional body.
408 func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
409 u, err := url.Parse(urlStr)
410 if err != nil {
411 return nil, err
412 }
413 rc, ok := body.(io.ReadCloser)
414 if !ok && body != nil {
415 rc = ioutil.NopCloser(body)
416 }
417 req := &Request{
418 Method: method,
419 URL: u,
420 Proto: "HTTP/1.1",
421 ProtoMajor: 1,
422 ProtoMinor: 1,
423 Header: make(Header),
424 Body: rc,
425 Host: u.Host,
426 }
427 if body != nil {
428 switch v := body.(type) {
429 case *strings.Reader:
430 req.ContentLength = int64(v.Len())
431 case *bytes.Buffer:
432 req.ContentLength = int64(v.Len())
433 }
434 }
435
436 return req, nil
437 }
438
439 // SetBasicAuth sets the request's Authorization header to use HTTP
440 // Basic Authentication with the provided username and password.
441 //
442 // With HTTP Basic Authentication the provided username and password
443 // are not encrypted.
444 func (r *Request) SetBasicAuth(username, password string) {
445 s := username + ":" + password
446 r.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
447 }
448
449 // ReadRequest reads and parses a request from b.
450 func ReadRequest(b *bufio.Reader) (req *Request, err error) {
451
452 tp := textproto.NewReader(b)
453 req = new(Request)
454
455 // First line: GET /index.html HTTP/1.0
456 var s string
457 if s, err = tp.ReadLine(); err != nil {
458 return nil, err
459 }
460 defer func() {
461 if err == io.EOF {
462 err = io.ErrUnexpectedEOF
463 }
464 }()
465
466 var f []string
467 if f = strings.SplitN(s, " ", 3); len(f) < 3 {
468 return nil, &badStringError{"malformed HTTP request", s}
469 }
470 req.Method, req.RequestURI, req.Proto = f[0], f[1], f[2]
471 rawurl := req.RequestURI
472 var ok bool
473 if req.ProtoMajor, req.ProtoMinor, ok = ParseHTTPVersion(req.Proto); !ok {
474 return nil, &badStringError{"malformed HTTP version", req.Proto}
475 }
476
477 // CONNECT requests are used two different ways, and neither uses a full URL:
478 // The standard use is to tunnel HTTPS through an HTTP proxy.
479 // It looks like "CONNECT www.google.com:443 HTTP/1.1", and the parameter is
480 // just the authority section of a URL. This information should go in req.URL.Host.
481 //
482 // The net/rpc package also uses CONNECT, but there the parameter is a path
483 // that starts with a slash. It can be parsed with the regular URL parser,
484 // and the path will end up in req.URL.Path, where it needs to be in order for
485 // RPC to work.
486 justAuthority := req.Method == "CONNECT" && !strings.HasPrefix(rawurl, "/")
487 if justAuthority {
488 rawurl = "http://" + rawurl
489 }
490
491 if req.URL, err = url.ParseRequestURI(rawurl); err != nil {
492 return nil, err
493 }
494
495 if justAuthority {
496 // Strip the bogus "http://" back off.
497 req.URL.Scheme = ""
498 }
499
500 // Subsequent lines: Key: value.
501 mimeHeader, err := tp.ReadMIMEHeader()
502 if err != nil {
503 return nil, err
504 }
505 req.Header = Header(mimeHeader)
506
507 // RFC2616: Must treat
508 // GET /index.html HTTP/1.1
509 // Host: www.google.com
510 // and
511 // GET http://www.google.com/index.html HTTP/1.1
512 // Host: doesntmatter
513 // the same. In the second case, any Host line is ignored.
514 req.Host = req.URL.Host
515 if req.Host == "" {
516 req.Host = req.Header.Get("Host")
517 }
518 req.Header.Del("Host")
519
520 fixPragmaCacheControl(req.Header)
521
522 // TODO: Parse specific header values:
523 // Accept
524 // Accept-Encoding
525 // Accept-Language
526 // Authorization
527 // Cache-Control
528 // Connection
529 // Date
530 // Expect
531 // From
532 // If-Match
533 // If-Modified-Since
534 // If-None-Match
535 // If-Range
536 // If-Unmodified-Since
537 // Max-Forwards
538 // Proxy-Authorization
539 // Referer [sic]
540 // TE (transfer-codings)
541 // Trailer
542 // Transfer-Encoding
543 // Upgrade
544 // User-Agent
545 // Via
546 // Warning
547
548 err = readTransfer(req, b)
549 if err != nil {
550 return nil, err
551 }
552
553 return req, nil
554 }
555
556 // MaxBytesReader is similar to io.LimitReader but is intended for
557 // limiting the size of incoming request bodies. In contrast to
558 // io.LimitReader, MaxBytesReader's result is a ReadCloser, returns a
559 // non-EOF error for a Read beyond the limit, and Closes the
560 // underlying reader when its Close method is called.
561 //
562 // MaxBytesReader prevents clients from accidentally or maliciously
563 // sending a large request and wasting server resources.
564 func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser {
565 return &maxBytesReader{w: w, r: r, n: n}
566 }
567
568 type maxBytesReader struct {
569 w ResponseWriter
570 r io.ReadCloser // underlying reader
571 n int64 // max bytes remaining
572 stopped bool
573 }
574
575 func (l *maxBytesReader) Read(p []byte) (n int, err error) {
576 if l.n <= 0 {
577 if !l.stopped {
578 l.stopped = true
579 if res, ok := l.w.(*response); ok {
580 res.requestTooLarge()
581 }
582 }
583 return 0, errors.New("http: request body too large")
584 }
585 if int64(len(p)) > l.n {
586 p = p[:l.n]
587 }
588 n, err = l.r.Read(p)
589 l.n -= int64(n)
590 return
591 }
592
593 func (l *maxBytesReader) Close() error {
594 return l.r.Close()
595 }
596
597 // ParseForm parses the raw query from the URL.
598 //
599 // For POST or PUT requests, it also parses the request body as a form.
600 // If the request Body's size has not already been limited by MaxBytesReader,
601 // the size is capped at 10MB.
602 //
603 // ParseMultipartForm calls ParseForm automatically.
604 // It is idempotent.
605 func (r *Request) ParseForm() (err error) {
606 if r.Form != nil {
607 return
608 }
609 if r.URL != nil {
610 r.Form, err = url.ParseQuery(r.URL.RawQuery)
611 }
612 if r.Method == "POST" || r.Method == "PUT" {
613 if r.Body == nil {
614 return errors.New("missing form body")
615 }
616 ct := r.Header.Get("Content-Type")
617 ct, _, err = mime.ParseMediaType(ct)
618 switch {
619 case ct == "application/x-www-form-urlencoded":
620 var reader io.Reader = r.Body
621 maxFormSize := int64(1<<63 - 1)
622 if _, ok := r.Body.(*maxBytesReader); !ok {
623 maxFormSize = int64(10 << 20) // 10 MB is a lot of text.
624 reader = io.LimitReader(r.Body, maxFormSize+1)
625 }
626 b, e := ioutil.ReadAll(reader)
627 if e != nil {
628 if err == nil {
629 err = e
630 }
631 break
632 }
633 if int64(len(b)) > maxFormSize {
634 return errors.New("http: POST too large")
635 }
636 var newValues url.Values
637 newValues, e = url.ParseQuery(string(b))
638 if err == nil {
639 err = e
640 }
641 if r.Form == nil {
642 r.Form = make(url.Values)
643 }
644 // Copy values into r.Form. TODO: make this smoother.
645 for k, vs := range newValues {
646 for _, value := range vs {
647 r.Form.Add(k, value)
648 }
649 }
650 case ct == "multipart/form-data":
651 // handled by ParseMultipartForm (which is calling us, or should be)
652 // TODO(bradfitz): there are too many possible
653 // orders to call too many functions here.
654 // Clean this up and write more tests.
655 // request_test.go contains the start of this,
656 // in TestRequestMultipartCallOrder.
657 }
658 }
659 return err
660 }
661
662 // ParseMultipartForm parses a request body as multipart/form-data.
663 // The whole request body is parsed and up to a total of maxMemory bytes of
664 // its file parts are stored in memory, with the remainder stored on
665 // disk in temporary files.
666 // ParseMultipartForm calls ParseForm if necessary.
667 // After one call to ParseMultipartForm, subsequent calls have no effect.
668 func (r *Request) ParseMultipartForm(maxMemory int64) error {
669 if r.MultipartForm == multipartByReader {
670 return errors.New("http: multipart handled by MultipartReader")
671 }
672 if r.Form == nil {
673 err := r.ParseForm()
674 if err != nil {
675 return err
676 }
677 }
678 if r.MultipartForm != nil {
679 return nil
680 }
681
682 mr, err := r.multipartReader()
683 if err == ErrNotMultipart {
684 return nil
685 } else if err != nil {
686 return err
687 }
688
689 f, err := mr.ReadForm(maxMemory)
690 if err != nil {
691 return err
692 }
693 for k, v := range f.Value {
694 r.Form[k] = append(r.Form[k], v...)
695 }
696 r.MultipartForm = f
697
698 return nil
699 }
700
701 // FormValue returns the first value for the named component of the query.
702 // FormValue calls ParseMultipartForm and ParseForm if necessary.
703 func (r *Request) FormValue(key string) string {
704 if r.Form == nil {
705 r.ParseMultipartForm(defaultMaxMemory)
706 }
707 if vs := r.Form[key]; len(vs) > 0 {
708 return vs[0]
709 }
710 return ""
711 }
712
713 // FormFile returns the first file for the provided form key.
714 // FormFile calls ParseMultipartForm and ParseForm if necessary.
715 func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {
716 if r.MultipartForm == multipartByReader {
717 return nil, nil, errors.New("http: multipart handled by MultipartReader")
718 }
719 if r.MultipartForm == nil {
720 err := r.ParseMultipartForm(defaultMaxMemory)
721 if err != nil {
722 return nil, nil, err
723 }
724 }
725 if r.MultipartForm != nil && r.MultipartForm.File != nil {
726 if fhs := r.MultipartForm.File[key]; len(fhs) > 0 {
727 f, err := fhs[0].Open()
728 return f, fhs[0], err
729 }
730 }
731 return nil, nil, ErrMissingFile
732 }
733
734 func (r *Request) expectsContinue() bool {
735 return strings.ToLower(r.Header.Get("Expect")) == "100-continue"
736 }
737
738 func (r *Request) wantsHttp10KeepAlive() bool {
739 if r.ProtoMajor != 1 || r.ProtoMinor != 0 {
740 return false
741 }
742 return strings.Contains(strings.ToLower(r.Header.Get("Connection")), "keep-alive")
743 }