Skip to content

Commit 440bc20

Browse files
authored
Merge pull request #809 from alga/alga-https-proxy
Fix HTTPS proxy handling
2 parents ac230b7 + 3ddff27 commit 440bc20

File tree

2 files changed

+64
-6
lines changed

2 files changed

+64
-6
lines changed

tests/integration/test_proxy.py

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Test using a proxy."""
22

3+
import asyncio
34
import http.server
45
import socketserver
56
import threading
@@ -36,6 +37,35 @@ def do_GET(self):
3637
self.end_headers()
3738
self.copyfile(upstream_response, self.wfile)
3839

40+
def do_CONNECT(self):
41+
host, port = self.path.split(":")
42+
43+
asyncio.run(self._tunnel(host, port, self.connection))
44+
45+
async def _tunnel(self, host, port, client_sock):
46+
target_r, target_w = await asyncio.open_connection(host=host, port=port)
47+
48+
self.send_response(http.HTTPStatus.OK)
49+
self.end_headers()
50+
51+
source_r, source_w = await asyncio.open_connection(sock=client_sock)
52+
53+
async def channel(reader, writer):
54+
while True:
55+
data = await reader.read(1024)
56+
if not data:
57+
break
58+
writer.write(data)
59+
await writer.drain()
60+
61+
writer.close()
62+
await writer.wait_closed()
63+
64+
await asyncio.gather(
65+
channel(target_r, source_w),
66+
channel(source_r, target_w),
67+
)
68+
3969

4070
@pytest.fixture(scope="session")
4171
def proxy_server():
@@ -52,10 +82,26 @@ def test_use_proxy(tmpdir, httpbin, proxy_server):
5282
with vcr.use_cassette(str(tmpdir.join("proxy.yaml"))):
5383
response = requests.get(httpbin.url, proxies={"http": proxy_server})
5484

55-
with vcr.use_cassette(str(tmpdir.join("proxy.yaml")), mode="once") as cassette:
85+
with vcr.use_cassette(str(tmpdir.join("proxy.yaml")), mode="none") as cassette:
5686
cassette_response = requests.get(httpbin.url, proxies={"http": proxy_server})
5787

58-
for key in set(cassette_response.headers.keys()) & set(response.headers.keys()):
59-
assert cassette_response.headers[key] == response.headers[key]
6088
assert cassette_response.headers == response.headers
6189
assert cassette.play_count == 1
90+
91+
92+
def test_use_https_proxy(tmpdir, httpbin_secure, proxy_server):
93+
"""Ensure that it works with an HTTPS proxy."""
94+
with vcr.use_cassette(str(tmpdir.join("proxy.yaml"))):
95+
response = requests.get(httpbin_secure.url, proxies={"https": proxy_server})
96+
97+
with vcr.use_cassette(str(tmpdir.join("proxy.yaml")), mode="none") as cassette:
98+
cassette_response = requests.get(
99+
httpbin_secure.url,
100+
proxies={"https": proxy_server},
101+
)
102+
103+
assert cassette_response.headers == response.headers
104+
assert cassette.play_count == 1
105+
106+
# The cassette URL points to httpbin, not the proxy
107+
assert cassette.requests[0].url == httpbin_secure.url + "/"

vcr/stubs/__init__.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,22 +187,34 @@ def _port_postfix(self):
187187
"""
188188
Returns empty string for the default port and ':port' otherwise
189189
"""
190-
port = self.real_connection.port
190+
port = (
191+
self.real_connection.port
192+
if not self.real_connection._tunnel_host
193+
else self.real_connection._tunnel_port
194+
)
191195
default_port = {"https": 443, "http": 80}[self._protocol]
192196
return f":{port}" if port != default_port else ""
193197

198+
def _real_host(self):
199+
"""Returns the request host"""
200+
if self.real_connection._tunnel_host:
201+
# The real connection is to an HTTPS proxy
202+
return self.real_connection._tunnel_host
203+
else:
204+
return self.real_connection.host
205+
194206
def _uri(self, url):
195207
"""Returns request absolute URI"""
196208
if url and not url.startswith("/"):
197209
# Then this must be a proxy request.
198210
return url
199-
uri = f"{self._protocol}://{self.real_connection.host}{self._port_postfix()}{url}"
211+
uri = f"{self._protocol}://{self._real_host()}{self._port_postfix()}{url}"
200212
log.debug("Absolute URI: %s", uri)
201213
return uri
202214

203215
def _url(self, uri):
204216
"""Returns request selector url from absolute URI"""
205-
prefix = f"{self._protocol}://{self.real_connection.host}{self._port_postfix()}"
217+
prefix = f"{self._protocol}://{self._real_host()}{self._port_postfix()}"
206218
return uri.replace(prefix, "", 1)
207219

208220
def request(self, method, url, body=None, headers=None, *args, **kwargs):

0 commit comments

Comments
 (0)