Source file src/pkg/cmd/go/internal/web/http.go
1
2
3
4
5
6
7
8
9
10
11
12 package web
13
14 import (
15 "crypto/tls"
16 "fmt"
17 "io/ioutil"
18 "net/http"
19 urlpkg "net/url"
20 "os"
21 "strings"
22 "time"
23
24 "cmd/go/internal/auth"
25 "cmd/go/internal/cfg"
26 "cmd/internal/browser"
27 )
28
29
30
31
32 var impatientInsecureHTTPClient = &http.Client{
33 Timeout: 5 * time.Second,
34 Transport: &http.Transport{
35 Proxy: http.ProxyFromEnvironment,
36 TLSClientConfig: &tls.Config{
37 InsecureSkipVerify: true,
38 },
39 },
40 }
41
42
43
44 var securityPreservingHTTPClient = &http.Client{
45 CheckRedirect: func(req *http.Request, via []*http.Request) error {
46 if len(via) > 0 && via[0].URL.Scheme == "https" && req.URL.Scheme != "https" {
47 lastHop := via[len(via)-1].URL
48 return fmt.Errorf("redirected from secure URL %s to insecure URL %s", lastHop, req.URL)
49 }
50 return nil
51 },
52 }
53
54 func get(security SecurityMode, url *urlpkg.URL) (*Response, error) {
55 start := time.Now()
56
57 if url.Scheme == "file" {
58 return getFile(url)
59 }
60
61 if os.Getenv("TESTGOPROXY404") == "1" && url.Host == "proxy.golang.org" {
62 res := &Response{
63 URL: Redacted(url),
64 Status: "404 testing",
65 StatusCode: 404,
66 Header: make(map[string][]string),
67 Body: ioutil.NopCloser(strings.NewReader("")),
68 }
69 if cfg.BuildX {
70 fmt.Fprintf(os.Stderr, "# get %s: %v (%.3fs)\n", Redacted(url), res.Status, time.Since(start).Seconds())
71 }
72 return res, nil
73 }
74
75 fetch := func(url *urlpkg.URL) (*urlpkg.URL, *http.Response, error) {
76
77
78
79
80 if cfg.BuildX {
81 fmt.Fprintf(os.Stderr, "# get %s\n", Redacted(url))
82 }
83
84 req, err := http.NewRequest("GET", url.String(), nil)
85 if err != nil {
86 return nil, nil, err
87 }
88 if url.Scheme == "https" {
89 auth.AddCredentials(req)
90 }
91
92 var res *http.Response
93 if security == Insecure && url.Scheme == "https" {
94 res, err = impatientInsecureHTTPClient.Do(req)
95 } else {
96 res, err = securityPreservingHTTPClient.Do(req)
97 }
98 return url, res, err
99 }
100
101 var (
102 fetched *urlpkg.URL
103 res *http.Response
104 err error
105 )
106 if url.Scheme == "" || url.Scheme == "https" {
107 secure := new(urlpkg.URL)
108 *secure = *url
109 secure.Scheme = "https"
110
111 fetched, res, err = fetch(secure)
112 if err != nil {
113 if cfg.BuildX {
114 fmt.Fprintf(os.Stderr, "# get %s: %v\n", Redacted(url), err)
115 }
116 if security != Insecure || url.Scheme == "https" {
117
118
119 return nil, err
120 }
121 }
122 }
123
124 if res == nil {
125 switch url.Scheme {
126 case "http":
127 if security == SecureOnly {
128 if cfg.BuildX {
129 fmt.Fprintf(os.Stderr, "# get %s: insecure\n", Redacted(url))
130 }
131 return nil, fmt.Errorf("insecure URL: %s", Redacted(url))
132 }
133 case "":
134 if security != Insecure {
135 panic("should have returned after HTTPS failure")
136 }
137 default:
138 if cfg.BuildX {
139 fmt.Fprintf(os.Stderr, "# get %s: unsupported\n", Redacted(url))
140 }
141 return nil, fmt.Errorf("unsupported scheme: %s", Redacted(url))
142 }
143
144 insecure := new(urlpkg.URL)
145 *insecure = *url
146 insecure.Scheme = "http"
147 if insecure.User != nil && security != Insecure {
148 if cfg.BuildX {
149 fmt.Fprintf(os.Stderr, "# get %s: insecure credentials\n", Redacted(url))
150 }
151 return nil, fmt.Errorf("refusing to pass credentials to insecure URL: %s", Redacted(insecure))
152 }
153
154 fetched, res, err = fetch(insecure)
155 if err != nil {
156 if cfg.BuildX {
157 fmt.Fprintf(os.Stderr, "# get %s: %v\n", Redacted(url), err)
158 }
159
160
161 return nil, err
162 }
163 }
164
165
166
167 if cfg.BuildX {
168 fmt.Fprintf(os.Stderr, "# get %s: %v (%.3fs)\n", Redacted(url), res.Status, time.Since(start).Seconds())
169 }
170 r := &Response{
171 URL: Redacted(fetched),
172 Status: res.Status,
173 StatusCode: res.StatusCode,
174 Header: map[string][]string(res.Header),
175 Body: res.Body,
176 }
177 return r, nil
178 }
179
180 func getFile(u *urlpkg.URL) (*Response, error) {
181 path, err := urlToFilePath(u)
182 if err != nil {
183 return nil, err
184 }
185 f, err := os.Open(path)
186
187 if os.IsNotExist(err) {
188 return &Response{
189 URL: Redacted(u),
190 Status: http.StatusText(http.StatusNotFound),
191 StatusCode: http.StatusNotFound,
192 Body: http.NoBody,
193 }, nil
194 }
195
196 if os.IsPermission(err) {
197 return &Response{
198 URL: Redacted(u),
199 Status: http.StatusText(http.StatusForbidden),
200 StatusCode: http.StatusForbidden,
201 Body: http.NoBody,
202 }, nil
203 }
204
205 if err != nil {
206 return nil, err
207 }
208
209 return &Response{
210 URL: Redacted(u),
211 Status: http.StatusText(http.StatusOK),
212 StatusCode: http.StatusOK,
213 Body: f,
214 }, nil
215 }
216
217 func openBrowser(url string) bool { return browser.Open(url) }
218
View as plain text