-
Notifications
You must be signed in to change notification settings - Fork 17.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
1 parent
65c365b
commit 33bfd52
Showing
5 changed files
with
439 additions
and
202 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
// Copyright 2018 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 ( | ||
"errors" | ||
"fmt" | ||
"golang_org/x/net/idna" | ||
"net" | ||
"net/url" | ||
"os" | ||
"strings" | ||
"sync" | ||
"unicode/utf8" | ||
) | ||
|
||
// ProxyFromEnvironment returns the URL of the proxy to use for a | ||
// given request, as indicated by the environment variables | ||
// HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the lowercase versions | ||
// thereof). HTTPS_PROXY takes precedence over HTTP_PROXY for https | ||
// requests. | ||
// | ||
// The environment values may be either a complete URL or a | ||
// "host[:port]", in which case the "http" scheme is assumed. | ||
// An error is returned if the value is a different form. | ||
// | ||
// A nil URL and nil error are returned if no proxy is defined in the | ||
// environment, or a proxy should not be used for the given request, | ||
// as defined by NO_PROXY. | ||
// | ||
// As a special case, if req.URL.Host is "localhost" (with or without | ||
// a port number), then a nil URL and nil error will be returned. | ||
func ProxyFromEnvironment(req *Request) (*url.URL, error) { | ||
var proxy string | ||
if req.URL.Scheme == "https" { | ||
proxy = httpsProxyEnv.Get() | ||
} | ||
if proxy == "" { | ||
proxy = httpProxyEnv.Get() | ||
if proxy != "" && os.Getenv("REQUEST_METHOD") != "" { | ||
return nil, errors.New("net/http: refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy") | ||
} | ||
} | ||
if proxy == "" { | ||
return nil, nil | ||
} | ||
if !useProxy(canonicalAddr(req.URL)) { | ||
return nil, nil | ||
} | ||
proxyURL, err := url.Parse(proxy) | ||
if err != nil || | ||
(proxyURL.Scheme != "http" && | ||
proxyURL.Scheme != "https" && | ||
proxyURL.Scheme != "socks5") { | ||
// proxy was bogus. Try prepending "http://" to it and | ||
// see if that parses correctly. If not, we fall | ||
// through and complain about the original one. | ||
if proxyURL, err := url.Parse("http://" + proxy); err == nil { | ||
return proxyURL, nil | ||
} | ||
|
||
} | ||
if err != nil { | ||
return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err) | ||
} | ||
return proxyURL, nil | ||
} | ||
|
||
// ProxyURL returns a proxy function (for use in a Transport) | ||
// that always returns the same URL. | ||
func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error) { | ||
return func(*Request) (*url.URL, error) { | ||
return fixedURL, nil | ||
} | ||
} | ||
|
||
func isASCII(s string) bool { | ||
for i := 0; i < len(s); i++ { | ||
if s[i] >= utf8.RuneSelf { | ||
return false | ||
} | ||
} | ||
return true | ||
} | ||
|
||
func idnaASCII(v string) (string, error) { | ||
// TODO: Consider removing this check after verifying performance is okay. | ||
// Right now punycode verification, length checks, context checks, and the | ||
// permissible character tests are all omitted. It also prevents the ToASCII | ||
// call from salvaging an invalid IDN, when possible. As a result it may be | ||
// possible to have two IDNs that appear identical to the user where the | ||
// ASCII-only version causes an error downstream whereas the non-ASCII | ||
// version does not. | ||
// Note that for correct ASCII IDNs ToASCII will only do considerably more | ||
// work, but it will not cause an allocation. | ||
if isASCII(v) { | ||
return v, nil | ||
} | ||
return idna.Lookup.ToASCII(v) | ||
} | ||
|
||
// canonicalAddr returns url.Host but always with a ":port" suffix | ||
func canonicalAddr(url *url.URL) string { | ||
addr := url.Hostname() | ||
if v, err := idnaASCII(addr); err == nil { | ||
addr = v | ||
} | ||
port := url.Port() | ||
if port == "" { | ||
port = portMap[url.Scheme] | ||
} | ||
return net.JoinHostPort(addr, port) | ||
} | ||
|
||
// envOnce looks up an environment variable (optionally by multiple | ||
// names) once. It mitigates expensive lookups on some platforms | ||
// (e.g. Windows). | ||
type envOnce struct { | ||
names []string | ||
once sync.Once | ||
val string | ||
} | ||
|
||
func (e *envOnce) Get() string { | ||
e.once.Do(e.init) | ||
return e.val | ||
} | ||
|
||
func (e *envOnce) init() { | ||
for _, n := range e.names { | ||
e.val = os.Getenv(n) | ||
if e.val != "" { | ||
return | ||
} | ||
} | ||
} | ||
|
||
// reset is used by tests | ||
func (e *envOnce) reset() { | ||
e.once = sync.Once{} | ||
e.val = "" | ||
} | ||
|
||
var ( | ||
httpProxyEnv = &envOnce{ | ||
names: []string{"HTTP_PROXY", "http_proxy"}, | ||
} | ||
httpsProxyEnv = &envOnce{ | ||
names: []string{"HTTPS_PROXY", "https_proxy"}, | ||
} | ||
noProxyEnv = &envOnce{ | ||
names: []string{"NO_PROXY", "no_proxy"}, | ||
} | ||
) | ||
|
||
// Given a string of the form "host", "host:port", or "[ipv6::address]:port", | ||
// return true if the string includes a port. | ||
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") } | ||
|
||
// useProxy reports whether requests to addr should use a proxy, | ||
// according to the NO_PROXY or no_proxy environment variable. | ||
// addr is always a canonicalAddr with a host and port. | ||
func useProxy(addr string) bool { | ||
if len(addr) == 0 { | ||
return true | ||
} | ||
host, _, err := net.SplitHostPort(addr) | ||
if err != nil { | ||
return false | ||
} | ||
if host == "localhost" { | ||
return false | ||
} | ||
if ip := net.ParseIP(host); ip != nil { | ||
if ip.IsLoopback() { | ||
return false | ||
} | ||
} | ||
|
||
noProxy := noProxyEnv.Get() | ||
if noProxy == "*" { | ||
return false | ||
} | ||
|
||
addr = strings.ToLower(strings.TrimSpace(addr)) | ||
if hasPort(addr) { | ||
addr = addr[:strings.LastIndex(addr, ":")] | ||
} | ||
|
||
for _, p := range strings.Split(noProxy, ",") { | ||
p = strings.ToLower(strings.TrimSpace(p)) | ||
if len(p) == 0 { | ||
continue | ||
} | ||
if hasPort(p) { | ||
p = p[:strings.LastIndex(p, ":")] | ||
} | ||
if addr == p { | ||
return false | ||
} | ||
if len(p) == 0 { | ||
// There is no host part, likely the entry is malformed; ignore. | ||
continue | ||
} | ||
if p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:]) { | ||
// no_proxy ".foo.com" matches "bar.foo.com" or "foo.com" | ||
return false | ||
} | ||
if p[0] != '.' && strings.HasSuffix(addr, p) && addr[len(addr)-len(p)-1] == '.' { | ||
// no_proxy "foo.com" matches "bar.foo.com" | ||
return false | ||
} | ||
} | ||
return true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.