Skip to content

Commit bcee6e3

Browse files
authored
test: add some proxy tests (#447)
1 parent 15fa866 commit bcee6e3

File tree

6 files changed

+286
-24
lines changed

6 files changed

+286
-24
lines changed

tests/async/conftest.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,16 @@ def browser_type(playwright, browser_name: str):
4848

4949
@pytest.fixture(scope="session")
5050
async def browser_factory(launch_arguments, browser_type):
51+
browsers = []
52+
5153
async def launch(**kwargs):
52-
return await browser_type.launch(**launch_arguments, **kwargs)
54+
browser = await browser_type.launch(**launch_arguments, **kwargs)
55+
browsers.append(browser)
56+
return browser
5357

54-
return launch
58+
yield launch
59+
for browser in browsers:
60+
await browser.close()
5561

5662

5763
@pytest.fixture(scope="session")
@@ -62,8 +68,22 @@ async def browser(browser_factory):
6268

6369

6470
@pytest.fixture
65-
async def context(browser):
66-
context = await browser.new_context()
71+
async def context_factory(browser):
72+
contexts = []
73+
74+
async def launch(**kwargs):
75+
context = await browser.new_context(**kwargs)
76+
contexts.append(context)
77+
return context
78+
79+
yield launch
80+
for context in contexts:
81+
await context.close()
82+
83+
84+
@pytest.fixture
85+
async def context(context_factory):
86+
context = await context_factory()
6787
yield context
6888
await context.close()
6989

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Copyright (c) Microsoft Corporation.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import base64
16+
17+
import pytest
18+
19+
20+
@pytest.fixture(scope="session")
21+
async def browser(browser_factory):
22+
browser = await browser_factory(proxy={"server": "dummy"})
23+
yield browser
24+
await browser.close()
25+
26+
27+
async def test_should_use_proxy(context_factory, server):
28+
server.set_route(
29+
"/target.html",
30+
lambda r: (
31+
r.write(b"<html><title>Served by the proxy</title></html>"),
32+
r.finish(),
33+
),
34+
)
35+
context = await context_factory(proxy={"server": f"localhost:{server.PORT}"})
36+
page = await context.new_page()
37+
await page.goto("http://non-existent.com/target.html")
38+
assert await page.title() == "Served by the proxy"
39+
40+
41+
async def test_should_use_proxy_for_second_page(context_factory, server):
42+
server.set_route(
43+
"/target.html",
44+
lambda r: (
45+
r.write(b"<html><title>Served by the proxy</title></html>"),
46+
r.finish(),
47+
),
48+
)
49+
context = await context_factory(proxy={"server": f"localhost:{server.PORT}"})
50+
51+
page1 = await context.new_page()
52+
await page1.goto("http://non-existent.com/target.html")
53+
assert await page1.title() == "Served by the proxy"
54+
55+
page2 = await context.new_page()
56+
await page2.goto("http://non-existent.com/target.html")
57+
assert await page2.title() == "Served by the proxy"
58+
59+
60+
async def test_should_work_with_ip_port_notion(context_factory, server):
61+
server.set_route(
62+
"/target.html",
63+
lambda r: (
64+
r.write(b"<html><title>Served by the proxy</title></html>"),
65+
r.finish(),
66+
),
67+
)
68+
context = await context_factory(proxy={"server": f"127.0.0.1:{server.PORT}"})
69+
page = await context.new_page()
70+
await page.goto("http://non-existent.com/target.html")
71+
assert await page.title() == "Served by the proxy"
72+
73+
74+
async def test_should_authenticate(context_factory, server):
75+
def handler(req):
76+
print(req)
77+
auth = req.getHeader("proxy-authorization")
78+
if not auth:
79+
req.setHeader(
80+
b"Proxy-Authenticate", b'Basic realm="Access to internal site"'
81+
)
82+
req.setResponseCode(407)
83+
else:
84+
req.write(f"<html><title>{auth}</title></html>".encode("utf-8"))
85+
req.finish()
86+
87+
server.set_route("/target.html", handler)
88+
89+
context = await context_factory(
90+
proxy={
91+
"server": f"localhost:{server.PORT}",
92+
"username": "user",
93+
"password": "secret",
94+
}
95+
)
96+
page = await context.new_page()
97+
await page.goto("http://non-existent.com/target.html")
98+
assert await page.title() == "Basic " + base64.b64encode(b"user:secret").decode(
99+
"utf-8"
100+
)
101+
102+
103+
async def test_should_authenticate_with_empty_password(context_factory, server):
104+
def handler(req):
105+
print(req)
106+
auth = req.getHeader("proxy-authorization")
107+
if not auth:
108+
req.setHeader(
109+
b"Proxy-Authenticate", b'Basic realm="Access to internal site"'
110+
)
111+
req.setResponseCode(407)
112+
else:
113+
req.write(f"<html><title>{auth}</title></html>".encode("utf-8"))
114+
req.finish()
115+
116+
server.set_route("/target.html", handler)
117+
118+
context = await context_factory(
119+
proxy={"server": f"localhost:{server.PORT}", "username": "user", "password": ""}
120+
)
121+
page = await context.new_page()
122+
await page.goto("http://non-existent.com/target.html")
123+
assert await page.title() == "Basic " + base64.b64encode(b"user:").decode("utf-8")

tests/async/test_interception.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -460,13 +460,6 @@ async def test_page_route_should_work_with_encoded_server(page, server):
460460
assert response.status == 404
461461

462462

463-
async def test_page_route_should_work_with_badly_encoded_server(page, server):
464-
server.set_route("/malformed?rnd=%911", lambda req: req.finish())
465-
await page.route("**/*", lambda route: route.continue_())
466-
response = await page.goto(server.PREFIX + "/malformed?rnd=%911")
467-
assert response.status == 200
468-
469-
470463
async def test_page_route_should_work_with_encoded_server___2(page, server):
471464
# The requestWillBeSent will report URL as-is, whereas interception will
472465
# report encoded URL for stylesheet. @see crbug.com/759388

tests/async/test_proxy.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Copyright (c) Microsoft Corporation.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import base64
16+
17+
import pytest
18+
19+
from playwright.async_api import Error
20+
21+
22+
async def test_should_throw_for_bad_server_value(browser_factory):
23+
with pytest.raises(Error) as exc_info:
24+
await browser_factory(proxy={"server": 123})
25+
assert "proxy.server: expected string, got number" in exc_info.value.message
26+
27+
28+
async def test_should_use_proxy(browser_factory, server):
29+
server.set_route(
30+
"/target.html",
31+
lambda r: (
32+
r.write(b"<html><title>Served by the proxy</title></html>"),
33+
r.finish(),
34+
),
35+
)
36+
browser = await browser_factory(proxy={"server": f"localhost:{server.PORT}"})
37+
page = await browser.new_page()
38+
await page.goto("http://non-existent.com/target.html")
39+
assert await page.title() == "Served by the proxy"
40+
41+
42+
async def test_should_use_proxy_for_second_page(browser_factory, server):
43+
server.set_route(
44+
"/target.html",
45+
lambda r: (
46+
r.write(b"<html><title>Served by the proxy</title></html>"),
47+
r.finish(),
48+
),
49+
)
50+
browser = await browser_factory(proxy={"server": f"localhost:{server.PORT}"})
51+
52+
page1 = await browser.new_page()
53+
await page1.goto("http://non-existent.com/target.html")
54+
assert await page1.title() == "Served by the proxy"
55+
56+
page2 = await browser.new_page()
57+
await page2.goto("http://non-existent.com/target.html")
58+
assert await page2.title() == "Served by the proxy"
59+
60+
61+
async def test_should_work_with_ip_port_notion(browser_factory, server):
62+
server.set_route(
63+
"/target.html",
64+
lambda r: (
65+
r.write(b"<html><title>Served by the proxy</title></html>"),
66+
r.finish(),
67+
),
68+
)
69+
browser = await browser_factory(proxy={"server": f"127.0.0.1:{server.PORT}"})
70+
page = await browser.new_page()
71+
await page.goto("http://non-existent.com/target.html")
72+
assert await page.title() == "Served by the proxy"
73+
74+
75+
async def test_should_authenticate(browser_factory, server):
76+
def handler(req):
77+
print(req)
78+
auth = req.getHeader("proxy-authorization")
79+
if not auth:
80+
req.setHeader(
81+
b"Proxy-Authenticate", b'Basic realm="Access to internal site"'
82+
)
83+
req.setResponseCode(407)
84+
else:
85+
req.write(f"<html><title>{auth}</title></html>".encode("utf-8"))
86+
req.finish()
87+
88+
server.set_route("/target.html", handler)
89+
90+
browser = await browser_factory(
91+
proxy={
92+
"server": f"localhost:{server.PORT}",
93+
"username": "user",
94+
"password": "secret",
95+
}
96+
)
97+
page = await browser.new_page()
98+
await page.goto("http://non-existent.com/target.html")
99+
assert await page.title() == "Basic " + base64.b64encode(b"user:secret").decode(
100+
"utf-8"
101+
)
102+
103+
104+
async def test_should_authenticate_with_empty_password(browser_factory, server):
105+
def handler(req):
106+
print(req)
107+
auth = req.getHeader("proxy-authorization")
108+
if not auth:
109+
req.setHeader(
110+
b"Proxy-Authenticate", b'Basic realm="Access to internal site"'
111+
)
112+
req.setResponseCode(407)
113+
else:
114+
req.write(f"<html><title>{auth}</title></html>".encode("utf-8"))
115+
req.finish()
116+
117+
server.set_route("/target.html", handler)
118+
119+
browser = await browser_factory(
120+
proxy={"server": f"localhost:{server.PORT}", "username": "user", "password": ""}
121+
)
122+
page = await browser.new_page()
123+
await page.goto("http://non-existent.com/target.html")
124+
assert await page.title() == "Basic " + base64.b64encode(b"user:").decode("utf-8")

tests/conftest.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ def assetdir():
5050
def launch_arguments(pytestconfig):
5151
return {
5252
"headless": not pytestconfig.getoption("--headful"),
53-
"chromium_sandbox": False,
5453
}
5554

5655

tests/server.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import threading
2121
from contextlib import closing
2222
from http import HTTPStatus
23+
from urllib.parse import urlparse
2324

2425
from autobahn.twisted.websocket import WebSocketServerFactory, WebSocketServerProtocol
2526
from OpenSSL import crypto
@@ -78,18 +79,20 @@ def process(self):
7879
request = self
7980
self.post_body = request.content.read()
8081
request.content.seek(0, 0)
81-
uri = request.uri.decode()
82-
if request_subscribers.get(uri):
83-
request_subscribers[uri].set_result(request)
84-
request_subscribers.pop(uri)
82+
uri = urlparse(request.uri.decode())
83+
path = uri.path
8584

86-
if auth.get(uri):
85+
if request_subscribers.get(path):
86+
request_subscribers[path].set_result(request)
87+
request_subscribers.pop(path)
88+
89+
if auth.get(path):
8790
authorization_header = request.requestHeaders.getRawHeaders(
8891
"authorization"
8992
)
9093
creds_correct = False
9194
if authorization_header:
92-
creds_correct = auth.get(uri) == (
95+
creds_correct = auth.get(path) == (
9396
request.getUser(),
9497
request.getPassword(),
9598
)
@@ -100,19 +103,19 @@ def process(self):
100103
request.setResponseCode(HTTPStatus.UNAUTHORIZED)
101104
request.finish()
102105
return
103-
if csp.get(uri):
104-
request.setHeader(b"Content-Security-Policy", csp[uri])
105-
if routes.get(uri):
106-
routes[uri](request)
106+
if csp.get(path):
107+
request.setHeader(b"Content-Security-Policy", csp[path])
108+
if routes.get(path):
109+
routes[path](request)
107110
return
108111
file_content = None
109112
try:
110113
file_content = (
111114
static_path / request.path.decode()[1:]
112115
).read_bytes()
113-
request.setHeader(b"Content-Type", mimetypes.guess_type(uri)[0])
116+
request.setHeader(b"Content-Type", mimetypes.guess_type(path)[0])
114117
request.setHeader(b"Cache-Control", "no-cache, no-store")
115-
if uri in gzip_routes:
118+
if path in gzip_routes:
116119
request.setHeader("Content-Encoding", "gzip")
117120
request.write(gzip.compress(file_content))
118121
else:

0 commit comments

Comments
 (0)