Skip to content

Commit bcee6e3

Browse files
authored
test: add some proxy tests (microsoft#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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy