-
Notifications
You must be signed in to change notification settings - Fork 180
/
Copy pathConnectionRequest.jl
129 lines (106 loc) · 3.73 KB
/
ConnectionRequest.jl
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
module ConnectionRequest
import ..Layer, ..request
using ..URIs, ..Sockets
using ..Messages
using ..IOExtras
using ..ConnectionPool
using MbedTLS: SSLContext
import ..@debug, ..DEBUG_LEVEL
# hasdotsuffix reports whether s ends in "."+suffix.
hasdotsuffix(s, suffix) = endswith(s, "." * suffix)
function isnoproxy(host)
for x in NO_PROXY
(hasdotsuffix(host, x) || (host == x)) && return true
end
return false
end
const NO_PROXY = String[]
function __init__()
# check for no_proxy environment variable
if haskey(ENV, "no_proxy")
for x in split(ENV["no_proxy"], ","; keepempty=false)
push!(NO_PROXY, startswith(x, ".") ? x[2:end] : x)
end
end
return
end
function getproxy(scheme, host)
isnoproxy(host) && return nothing
if scheme == "http" && haskey(ENV, "http_proxy")
return ENV["http_proxy"]
elseif scheme == "https" && haskey(ENV, "https_proxy")
return ENV["https_proxy"]
end
return nothing
end
"""
request(ConnectionPoolLayer, ::URI, ::Request, body) -> HTTP.Response
Retrieve an `IO` connection from the [`ConnectionPool`](@ref).
Close the connection if the request throws an exception.
Otherwise leave it open so that it can be reused.
`IO` related exceptions from `Base` are wrapped in `HTTP.IOError`.
See [`isioerror`](@ref).
"""
abstract type ConnectionPoolLayer{Next <: Layer} <: Layer{Next} end
export ConnectionPoolLayer
function request(::Type{ConnectionPoolLayer{Next}}, url::URI, req, body;
proxy=getproxy(url.scheme, url.host),
socket_type::Type=TCPSocket,
reuse_limit::Int=ConnectionPool.nolimit, kw...) where Next
if proxy !== nothing
target_url = url
url = URI(proxy)
if target_url.scheme == "http"
req.target = string(target_url)
end
end
IOType = ConnectionPool.Transaction{sockettype(url, socket_type)}
local io
try
io = getconnection(IOType, url.host, url.port;
reuse_limit=reuse_limit, kw...)
catch e
rethrow(isioerror(e) ? IOError(e, "during request($url)") : e)
end
if io.sequence >= reuse_limit
defaultheader!(req, "Connection" => "close")
end
try
if proxy !== nothing && target_url.scheme == "https"
target_url = merge(target_url, port=443)
return tunnel_request(Next, io, target_url, req, body; kw...)
end
r = request(Next, io, req, body; kw...)
if (io.sequence >= reuse_limit
|| (proxy !== nothing && target_url.scheme == "https"))
close(io)
end
return r
catch e
@debug 1 "❗️ ConnectionLayer $e. Closing: $io"
try; close(io); catch; end
rethrow(isioerror(e) ? IOError(e, "during request($url)") : e)
end
end
sockettype(url::URI, default) = url.scheme in ("wss", "https") ? SSLContext :
default
function tunnel_request(Next, io, target_url, req, body; kw...)
r = connect_tunnel(io, target_url, req)
if r.status != 200
return r
end
io = ConnectionPool.sslupgrade(io, target_url.host; kw...)
req.headers = filter(x->x.first != "Proxy-Authorization", req.headers)
return request(Next, io, req, body; kw...)
end
function connect_tunnel(io, target_url, req)
target = "$(URIs.hoststring(target_url.host)):$(target_url.port)"
@debug 1 "📡 CONNECT HTTPS tunnel to $target"
headers = Dict(filter(x->x.first == "Proxy-Authorization", req.headers))
request = Request("CONNECT", target, headers)
writeheaders(io, request)
startread(io)
readheaders(io, request.response)
return request.response
end
end # module ConnectionRequest