Skip to content

Commit 4544fe4

Browse files
authored
tests(testserver): added support for SSL (#80)
1 parent 413a810 commit 4544fe4

File tree

6 files changed

+189
-23
lines changed

6 files changed

+189
-23
lines changed

local-requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ black==19.10b0
1111
pre-commit==2.6.0
1212
flake8==3.8.3
1313
twine==3.2.0
14+
pyOpenSSL==19.1.0
15+
service_identity==18.1.0

tests/conftest.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import playwright
2121

22-
from .server import server as server_object
22+
from .server import test_server
2323
from .utils import utils as utils_object
2424

2525

@@ -92,7 +92,12 @@ async def page(context):
9292

9393
@pytest.fixture
9494
def server():
95-
yield server_object
95+
yield test_server.server
96+
97+
98+
@pytest.fixture
99+
def https_server():
100+
yield test_server.https_server
96101

97102

98103
@pytest.fixture
@@ -101,16 +106,16 @@ def utils():
101106

102107

103108
@pytest.fixture(autouse=True, scope="session")
104-
async def start_http_server():
105-
server_object.start()
109+
async def start_server():
110+
test_server.start()
106111
yield
107-
server_object.stop()
112+
test_server.stop()
108113

109114

110115
@pytest.fixture(autouse=True)
111116
def after_each_hook():
112117
yield
113-
server_object.reset()
118+
test_server.reset()
114119

115120

116121
@pytest.fixture(scope="session")

tests/server.py

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import abc
1516
import asyncio
1617
import gzip
1718
import mimetypes
@@ -20,24 +21,30 @@
2021
import threading
2122
from contextlib import closing
2223
from http import HTTPStatus
24+
from pathlib import Path
2325

24-
from twisted.internet import reactor
26+
from OpenSSL import crypto
27+
from twisted.internet import reactor, ssl
2528
from twisted.web import http
2629

30+
_dirname = Path(os.path.join(os.path.dirname(__file__)))
2731

28-
def find_free_port():
32+
33+
def _find_free_port():
2934
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
3035
s.bind(("", 0))
3136
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
3237
return s.getsockname()[1]
3338

3439

3540
class Server:
41+
protocol = "http"
42+
3643
def __init__(self):
37-
self.PORT = find_free_port()
38-
self.EMPTY_PAGE = f"http://localhost:{self.PORT}/empty.html"
39-
self.PREFIX = f"http://localhost:{self.PORT}"
40-
self.CROSS_PROCESS_PREFIX = f"http://127.0.0.1:{self.PORT}"
44+
self.PORT = _find_free_port()
45+
self.EMPTY_PAGE = f"{self.protocol}://localhost:{self.PORT}/empty.html"
46+
self.PREFIX = f"{self.protocol}://localhost:{self.PORT}"
47+
self.CROSS_PROCESS_PREFIX = f"{self.protocol}://127.0.0.1:{self.PORT}"
4148
# On Windows, this list can be empty, reporting text/plain for scripts.
4249
mimetypes.add_type("text/html", ".html")
4350
mimetypes.add_type("text/css", ".css")
@@ -48,6 +55,10 @@ def __init__(self):
4855
def __repr__(self) -> str:
4956
return self.PREFIX
5057

58+
@abc.abstractmethod
59+
def listen(self, factory):
60+
pass
61+
5162
def start(self):
5263
request_subscribers = {}
5364
auth = {}
@@ -59,7 +70,7 @@ def start(self):
5970
self.csp = csp
6071
self.routes = routes
6172
self.gzip_routes = gzip_routes
62-
static_path = os.path.join(os.path.dirname(__file__), "assets")
73+
static_path = _dirname / "assets"
6374

6475
class TestServerHTTPHandler(http.Request):
6576
def process(self):
@@ -116,15 +127,7 @@ class MyHttp(http.HTTPChannel):
116127
class MyHttpFactory(http.HTTPFactory):
117128
protocol = MyHttp
118129

119-
reactor.listenTCP(self.PORT, MyHttpFactory())
120-
self.thread = threading.Thread(
121-
target=lambda: reactor.run(installSignalHandlers=0)
122-
)
123-
self.thread.start()
124-
125-
def stop(self):
126-
reactor.stop()
127-
self.thread.join()
130+
self.listen(MyHttpFactory())
128131

129132
async def wait_for_request(self, path):
130133
if path in self.request_subscribers:
@@ -161,4 +164,47 @@ def handle_redirect(request):
161164
self.set_route(from_, handle_redirect)
162165

163166

164-
server = Server()
167+
class HTTPServer(Server):
168+
def listen(self, factory):
169+
reactor.listenTCP(self.PORT, factory)
170+
171+
172+
class HTTPSServer(Server):
173+
protocol = "https"
174+
175+
def listen(self, factory):
176+
cert = ssl.PrivateCertificate.fromCertificateAndKeyPair(
177+
ssl.Certificate.loadPEM(
178+
(_dirname / "testserver" / "cert.pem").read_bytes()
179+
),
180+
ssl.KeyPair.load(
181+
(_dirname / "testserver" / "key.pem").read_bytes(), crypto.FILETYPE_PEM
182+
),
183+
)
184+
contextFactory = cert.options()
185+
reactor.listenSSL(self.PORT, factory, contextFactory)
186+
187+
188+
class TestServer:
189+
def __init__(self) -> None:
190+
self.server = HTTPServer()
191+
self.https_server = HTTPSServer()
192+
193+
def start(self) -> None:
194+
self.server.start()
195+
self.https_server.start()
196+
self.thread = threading.Thread(
197+
target=lambda: reactor.run(installSignalHandlers=0)
198+
)
199+
self.thread.start()
200+
201+
def stop(self) -> None:
202+
reactor.stop()
203+
self.thread.join()
204+
205+
def reset(self) -> None:
206+
self.server.reset()
207+
self.https_server.reset()
208+
209+
210+
test_server = TestServer()

tests/test_ignore_https_errors.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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 pytest
16+
17+
from playwright import Error
18+
19+
20+
async def test_ignore_https_error_should_work(browser, https_server):
21+
context = await browser.newContext(ignoreHTTPSErrors=True)
22+
page = await context.newPage()
23+
response = await page.goto(https_server.EMPTY_PAGE)
24+
assert response.ok
25+
await context.close()
26+
27+
28+
async def test_ignore_https_error_should_work_negative_case(browser, https_server):
29+
context = await browser.newContext()
30+
page = await context.newPage()
31+
with pytest.raises(Error):
32+
await page.goto(https_server.EMPTY_PAGE)
33+
await context.close()

tests/testserver/cert.pem

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIEsjCCApoCCQCIPLvQDgoZojANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9w
3+
dXBwZXRlZXItdGVzdHMwIBcNMTkwMjEzMTkwNzQzWhgPMzAxODA2MTYxOTA3NDNa
4+
MBoxGDAWBgNVBAMMD3B1cHBldGVlci10ZXN0czCCAiIwDQYJKoZIhvcNAQEBBQAD
5+
ggIPADCCAgoCggIBAJue1yqA4qn0SJR3rgTd6sCYVHMKqUouD0No09H7qf+5ZaIb
6+
3yGpC5J9Bsf/ZbvD5xpgqbGEYkHj7Qh6Z/cPCSHA+ZpsUzDXVrLFXrdwwiK1FrIS
7+
rDI2RYsiP+e52XPC/acWC/7f+E54C62oMjYojaVaDn8gu06gyS1rXK2JITQ6CrKn
8+
b+PVSkjtPB4ku245u1qCKoblkNEZSkEmw8Csl+gw6ydGqOSQAoo8rsDte5zCMnPX
9+
7XzL6EhRqpiVx7PCuQWnXhL7j9N214Pit7s7F8TeAA6yZR9oswW+h0dWO+XwocJ1
10+
rwkODXOngbCqO+GUxyuavIl2m0d2MP8n6Wa9RVqYetmPQzafKkR5hjiV4mgCFqNQ
11+
bHMTjI6udcR+h5pYoWKxN9/gJaWwyAAzck0AiMeGVrvKR3JKACqlTMzy/Y30obRF
12+
dddURoFf2wjKJvuTK9hHI7pwM5tlPEwu9bTCWNA6XXs2Bq1f6N2OAKhpKOcihNem
13+
aeGUPmygLPb66z9JO75yZXM+1yk1ScXaNHWZLmluVpEPk7maWULpSpxPAlaN3PmK
14+
8lEihgfBBovampxZo8SvPEt+g5jGyPq9weNg8ic8476PuRVQdg7D8spVxl6whDlJ
15+
bcFojzgrX70t13jqZOtla4WK1vRnZAGplfoH0i5WvAVw+i5S/OVzsmNDtGFbAgMB
16+
AAEwDQYJKoZIhvcNAQELBQADggIBADUAjA/dH+b5UxDC5SL98w1hphw9PvD1cuGS
17+
sVnKPM236JoTiO3KVfm3NMBfSoBi1hPNkXzqr/R4xbyje4Kc4oYcdjGtpll3T5da
18+
wkx1+qumx6O2mEaOshxh76dfZfZne6SQphQKHw8PD10CfDb/NMnmdEbiOSENSqS4
19+
jGELuGviUl361oCBU45UEN7lfs7ANAhwSZyEO7deroyGdvsxfQUaqQrEQsG30jn3
20+
t0cCamYU6eK3bNR/yNXJrZFv3dzoquRY9H52YtVElRqdAIsNlnbxbqz0cm5xFKFt
21+
YTIrMSO1EvDTbB0PPwC5FJvONHhjwiWzgVXSnZrcs/05TsWWnSHH92S+wGCIBC+0
22+
6fcSKnjdBn9ks5TrDX0TRY6N890KyDQWxPRhHYrMVpn833WY8y/SguxqiMgLFgMD
23+
WLy6yZzJloW7NgpLGAfMA0nMG1O92hfKmQw82Pyf3SVXGTDiXiEOXn0vN6bsPaV/
24+
3Ws2LJQECnVfHj3TsuxdtwcO+VGcFCarMOqlhE6IlQzfK8ykYdP6wCkVgXEtiVCR
25+
T1OWUWCFowoFpwBFLf1lA065qsAymddnkrUEOMiScZ/3OZhmd+FvgQ+O0iYuqpeI
26+
xauiQ68+Jb4KjVWnu5QBVq8n1vUJ5+gAzowNMN9G+1+A282Ox23T48dce22BTS6B
27+
3Taaccm+
28+
-----END CERTIFICATE-----

tests/testserver/key.pem

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCbntcqgOKp9EiU
3+
d64E3erAmFRzCqlKLg9DaNPR+6n/uWWiG98hqQuSfQbH/2W7w+caYKmxhGJB4+0I
4+
emf3DwkhwPmabFMw11ayxV63cMIitRayEqwyNkWLIj/nudlzwv2nFgv+3/hOeAut
5+
qDI2KI2lWg5/ILtOoMkta1ytiSE0Ogqyp2/j1UpI7TweJLtuObtagiqG5ZDRGUpB
6+
JsPArJfoMOsnRqjkkAKKPK7A7XucwjJz1+18y+hIUaqYlcezwrkFp14S+4/TdteD
7+
4re7OxfE3gAOsmUfaLMFvodHVjvl8KHCda8JDg1zp4GwqjvhlMcrmryJdptHdjD/
8+
J+lmvUVamHrZj0M2nypEeYY4leJoAhajUGxzE4yOrnXEfoeaWKFisTff4CWlsMgA
9+
M3JNAIjHhla7ykdySgAqpUzM8v2N9KG0RXXXVEaBX9sIyib7kyvYRyO6cDObZTxM
10+
LvW0wljQOl17NgatX+jdjgCoaSjnIoTXpmnhlD5soCz2+us/STu+cmVzPtcpNUnF
11+
2jR1mS5pblaRD5O5mllC6UqcTwJWjdz5ivJRIoYHwQaL2pqcWaPErzxLfoOYxsj6
12+
vcHjYPInPOO+j7kVUHYOw/LKVcZesIQ5SW3BaI84K1+9Ldd46mTrZWuFitb0Z2QB
13+
qZX6B9IuVrwFcPouUvzlc7JjQ7RhWwIDAQABAoICAFUvM5SejHR/taMfh/A+EZxv
14+
RfrbISPr5or9vMU6vymuMIX2P8PLJvx+19Fuah/H8p8rvnffgXGT9FIpvvMsFdGW
15+
MotnNHqNxXWCOICthnc9LTk4o22w64xnqReNUgzd9b8agGJ58w/xAmOCqEmhFTgn
16+
/bt1DVLTDIyCMm8Dm1tdUjHNGaBbRph40+mkLbz+eSHoEqNY0lbDQzQ6pfi4AUcm
17+
T/Jl6VmDwwAsi3QsCvgaDUgAMI2ZiILdwUZY5sHtmx4PKZ22elpEuWAGIJCqni4z
18+
X1CsMlJpG2XPj3lrKMqLV+B8Tt3kBVUDoig0ZybqK8QgpYeRlxodBmEFVevZOzar
19+
r/qDRh+vrQQxjpFYfrMkPiueRmz0+K1a7KiKSmrjHIb9CTi3BpgEhawbsOB7M+9z
20+
G5Q7YtGbVyJPmEAAva89ZZqYvyAwxZk3V4pwpoUYzjgPiHm6Oq0vzKPuCgQxsYzx
21+
UrCVRo7pSE4tTin4SRThY2/yHiMJl8QY//MkahgY8KEHtXE36km6pMRH/ssdSm+C
22+
SNCOtzUDY8wpaDQ++aB29NWqgnSgwoBrRUXr5NHq+wNpWtmD7L0wDSKUCPfiCduR
23+
DoSHBIno5U2jgPrH5Wk7X7loG2XxiDR0qtNOiH24SCI+C1nsLRGBS2Tmo0Qby/Ll
24+
oYYCZ0U3S7wk9UY5HcuRAoIBAQDIl8HTaIuzyOrrpsRdUv8jAxnkdhVjYhWGp7mU
25+
5concRazcEO5/vDJlsIuQI/w7U+xS7PCBPRq7NxUtaUntlQ00s224Ws0sPHIWUD0
26+
NBsodTX8hik2PdmZ5ZbBHVaeVbMV/5zV8eOPGGCsAn//7l4YIp2I2Abs79leqSDI
27+
7tBpF4IsUq7xqcVZ1QhWBZmTqE4gYDVqFEVe9O6OmAdkM1qxVSur2E5Ib+islnu8
28+
yKlu0QlXg596zLVAjxajKYf20NXxh7O+xt5QDEy3dmJEhz5viS6eI7QECM7Lid2T
29+
c22mABSKzYfbwQroM9yBiI3p0zjwRhha0hRKocLkSiNUlOWHAoIBAQDGmwRM6Xmu
30+
j7/lV3KvrOmvcNIbUwMbYY/ATY3Wuph6GFjwdliRiju9F3ZUHMV+yNlVDqH3DeRC
31+
QwGKIcFiVk+4fq1AbWVCWk2MOf49akeJwqFzgF8nkxVXF9PS73VdEvreSvy7g89t
32+
ABqJN6pmGHWVkE1mf/3LJyS2Y7WCZqSaWG3TBZ2SRb2t/t/DnX1L6tzNMkuNAizA
33+
sDB3J1hH1eGcWn/24NB9sc7i2Bk+Cpi0S/xDn9FfoBo5U0p/lpopgfFoSeQZXq1A
34+
KIQdtUPLp3KR9EG/ItimfW875zqFe8bekB9/gakyLsbIyINz1iQQS1L1FFmOO8zN
35+
RtRmm3MrG9qNAoIBAQC1v2rLFgqeVwkjgvKgbDbnjkPDkIpIhfJjE00+8AV+PyUG
36+
aE21FJ0uyf4e0jiZXyu5xJGW1c5vozTvO7XsiXM6eVYSwaPVFg28LcKAgUWqHqlP
37+
qG9myhuDKVaymtaEl7mv0O5VmtlIKhpNP+aiCWQQEi0SdEmyHI+jCTK/XEJRNg+o
38+
ATKpm91IS5FF/8Tq2LAQ/ZroBn3kT6BmarEnxLADxNvQ1Cf50gvLdH2gy19ZHOWN
39+
+aBiL2B6oissotCifQ2bzgy6ao27kalhAU6AMNoNTQqEFm1gymo0WTH+C7PpmGEE
40+
cr0KC5rKUVMVuph6p/sLGTev8nCYPoDLP7FLTa25AoIBAGmP559B0c141pR+AJVj
41+
oOoBW4vueY5KMvARyLxDfdwXqN5W6QiiotIE8H4QtOCIvQu6tVftaE/X8a+L9Y/h
42+
NIppuoiuHM5B1UodYQcfwFp2uv37U5hjU0pxfcN2R7lq5zDURrUcgFn9Xh1lGwsd
43+
IRKYGqvKiAk9CwRuxwFCsWbgba9mIrSmoQUknacJxJlfgnEGtKWEbGkWvQv4O7Ii
44+
+sHyUGXWZLsKkV59Yh1Z4ISkhrci8VSUcpvZq5VZZSN+z+OQss7RReD+KArqV9id
45+
bgYp//AqA2Gq9j6uzqo4eiG+FR/euSHVPw9llIkzXwPSJYvifx9cpaTOawMGyRY2
46+
vdkCggEBAJECl41qbQE7OIPsSmhcz0nK6L4aIkdOxZ6hs6xO7fPwh7EntojPIB6J
47+
bMuvfujqW4SZ+ZpwZkCc8p8j2VuQvlXLI+s6923IdYAOK5ND9q3Xj7AqgJjUKbhH
48+
lpYUtfDmIjqVADoiIYXmZBPAo7QvzkX7A2qclV7VL/Dc94bBS6M+v/JGh7QyTCsO
49+
oPK6IOlGL1yg+CdZIzdSiJKVeESPMOBhNtPhm+vOXvRV08ECEILD1j52rUKcPs+I
50+
uINxopeXePgekedm7nyAW3IMHFKa4EiuEU3LQOaWeKEnaxNdOh12Pyyd6w2iAmrr
51+
rj/p/2CWVN8OTi7CY5cOTCadHZRyjYA=
52+
-----END PRIVATE KEY-----

0 commit comments

Comments
 (0)