1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package http
import (
"bufio"
"crypto/tls"
"encoding/base64"
"fmt"
"net"
"os"
"strings"
"sync"
)
// DefaultTransport is the default implementation of Transport and is
// used by DefaultClient. It establishes a new network connection for
// each call to Do and uses HTTP proxies as directed by the
// $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy)
// environment variables.
var DefaultTransport Transport = &transport{}
// transport implements Tranport for the default case, using TCP
// connections to either the host or a proxy, serving http or https
// schemes. In the future this may become public and support options
// on keep-alive connection duration, pipelining controls, etc. For
// now this is simply a port of the old Go code client code to the
// Transport interface.
type transport struct {
// TODO: keep-alives, pipelining, etc using a map from
// scheme/host to a connection. Something like:
l sync.Mutex
hostConn map[string]*ClientConn
}
func (ct *transport) Do(req *Request) (resp *Response, err os.Error) {
if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme}
}
addr := req.URL.Host
if !hasPort(addr) {
addr += ":" + req.URL.Scheme
}
var proxyURL *URL
proxyAuth := ""
proxy := ""
if !matchNoProxy(addr) {
proxy = os.Getenv("HTTP_PROXY")
if proxy == "" {
proxy = os.Getenv("http_proxy")
}
}
var write = (*Request).Write
if proxy != "" {
write = (*Request).WriteProxy
proxyURL, err = ParseRequestURL(proxy)
if err != nil {
return nil, os.ErrorString("invalid proxy address")
}
if proxyURL.Host == "" {
proxyURL, err = ParseRequestURL("http://" + proxy)
if err != nil {
return nil, os.ErrorString("invalid proxy address")
}
}
addr = proxyURL.Host
proxyInfo := proxyURL.RawUserinfo
if proxyInfo != "" {
enc := base64.URLEncoding
encoded := make([]byte, enc.EncodedLen(len(proxyInfo)))
enc.Encode(encoded, []byte(proxyInfo))
proxyAuth = "Basic " + string(encoded)
}
}
// Connect to server or proxy
conn, err := net.Dial("tcp", "", addr)
if err != nil {
return nil, err
}
if req.URL.Scheme == "http" {
// Include proxy http header if needed.
if proxyAuth != "" {
req.Header.Set("Proxy-Authorization", proxyAuth)
}
} else { // https
if proxyURL != nil {
// Ask proxy for direct connection to server.
// addr defaults above to ":https" but we need to use numbers
addr = req.URL.Host
if !hasPort(addr) {
addr += ":443"
}
fmt.Fprintf(conn, "CONNECT %s HTTP/1.1\r\n", addr)
fmt.Fprintf(conn, "Host: %s\r\n", addr)
if proxyAuth != "" {
fmt.Fprintf(conn, "Proxy-Authorization: %s\r\n", proxyAuth)
}
fmt.Fprintf(conn, "\r\n")
// Read response.
// Okay to use and discard buffered reader here, because
// TLS server will not speak until spoken to.
br := bufio.NewReader(conn)
resp, err := ReadResponse(br, "CONNECT")
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
f := strings.Split(resp.Status, " ", 2)
return nil, os.ErrorString(f[1])
}
}
// Initiate TLS and check remote host name against certificate.
conn = tls.Client(conn, nil)
if err = conn.(*tls.Conn).Handshake(); err != nil {
return nil, err
}
h := req.URL.Host
if hasPort(h) {
h = h[:strings.LastIndex(h, ":")]
}
if err = conn.(*tls.Conn).VerifyHostname(h); err != nil {
return nil, err
}
}
err = write(req, conn)
if err != nil {
conn.Close()
return nil, err
}
reader := bufio.NewReader(conn)
resp, err = ReadResponse(reader, req.Method)
if err != nil {
conn.Close()
return nil, err
}
resp.Body = readClose{resp.Body, conn}
return
}
|