Skip to content

Instantly share code, notes, and snippets.

@tdpreece
Created November 5, 2016 12:24
Show Gist options
  • Save tdpreece/104f1a75eacb1d4d1ca3a61fc4398af8 to your computer and use it in GitHub Desktop.
Save tdpreece/104f1a75eacb1d4d1ca3a61fc4398af8 to your computer and use it in GitHub Desktop.
Stub http server that can find a free port to run on
'''
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))
{ "x": 1, "y": 2 }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment