Skip to content

Commit 33bfd52

Browse files
net/http: Add js/wasm compatible DefaultTransport
Adds a new Transport type for the js/wasm target that uses the JavaScript Fetch API for sending HTTP requests. Support for streaming response bodies is used when available, falling back to reading the entire response into memory at once.
1 parent 65c365b commit 33bfd52

File tree

5 files changed

+439
-202
lines changed

5 files changed

+439
-202
lines changed

src/net/http/http.go

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@ type contextKey struct {
3333

3434
func (k *contextKey) String() string { return "net/http context value " + k.name }
3535

36-
// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
37-
// return true if the string includes a port.
38-
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
39-
4036
// removeEmptyPort strips the empty port in ":port" to ""
4137
// as mandated by RFC 3986 Section 6.2.3.
4238
func removeEmptyPort(host string) string {
@@ -50,15 +46,6 @@ func isNotToken(r rune) bool {
5046
return !httpguts.IsTokenRune(r)
5147
}
5248

53-
func isASCII(s string) bool {
54-
for i := 0; i < len(s); i++ {
55-
if s[i] >= utf8.RuneSelf {
56-
return false
57-
}
58-
}
59-
return true
60-
}
61-
6249
func hexEscapeNonASCII(s string) string {
6350
newLen := 0
6451
for i := 0; i < len(s); i++ {

src/net/http/proxy.go

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
// Copyright 2018 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+
package http
6+
7+
import (
8+
"errors"
9+
"fmt"
10+
"golang_org/x/net/idna"
11+
"net"
12+
"net/url"
13+
"os"
14+
"strings"
15+
"sync"
16+
"unicode/utf8"
17+
)
18+
19+
// ProxyFromEnvironment returns the URL of the proxy to use for a
20+
// given request, as indicated by the environment variables
21+
// HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the lowercase versions
22+
// thereof). HTTPS_PROXY takes precedence over HTTP_PROXY for https
23+
// requests.
24+
//
25+
// The environment values may be either a complete URL or a
26+
// "host[:port]", in which case the "http" scheme is assumed.
27+
// An error is returned if the value is a different form.
28+
//
29+
// A nil URL and nil error are returned if no proxy is defined in the
30+
// environment, or a proxy should not be used for the given request,
31+
// as defined by NO_PROXY.
32+
//
33+
// As a special case, if req.URL.Host is "localhost" (with or without
34+
// a port number), then a nil URL and nil error will be returned.
35+
func ProxyFromEnvironment(req *Request) (*url.URL, error) {
36+
var proxy string
37+
if req.URL.Scheme == "https" {
38+
proxy = httpsProxyEnv.Get()
39+
}
40+
if proxy == "" {
41+
proxy = httpProxyEnv.Get()
42+
if proxy != "" && os.Getenv("REQUEST_METHOD") != "" {
43+
return nil, errors.New("net/http: refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy")
44+
}
45+
}
46+
if proxy == "" {
47+
return nil, nil
48+
}
49+
if !useProxy(canonicalAddr(req.URL)) {
50+
return nil, nil
51+
}
52+
proxyURL, err := url.Parse(proxy)
53+
if err != nil ||
54+
(proxyURL.Scheme != "http" &&
55+
proxyURL.Scheme != "https" &&
56+
proxyURL.Scheme != "socks5") {
57+
// proxy was bogus. Try prepending "http://" to it and
58+
// see if that parses correctly. If not, we fall
59+
// through and complain about the original one.
60+
if proxyURL, err := url.Parse("http://" + proxy); err == nil {
61+
return proxyURL, nil
62+
}
63+
64+
}
65+
if err != nil {
66+
return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err)
67+
}
68+
return proxyURL, nil
69+
}
70+
71+
// ProxyURL returns a proxy function (for use in a Transport)
72+
// that always returns the same URL.
73+
func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error) {
74+
return func(*Request) (*url.URL, error) {
75+
return fixedURL, nil
76+
}
77+
}
78+
79+
func isASCII(s string) bool {
80+
for i := 0; i < len(s); i++ {
81+
if s[i] >= utf8.RuneSelf {
82+
return false
83+
}
84+
}
85+
return true
86+
}
87+
88+
func idnaASCII(v string) (string, error) {
89+
// TODO: Consider removing this check after verifying performance is okay.
90+
// Right now punycode verification, length checks, context checks, and the
91+
// permissible character tests are all omitted. It also prevents the ToASCII
92+
// call from salvaging an invalid IDN, when possible. As a result it may be
93+
// possible to have two IDNs that appear identical to the user where the
94+
// ASCII-only version causes an error downstream whereas the non-ASCII
95+
// version does not.
96+
// Note that for correct ASCII IDNs ToASCII will only do considerably more
97+
// work, but it will not cause an allocation.
98+
if isASCII(v) {
99+
return v, nil
100+
}
101+
return idna.Lookup.ToASCII(v)
102+
}
103+
104+
// canonicalAddr returns url.Host but always with a ":port" suffix
105+
func canonicalAddr(url *url.URL) string {
106+
addr := url.Hostname()
107+
if v, err := idnaASCII(addr); err == nil {
108+
addr = v
109+
}
110+
port := url.Port()
111+
if port == "" {
112+
port = portMap[url.Scheme]
113+
}
114+
return net.JoinHostPort(addr, port)
115+
}
116+
117+
// envOnce looks up an environment variable (optionally by multiple
118+
// names) once. It mitigates expensive lookups on some platforms
119+
// (e.g. Windows).
120+
type envOnce struct {
121+
names []string
122+
once sync.Once
123+
val string
124+
}
125+
126+
func (e *envOnce) Get() string {
127+
e.once.Do(e.init)
128+
return e.val
129+
}
130+
131+
func (e *envOnce) init() {
132+
for _, n := range e.names {
133+
e.val = os.Getenv(n)
134+
if e.val != "" {
135+
return
136+
}
137+
}
138+
}
139+
140+
// reset is used by tests
141+
func (e *envOnce) reset() {
142+
e.once = sync.Once{}
143+
e.val = ""
144+
}
145+
146+
var (
147+
httpProxyEnv = &envOnce{
148+
names: []string{"HTTP_PROXY", "http_proxy"},
149+
}
150+
httpsProxyEnv = &envOnce{
151+
names: []string{"HTTPS_PROXY", "https_proxy"},
152+
}
153+
noProxyEnv = &envOnce{
154+
names: []string{"NO_PROXY", "no_proxy"},
155+
}
156+
)
157+
158+
// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
159+
// return true if the string includes a port.
160+
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
161+
162+
// useProxy reports whether requests to addr should use a proxy,
163+
// according to the NO_PROXY or no_proxy environment variable.
164+
// addr is always a canonicalAddr with a host and port.
165+
func useProxy(addr string) bool {
166+
if len(addr) == 0 {
167+
return true
168+
}
169+
host, _, err := net.SplitHostPort(addr)
170+
if err != nil {
171+
return false
172+
}
173+
if host == "localhost" {
174+
return false
175+
}
176+
if ip := net.ParseIP(host); ip != nil {
177+
if ip.IsLoopback() {
178+
return false
179+
}
180+
}
181+
182+
noProxy := noProxyEnv.Get()
183+
if noProxy == "*" {
184+
return false
185+
}
186+
187+
addr = strings.ToLower(strings.TrimSpace(addr))
188+
if hasPort(addr) {
189+
addr = addr[:strings.LastIndex(addr, ":")]
190+
}
191+
192+
for _, p := range strings.Split(noProxy, ",") {
193+
p = strings.ToLower(strings.TrimSpace(p))
194+
if len(p) == 0 {
195+
continue
196+
}
197+
if hasPort(p) {
198+
p = p[:strings.LastIndex(p, ":")]
199+
}
200+
if addr == p {
201+
return false
202+
}
203+
if len(p) == 0 {
204+
// There is no host part, likely the entry is malformed; ignore.
205+
continue
206+
}
207+
if p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:]) {
208+
// no_proxy ".foo.com" matches "bar.foo.com" or "foo.com"
209+
return false
210+
}
211+
if p[0] != '.' && strings.HasSuffix(addr, p) && addr[len(addr)-len(p)-1] == '.' {
212+
// no_proxy "foo.com" matches "bar.foo.com"
213+
return false
214+
}
215+
}
216+
return true
217+
}

src/net/http/request.go

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ import (
2525
"strconv"
2626
"strings"
2727
"sync"
28-
29-
"golang_org/x/net/idna"
3028
)
3129

3230
const (
@@ -638,22 +636,6 @@ func (r *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, waitF
638636
// This error type should not escape the net/http package to users.
639637
type requestBodyReadError struct{ error }
640638

641-
func idnaASCII(v string) (string, error) {
642-
// TODO: Consider removing this check after verifying performance is okay.
643-
// Right now punycode verification, length checks, context checks, and the
644-
// permissible character tests are all omitted. It also prevents the ToASCII
645-
// call from salvaging an invalid IDN, when possible. As a result it may be
646-
// possible to have two IDNs that appear identical to the user where the
647-
// ASCII-only version causes an error downstream whereas the non-ASCII
648-
// version does not.
649-
// Note that for correct ASCII IDNs ToASCII will only do considerably more
650-
// work, but it will not cause an allocation.
651-
if isASCII(v) {
652-
return v, nil
653-
}
654-
return idna.Lookup.ToASCII(v)
655-
}
656-
657639
// cleanHost cleans up the host sent in request's Host header.
658640
//
659641
// It both strips anything after '/' or ' ', and puts the value

0 commit comments

Comments
 (0)