Created
November 5, 2016 12:24
-
-
Save tdpreece/104f1a75eacb1d4d1ca3a61fc4398af8 to your computer and use it in GitHub Desktop.
Stub http server that can find a free port to run on
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
Demo of how to start up a simple http server on a free port in a given range. | |
I find that this can be useful in tests. | |
>$ python run_stub_server_example.py | |
*** Server 1 *** | |
Server 1: is_running=True, port=60000 | |
*** Server 2 *** | |
Port 60000 in use. | |
Server 2: is_running=True, port=60001 | |
Stopping server on port 60001. | |
Stopping server on port 60000. | |
*** Server 3 *** | |
Server 3: is_running=True, port=60000 | |
Stopping server on port 60000. | |
Traceback (most recent call last): | |
File "run_stub_server_example.py", line 87, in <module> | |
raise Exception('Server on port {} should still be stopped.'.format(server_3.port)) | |
Exception: Server on port 60000 should still be stopped. | |
''' | |
from contextlib import contextmanager | |
from os import path | |
import socket | |
import subprocess | |
from time import sleep | |
import requests | |
@contextmanager | |
def start_server(min_port, max_port): | |
server = None | |
try: | |
server = StubServer.start_on_port_in_range(max_port, min_port) | |
yield server | |
finally: | |
if server: | |
server.stop() | |
class StubServer(object): | |
@staticmethod | |
def start(port): | |
fixture_dir = path.dirname(path.realpath(__file__)) | |
p = subprocess.Popen( | |
['python', '-m', 'SimpleHTTPServer', str(port)], | |
cwd=fixture_dir, | |
stderr=subprocess.STDOUT, | |
stdout=subprocess.PIPE, | |
) | |
sleep(2) | |
return StubServer(p, port) | |
@classmethod | |
def start_on_port_in_range(cls, max_port, min_port): | |
for port in range(min_port, max_port + 1): | |
process = cls.start(port) | |
if process.is_running(): | |
break | |
print('Port {} in use.'.format(port)) | |
else: | |
raise Exception('No free ports in range {}-{}'.format(min_port, max_port)) | |
return process | |
def __init__(self, process, port): | |
self.process = process | |
self.hostname = socket.gethostname() | |
self.port = port | |
@property | |
def base_url(self): | |
return 'http://{}:{}'.format(self.hostname, self.port) | |
def is_running(self): | |
return not self.process.poll() | |
def stop(self): | |
if not self.process.poll(): | |
print('Stopping server on port {}.'.format(self.port)) | |
self.process.kill() | |
print('*** Server 1 ***') | |
server_1 = StubServer.start(60000) | |
print('Server 1: is_running={}, port={}'.format(server_1.is_running(), server_1.port)) | |
print('*** Server 2 ***') | |
with start_server(60000, 60005) as server_2: | |
print('Server 2: is_running={}, port={}'.format(server_2.is_running(), server_2.port)) | |
url = '{}/stub_data.json'.format(server_2.base_url) | |
response = requests.get(url) | |
assert {"x": 1, "y": 2} == response.json() | |
server_1.stop() | |
print('*** Server 3 ***') | |
with start_server(60000, 60005) as server_3: | |
print('Server 3: is_running={}, port={}'.format(server_3.is_running(), server_3.port)) | |
raise Exception('Server on port {} should still be stopped.'.format(server_3.port)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ "x": 1, "y": 2 } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment