Skip to content
This repository was archived by the owner on Jan 13, 2021. It is now read-only.

Commit 11c125d

Browse files
committed
Define the test server.
This is a useful way of testing things without having to actually spin up a remote server. Instead, this spins up some local sockets and lets me define expected behaviours on a test-by-test basis. Very neat. This idea (and most of its implementation) stolen wholesale from @shazow in his shazow/urllib3 module, with his kind permission.
1 parent 3e15b79 commit 11c125d

File tree

1 file changed

+90
-0
lines changed

1 file changed

+90
-0
lines changed

test/server.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
test/server
4+
~~~~~~~~~~~
5+
6+
This module defines some testing infrastructure that is very useful for
7+
integration-type testing of hyper. It works by spinning up background threads
8+
that run test-defined logic while listening to a background thread.
9+
10+
This very-clever idea and most of its implementation are ripped off from
11+
Andrey Petrov's excellent urllib3 project. I owe him a substantial debt in
12+
ingenuity and about a million beers.
13+
"""
14+
15+
import threading
16+
import socket
17+
import ssl
18+
import sys
19+
20+
class SocketServerThread(threading.Thread):
21+
"""
22+
This method stolen wholesale from shazow/urllib3.
23+
24+
:param socket_handler: Callable which receives a socket argument for one
25+
request.
26+
:param ready_event: Event which gets set when the socket handler is
27+
ready to receive requests.
28+
"""
29+
def __init__(self, socket_handler, host='localhost', port=8081,
30+
ready_event=None):
31+
threading.Thread.__init__(self)
32+
33+
self.socket_handler = socket_handler
34+
self.host = host
35+
self.ready_event = ready_event
36+
self.cxt = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
37+
self.cxt.set_npn_protocols(['HTTP-draft-09/2.0'])
38+
self.cxt.load_cert_chain(certfile='test/certs/server.crt', keyfile='test/certs/server.key')
39+
40+
def _start_server(self):
41+
sock = socket.socket(socket.AF_INET6)
42+
if sys.platform != 'win32':
43+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
44+
sock = self.cxt.wrap_socket(sock, server_side=True)
45+
sock.bind((self.host, 0))
46+
self.port = sock.getsockname()[1]
47+
48+
# Once listen() returns, the server socket is ready
49+
sock.listen(1)
50+
51+
if self.ready_event:
52+
self.ready_event.set()
53+
54+
self.socket_handler(sock)
55+
sock.close()
56+
57+
def run(self):
58+
self.server = self._start_server()
59+
60+
61+
class SocketLevelTest(object):
62+
"""
63+
A test-class that defines a few helper methods for running socket-level
64+
tests.
65+
"""
66+
def set_up(self):
67+
self.host = None
68+
self.port = None
69+
self.server_thread = None
70+
71+
def _start_server(self, socket_handler):
72+
"""
73+
Starts a background thread that runs the given socket handler.
74+
"""
75+
ready_event = threading.Event()
76+
self.server_thread = SocketServerThread(
77+
socket_handler=socket_handler,
78+
ready_event=ready_event
79+
)
80+
self.server_thread.start()
81+
ready_event.wait()
82+
self.host = self.server_thread.host
83+
self.port = self.server_thread.port
84+
85+
def tear_down(self):
86+
"""
87+
Tears down the testing thread.
88+
"""
89+
self.server_thread.join(0.1)
90+

0 commit comments

Comments
 (0)