Skip to content

Commit

Permalink
add explitit .start() and .stop() functions to BackgroundHTTPServer
Browse files Browse the repository at this point in the history
This should hopefully be less confusing in terms of usage with
a Context Manager.

Signed-off-by: Jiri Jaburek <comps@nomail.dom>
  • Loading branch information
comps committed Jul 16, 2024
1 parent 8b4d79d commit e02090f
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 28 deletions.
11 changes: 6 additions & 5 deletions hardening/anaconda/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@
ks.add_package_group('Server with GUI')

# host a HTTP server with a datastream and let the guest download it
srv = util.BackgroundHTTPServer(virt.NETWORK_HOST, 0)
oscap.unselect_rules(util.get_datastream(), 'remediation-ds.xml', remediation.excludes())
srv.add_file('remediation-ds.xml')
with srv:
host, port = srv.server.server_address
with util.BackgroundHTTPServer(virt.NETWORK_HOST, 0) as srv:
oscap.unselect_rules(util.get_datastream(), 'remediation-ds.xml', remediation.excludes())
srv.add_file('remediation-ds.xml')

host, port = srv.start()

oscap_conf = {
'content-type': 'datastream',
'content-url': f'http://{host}:{port}/remediation-ds.xml',
Expand Down
5 changes: 2 additions & 3 deletions lib/osbuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,14 +335,13 @@ def create(self, *, blueprint=None, bp_verbatim=None, profile=None):

# osbuild-composer doesn't support file:// repos, so host
# the custom RPM on a HTTP server
srv = util.BackgroundHTTPServer('127.0.0.1', 0)
srv = stack.enter_context(util.BackgroundHTTPServer('127.0.0.1', 0))
srv.add_dir(repo, 'repo')
stack.enter_context(srv)
http_host, http_port = srv.start()

# overwrite default Red Hat CDN host repos via a custom HTTP server
repos = ComposerRepos()
repos.add_host_repos()
http_host, http_port = srv.server.server_address
repos.repos.append({
'name': 'contest-rpmpack',
'baseurl': f'http://{http_host}:{http_port}/repo',
Expand Down
57 changes: 45 additions & 12 deletions lib/util/httpsrv.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,39 @@
backed by (different) filesystem paths.
Ie.
srv = BackgroundHTTPServer('127.0.0.1', 8080)
srv.add_file('/on/disk/file.txt', '/visible.txt')
srv.add_file('/on/disk/dir', '/somedir')
with srv:
with BackgroundHTTPServer('127.0.0.1', 8080) as srv:
srv.add_file('/on/disk/file.txt', '/visible.txt')
srv.add_file('/on/disk/dir', '/somedir')
srv.start()
...
Any HTTP GET requests for '/visible.txt' will receive the contents of
'/on/disk/file.txt' (or 404).
Any HTTP GET requests for '/somedir/aa/bb' will receive the contents of
'/on/disk/dir/aa/bb' (or 404).
You can call 'srv.add_*' functions inside the context manager, however
You can call 'srv.add_*' functions even after calling .start(), however
be aware that this creates a potential race condition with the request-
handling code, so make sure nothing queries the server while new entries
are being added.
Port can also be specified as 0 (just like for Python's socketserver)
which will cause a random unused port to be allocated by the OS kernel.
You can then retrieve it from server_address (just like socketserver):
You can then retrieve it from the return tuple of .start():
with BackgroundHTTPServer('127.0.0.1', 0) as srv:
host, port = srv.start()
# 'host' will be '127.0.0.1' with port being >0
You can also use the server without a context manager, assuming you take
care of stopping it (manually, via try/finally, etc.):
srv = BackgroundHTTPServer('127.0.0.1', 0)
with srv:
host, port = srv.server.server_address
# 'port' here will be >0
try:
host, port = srv.start()
...
finally:
srv.stop()
"""

import shutil
Expand Down Expand Up @@ -79,6 +88,7 @@ def log_message(self, form, *args):

class BackgroundHTTPServer:
def __init__(self, host, port):
self.server = None
self.file_mapping = {}
self.dir_mapping = {}
self.requested_address = (host, port)
Expand Down Expand Up @@ -120,7 +130,15 @@ def add_dir(self, fs_path, url_path=None):
url_path = Path(fs_path) if url_path is None else Path(url_path.lstrip('/'))
self.dir_mapping[url_path] = Path(fs_path)

def __enter__(self):
def start(self):
"""
Start the HTTP server - open a listening socket, start serving requests.
The server can be shut down either by exiting a context manager (if it
was created by the context manager), or by calling .stop() manually.
Returns a (host, port) tuple the server is listening on.
"""
server = HTTPServer(self.requested_address, _BackgroundHTTPServerHandler)

host, port = server.server_address
Expand All @@ -145,11 +163,20 @@ def __enter__(self):
['firewall-cmd', f'--zone={zone}', f'--add-port={port}/tcp'],
stdout=subprocess.DEVNULL, check=True)

self.server = server
self.thread = threading.Thread(target=server.serve_forever)
self.thread.start()

def __exit__(self, exc_type, exc_value, traceback):
self.server = server
return server.server_address

def stop(self):
"""
Stop the HTTP server, close the listening socket.
"""
# if it was never started
if self.server is None:
return

host, port = self.server.server_address
util.log(f"ending: {host}:{port}")

Expand All @@ -162,3 +189,9 @@ def __exit__(self, exc_type, exc_value, traceback):
util.subprocess_run(
['firewall-cmd', f'--zone={zone}', f'--remove-port={port}/tcp'],
stdout=subprocess.DEVNULL, check=True)

def __enter__(self):
return self

def __exit__(self, exc_type, exc_value, traceback):
self.stop()
5 changes: 2 additions & 3 deletions lib/virt.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,13 +424,12 @@ def install(self, location=None, kickstart=None, rpmpack=None, disk_format='raw'

# host the custom RPM on a HTTP server, as Anaconda needs a YUM repo
# to pull packages from
srv = util.BackgroundHTTPServer(NETWORK_HOST, 0)
srv = stack.enter_context(util.BackgroundHTTPServer(NETWORK_HOST, 0))
srv.add_dir(repo, 'repo')
stack.enter_context(srv)
http_host, http_port = srv.start()

# now that we know the address/port of the HTTP server, add it to
# the kickstart as well
http_host, http_port = srv.server.server_address
kickstart.add_install_only_repo(
'contest-rpmpack',
f'http://{http_host}:{http_port}/repo',
Expand Down
11 changes: 6 additions & 5 deletions scanning/disa-alignment/anaconda.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
ks = virt.translate_ssg_kickstart(shared.profile)

# host a HTTP server with a datastream and let the guest download it
srv = util.BackgroundHTTPServer(virt.NETWORK_HOST, 0)
oscap.unselect_rules(util.get_datastream(), 'remediation-ds.xml', remediation.excludes())
srv.add_file('remediation-ds.xml')
with srv:
host, port = srv.server.server_address
with util.BackgroundHTTPServer(virt.NETWORK_HOST, 0) as srv:
oscap.unselect_rules(util.get_datastream(), 'remediation-ds.xml', remediation.excludes())
srv.add_file('remediation-ds.xml')

host, port = srv.start()

oscap_conf = {
'content-type': 'datastream',
'content-url': f'http://{host}:{port}/remediation-ds.xml',
Expand Down

0 comments on commit e02090f

Please sign in to comment.