LEFT | RIGHT |
(no file at all) | |
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 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 package http | 5 package http |
6 | 6 |
7 import ( | 7 import ( |
8 "bufio" | 8 "bufio" |
9 "compress/gzip" | 9 "compress/gzip" |
10 "crypto/tls" | 10 "crypto/tls" |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
93 } | 93 } |
94 | 94 |
95 // ProxyURL returns a proxy function (for use in a Transport) | 95 // ProxyURL returns a proxy function (for use in a Transport) |
96 // that always returns the same URL. | 96 // that always returns the same URL. |
97 func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, os.Error) { | 97 func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, os.Error) { |
98 return func(*Request) (*url.URL, os.Error) { | 98 return func(*Request) (*url.URL, os.Error) { |
99 return fixedURL, nil | 99 return fixedURL, nil |
100 } | 100 } |
101 } | 101 } |
102 | 102 |
| 103 // transportRequest is a wrapper around a *Request that adds |
| 104 // optional extra headers to write. |
| 105 type transportRequest struct { |
| 106 *Request // original request, not to be mutated |
| 107 extra Header // extra headers to write, or nil |
| 108 } |
| 109 |
| 110 func (tr *transportRequest) extraHeaders() Header { |
| 111 if tr.extra == nil { |
| 112 tr.extra = make(Header) |
| 113 } |
| 114 return tr.extra |
| 115 } |
| 116 |
103 // RoundTrip implements the RoundTripper interface. | 117 // RoundTrip implements the RoundTripper interface. |
104 func (t *Transport) RoundTrip(req *Request) (resp *Response, err os.Error) { | 118 func (t *Transport) RoundTrip(req *Request) (resp *Response, err os.Error) { |
105 if req.URL == nil { | 119 if req.URL == nil { |
106 return nil, os.NewError("http: nil Request.URL") | 120 return nil, os.NewError("http: nil Request.URL") |
| 121 } |
| 122 if req.Header == nil { |
| 123 return nil, os.NewError("http: nil Request.Header") |
107 } | 124 } |
108 if req.URL.Scheme != "http" && req.URL.Scheme != "https" { | 125 if req.URL.Scheme != "http" && req.URL.Scheme != "https" { |
109 t.lk.Lock() | 126 t.lk.Lock() |
110 var rt RoundTripper | 127 var rt RoundTripper |
111 if t.altProto != nil { | 128 if t.altProto != nil { |
112 rt = t.altProto[req.URL.Scheme] | 129 rt = t.altProto[req.URL.Scheme] |
113 } | 130 } |
114 t.lk.Unlock() | 131 t.lk.Unlock() |
115 if rt == nil { | 132 if rt == nil { |
116 return nil, &badStringError{"unsupported protocol scheme
", req.URL.Scheme} | 133 return nil, &badStringError{"unsupported protocol scheme
", req.URL.Scheme} |
117 } | 134 } |
118 return rt.RoundTrip(req) | 135 return rt.RoundTrip(req) |
119 } | 136 } |
120 | 137 » treq := &transportRequest{Request: req} |
121 » cm, err := t.connectMethodForRequest(req) | 138 » cm, err := t.connectMethodForRequest(treq) |
122 if err != nil { | 139 if err != nil { |
123 return nil, err | 140 return nil, err |
124 } | 141 } |
125 | 142 |
126 // Get the cached or newly-created connection to either the | 143 // Get the cached or newly-created connection to either the |
127 // host (for http or https), the http proxy, or the http proxy | 144 // host (for http or https), the http proxy, or the http proxy |
128 // pre-CONNECTed to https server. In any case, we'll be ready | 145 // pre-CONNECTed to https server. In any case, we'll be ready |
129 // to send it requests. | 146 // to send it requests. |
130 pconn, err := t.getConn(cm) | 147 pconn, err := t.getConn(cm) |
131 if err != nil { | 148 if err != nil { |
132 return nil, err | 149 return nil, err |
133 } | 150 } |
134 | 151 |
135 » return pconn.roundTrip(req) | 152 » return pconn.roundTrip(treq) |
136 } | 153 } |
137 | 154 |
138 // RegisterProtocol registers a new protocol with scheme. | 155 // RegisterProtocol registers a new protocol with scheme. |
139 // The Transport will pass requests using the given scheme to rt. | 156 // The Transport will pass requests using the given scheme to rt. |
140 // It is rt's responsibility to simulate HTTP request semantics. | 157 // It is rt's responsibility to simulate HTTP request semantics. |
141 // | 158 // |
142 // RegisterProtocol can be used by other packages to provide | 159 // RegisterProtocol can be used by other packages to provide |
143 // implementations of protocol schemes like "ftp" or "file". | 160 // implementations of protocol schemes like "ftp" or "file". |
144 func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) { | 161 func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) { |
145 if scheme == "http" || scheme == "https" { | 162 if scheme == "http" || scheme == "https" { |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
178 // Private implementation past this point. | 195 // Private implementation past this point. |
179 // | 196 // |
180 | 197 |
181 func getenvEitherCase(k string) string { | 198 func getenvEitherCase(k string) string { |
182 if v := os.Getenv(strings.ToUpper(k)); v != "" { | 199 if v := os.Getenv(strings.ToUpper(k)); v != "" { |
183 return v | 200 return v |
184 } | 201 } |
185 return os.Getenv(strings.ToLower(k)) | 202 return os.Getenv(strings.ToLower(k)) |
186 } | 203 } |
187 | 204 |
188 func (t *Transport) connectMethodForRequest(req *Request) (*connectMethod, os.Er
ror) { | 205 func (t *Transport) connectMethodForRequest(treq *transportRequest) (*connectMet
hod, os.Error) { |
189 cm := &connectMethod{ | 206 cm := &connectMethod{ |
190 » » targetScheme: req.URL.Scheme, | 207 » » targetScheme: treq.URL.Scheme, |
191 » » targetAddr: canonicalAddr(req.URL), | 208 » » targetAddr: canonicalAddr(treq.URL), |
192 } | 209 } |
193 if t.Proxy != nil { | 210 if t.Proxy != nil { |
194 var err os.Error | 211 var err os.Error |
195 » » cm.proxyURL, err = t.Proxy(req) | 212 » » cm.proxyURL, err = t.Proxy(treq.Request) |
196 if err != nil { | 213 if err != nil { |
197 return nil, err | 214 return nil, err |
198 } | 215 } |
199 } | 216 } |
200 return cm, nil | 217 return cm, nil |
201 } | 218 } |
202 | 219 |
203 // proxyAuth returns the Proxy-Authorization header to set | 220 // proxyAuth returns the Proxy-Authorization header to set |
204 // on requests, if applicable. | 221 // on requests, if applicable. |
205 func (cm *connectMethod) proxyAuth() string { | 222 func (cm *connectMethod) proxyAuth() string { |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
288 } | 305 } |
289 | 306 |
290 pa := cm.proxyAuth() | 307 pa := cm.proxyAuth() |
291 | 308 |
292 pconn := &persistConn{ | 309 pconn := &persistConn{ |
293 t: t, | 310 t: t, |
294 cacheKey: cm.String(), | 311 cacheKey: cm.String(), |
295 conn: conn, | 312 conn: conn, |
296 reqch: make(chan requestAndChan, 50), | 313 reqch: make(chan requestAndChan, 50), |
297 } | 314 } |
298 newClientConnFunc := NewClientConn | |
299 | 315 |
300 switch { | 316 switch { |
301 case cm.proxyURL == nil: | 317 case cm.proxyURL == nil: |
302 // Do nothing. | 318 // Do nothing. |
303 case cm.targetScheme == "http": | 319 case cm.targetScheme == "http": |
304 » » newClientConnFunc = NewProxyClientConn | 320 » » pconn.isProxy = true |
305 if pa != "" { | 321 if pa != "" { |
306 » » » pconn.mutateRequestFunc = func(req *Request) { | 322 » » » pconn.mutateHeaderFunc = func(h Header) { |
307 » » » » if req.Header == nil { | 323 » » » » h.Set("Proxy-Authorization", pa) |
308 » » » » » req.Header = make(Header) | |
309 » » » » } | |
310 » » » » req.Header.Set("Proxy-Authorization", pa) | |
311 } | 324 } |
312 } | 325 } |
313 case cm.targetScheme == "https": | 326 case cm.targetScheme == "https": |
314 connectReq := &Request{ | 327 connectReq := &Request{ |
315 Method: "CONNECT", | 328 Method: "CONNECT", |
316 URL: &url.URL{RawPath: cm.targetAddr}, | 329 URL: &url.URL{RawPath: cm.targetAddr}, |
317 Host: cm.targetAddr, | 330 Host: cm.targetAddr, |
318 Header: make(Header), | 331 Header: make(Header), |
319 } | 332 } |
320 if pa != "" { | 333 if pa != "" { |
(...skipping 23 matching lines...) Expand all Loading... |
344 if err = conn.(*tls.Conn).Handshake(); err != nil { | 357 if err = conn.(*tls.Conn).Handshake(); err != nil { |
345 return nil, err | 358 return nil, err |
346 } | 359 } |
347 if err = conn.(*tls.Conn).VerifyHostname(cm.tlsHost()); err != n
il { | 360 if err = conn.(*tls.Conn).VerifyHostname(cm.tlsHost()); err != n
il { |
348 return nil, err | 361 return nil, err |
349 } | 362 } |
350 pconn.conn = conn | 363 pconn.conn = conn |
351 } | 364 } |
352 | 365 |
353 pconn.br = bufio.NewReader(pconn.conn) | 366 pconn.br = bufio.NewReader(pconn.conn) |
354 » pconn.cc = newClientConnFunc(conn, pconn.br) | 367 » pconn.cc = NewClientConn(conn, pconn.br) |
355 go pconn.readLoop() | 368 go pconn.readLoop() |
356 return pconn, nil | 369 return pconn, nil |
357 } | 370 } |
358 | 371 |
359 // useProxy returns true if requests to addr should use a proxy, | 372 // useProxy returns true if requests to addr should use a proxy, |
360 // according to the NO_PROXY or no_proxy environment variable. | 373 // according to the NO_PROXY or no_proxy environment variable. |
361 // addr is always a canonicalAddr with a host and port. | 374 // addr is always a canonicalAddr with a host and port. |
362 func useProxy(addr string) bool { | 375 func useProxy(addr string) bool { |
363 if len(addr) == 0 { | 376 if len(addr) == 0 { |
364 return true | 377 return true |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
440 // tlsHost returns the host name to match against the peer's | 453 // tlsHost returns the host name to match against the peer's |
441 // TLS certificate. | 454 // TLS certificate. |
442 func (cm *connectMethod) tlsHost() string { | 455 func (cm *connectMethod) tlsHost() string { |
443 h := cm.targetAddr | 456 h := cm.targetAddr |
444 if hasPort(h) { | 457 if hasPort(h) { |
445 h = h[:strings.LastIndex(h, ":")] | 458 h = h[:strings.LastIndex(h, ":")] |
446 } | 459 } |
447 return h | 460 return h |
448 } | 461 } |
449 | 462 |
450 type readResult struct { | |
451 res *Response // either res or err will be set | |
452 err os.Error | |
453 } | |
454 | |
455 type writeRequest struct { | |
456 // Set by client (in pc.roundTrip) | |
457 req *Request | |
458 resch chan *readResult | |
459 | |
460 // Set by writeLoop if an error writing headers. | |
461 writeErr os.Error | |
462 } | |
463 | |
464 // persistConn wraps a connection, usually a persistent one | 463 // persistConn wraps a connection, usually a persistent one |
465 // (but may be used for non-keep-alive requests as well) | 464 // (but may be used for non-keep-alive requests as well) |
466 type persistConn struct { | 465 type persistConn struct { |
467 » t *Transport | 466 » t *Transport |
468 » cacheKey string // its connectMethod.String() | 467 » cacheKey string // its connectMethod.String() |
469 » conn net.Conn | 468 » conn net.Conn |
470 » cc *ClientConn | 469 » cc *ClientConn |
471 » br *bufio.Reader | 470 » br *bufio.Reader |
472 » reqch chan requestAndChan // written by roundTrip(); read by
readLoop() | 471 » reqch chan requestAndChan // written by roundTrip(); read by readLoop
() |
473 » mutateRequestFunc func(*Request) // nil or func to modify each outb
ound request | 472 » isProxy bool |
| 473 |
| 474 » // mutateHeaderFunc is an optional func to modify extra |
| 475 » // headers on each outbound request before it's written. (the |
| 476 » // original Request given to RoundTrip is not modified) |
| 477 » mutateHeaderFunc func(Header) |
474 | 478 |
475 lk sync.Mutex // guards numExpectedResponses and broke
n | 479 lk sync.Mutex // guards numExpectedResponses and broke
n |
476 numExpectedResponses int | 480 numExpectedResponses int |
477 broken bool // an error has happened on this connection; m
arked broken so it's not reused. | 481 broken bool // an error has happened on this connection; m
arked broken so it's not reused. |
478 } | 482 } |
479 | 483 |
480 func (pc *persistConn) isBroken() bool { | 484 func (pc *persistConn) isBroken() bool { |
481 pc.lk.Lock() | 485 pc.lk.Lock() |
482 defer pc.lk.Unlock() | 486 defer pc.lk.Unlock() |
483 return pc.broken | 487 return pc.broken |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
518 string(pb), err) | 522 string(pb), err) |
519 pc.close() | 523 pc.close() |
520 return | 524 return |
521 } | 525 } |
522 | 526 |
523 rc := <-pc.reqch | 527 rc := <-pc.reqch |
524 resp, err := pc.cc.readUsing(rc.req, func(buf *bufio.Reader, for
Req *Request) (*Response, os.Error) { | 528 resp, err := pc.cc.readUsing(rc.req, func(buf *bufio.Reader, for
Req *Request) (*Response, os.Error) { |
525 resp, err := ReadResponse(buf, forReq) | 529 resp, err := ReadResponse(buf, forReq) |
526 if err != nil || resp.ContentLength == 0 { | 530 if err != nil || resp.ContentLength == 0 { |
527 return resp, err | 531 return resp, err |
528 } | |
529 if rc.addedGzip { | |
530 forReq.Header.Del("Accept-Encoding") | |
531 } | 532 } |
532 if rc.addedGzip && resp.Header.Get("Content-Encoding") =
= "gzip" { | 533 if rc.addedGzip && resp.Header.Get("Content-Encoding") =
= "gzip" { |
533 resp.Header.Del("Content-Encoding") | 534 resp.Header.Del("Content-Encoding") |
534 resp.Header.Del("Content-Length") | 535 resp.Header.Del("Content-Length") |
535 resp.ContentLength = -1 | 536 resp.ContentLength = -1 |
536 gzReader, err := gzip.NewReader(resp.Body) | 537 gzReader, err := gzip.NewReader(resp.Body) |
537 if err != nil { | 538 if err != nil { |
538 pc.close() | 539 pc.close() |
539 return nil, err | 540 return nil, err |
540 } | 541 } |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
597 type requestAndChan struct { | 598 type requestAndChan struct { |
598 req *Request | 599 req *Request |
599 ch chan responseAndError | 600 ch chan responseAndError |
600 | 601 |
601 // did the Transport (as opposed to the client code) add an | 602 // did the Transport (as opposed to the client code) add an |
602 // Accept-Encoding gzip header? only if it we set it do | 603 // Accept-Encoding gzip header? only if it we set it do |
603 // we transparently decode the gzip. | 604 // we transparently decode the gzip. |
604 addedGzip bool | 605 addedGzip bool |
605 } | 606 } |
606 | 607 |
607 func (pc *persistConn) roundTrip(req *Request) (resp *Response, err os.Error) { | 608 func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err os.
Error) { |
608 » if pc.mutateRequestFunc != nil { | 609 » if pc.mutateHeaderFunc != nil { |
609 » » pc.mutateRequestFunc(req) | 610 » » pc.mutateHeaderFunc(req.extraHeaders()) |
610 } | 611 } |
611 | 612 |
612 // Ask for a compressed version if the caller didn't set their | 613 // Ask for a compressed version if the caller didn't set their |
613 // own value for Accept-Encoding. We only attempted to | 614 // own value for Accept-Encoding. We only attempted to |
614 // uncompress the gzip stream if we were the layer that | 615 // uncompress the gzip stream if we were the layer that |
615 // requested it. | 616 // requested it. |
616 requestedGzip := false | 617 requestedGzip := false |
617 if !pc.t.DisableCompression && req.Header.Get("Accept-Encoding") == "" { | 618 if !pc.t.DisableCompression && req.Header.Get("Accept-Encoding") == "" { |
618 // Request gzip only, not deflate. Deflate is ambiguous and· | 619 // Request gzip only, not deflate. Deflate is ambiguous and· |
619 » » // as universally supported anyway. | 620 » » // not as universally supported anyway. |
620 // See: http://www.gzip.org/zlib/zlib_faq.html#faq38 | 621 // See: http://www.gzip.org/zlib/zlib_faq.html#faq38 |
621 requestedGzip = true | 622 requestedGzip = true |
622 » » req.Header.Set("Accept-Encoding", "gzip") | 623 » » req.extraHeaders().Set("Accept-Encoding", "gzip") |
623 } | 624 } |
624 | 625 |
625 pc.lk.Lock() | 626 pc.lk.Lock() |
626 pc.numExpectedResponses++ | 627 pc.numExpectedResponses++ |
627 pc.lk.Unlock() | 628 pc.lk.Unlock() |
628 | 629 |
629 » err = pc.cc.Write(req) | 630 » pc.cc.writeReq = func(r *Request, w io.Writer) os.Error { |
| 631 » » return r.write(w, pc.isProxy, req.extra) |
| 632 » } |
| 633 |
| 634 » err = pc.cc.Write(req.Request) |
630 if err != nil { | 635 if err != nil { |
631 pc.close() | 636 pc.close() |
632 return | 637 return |
633 } | 638 } |
634 | 639 |
635 ch := make(chan responseAndError, 1) | 640 ch := make(chan responseAndError, 1) |
636 » pc.reqch <- requestAndChan{req, ch, requestedGzip} | 641 » pc.reqch <- requestAndChan{req.Request, ch, requestedGzip} |
637 re := <-ch | 642 re := <-ch |
638 pc.lk.Lock() | 643 pc.lk.Lock() |
639 pc.numExpectedResponses-- | 644 pc.numExpectedResponses-- |
640 pc.lk.Unlock() | 645 pc.lk.Unlock() |
641 | 646 |
642 return re.res, re.err | 647 return re.res, re.err |
643 } | 648 } |
644 | 649 |
645 func (pc *persistConn) close() { | 650 func (pc *persistConn) close() { |
646 pc.lk.Lock() | 651 pc.lk.Lock() |
647 defer pc.lk.Unlock() | 652 defer pc.lk.Unlock() |
648 pc.broken = true | 653 pc.broken = true |
649 pc.cc.Close() | 654 pc.cc.Close() |
650 pc.conn.Close() | 655 pc.conn.Close() |
651 » pc.mutateRequestFunc = nil | 656 » pc.mutateHeaderFunc = nil |
652 } | 657 } |
653 | 658 |
654 var portMap = map[string]string{ | 659 var portMap = map[string]string{ |
655 "http": "80", | 660 "http": "80", |
656 "https": "443", | 661 "https": "443", |
657 } | 662 } |
658 | 663 |
659 // canonicalAddr returns url.Host but always with a ":port" suffix | 664 // canonicalAddr returns url.Host but always with a ":port" suffix |
660 func canonicalAddr(url *url.URL) string { | 665 func canonicalAddr(url *url.URL) string { |
661 addr := url.Host | 666 addr := url.Host |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
722 | 727 |
723 // discardOnCloseReadCloser consumes all its input on Close. | 728 // discardOnCloseReadCloser consumes all its input on Close. |
724 type discardOnCloseReadCloser struct { | 729 type discardOnCloseReadCloser struct { |
725 io.ReadCloser | 730 io.ReadCloser |
726 } | 731 } |
727 | 732 |
728 func (d *discardOnCloseReadCloser) Close() os.Error { | 733 func (d *discardOnCloseReadCloser) Close() os.Error { |
729 io.Copy(ioutil.Discard, d.ReadCloser) // ignore errors; likely invalid o
r already closed | 734 io.Copy(ioutil.Discard, d.ReadCloser) // ignore errors; likely invalid o
r already closed |
730 return d.ReadCloser.Close() | 735 return d.ReadCloser.Close() |
731 } | 736 } |
LEFT | RIGHT |