47 lines
1.8 KiB
Python
47 lines
1.8 KiB
Python
"""The listening loop of the web service and of its bootstrap server.
|
|
|
|
Only the Python standard library is used, so the bootstrap server, which must run whatever else
|
|
is broken, can use it.
|
|
"""
|
|
|
|
import threading
|
|
import time
|
|
from http.server import ThreadingHTTPServer
|
|
|
|
|
|
def serve(wanted, handler, log, interval):
|
|
"""Work out where to listen every interval seconds with wanted, and run an HTTP server of handler at each place.
|
|
|
|
wanted answers ([(address, port), ...], '') or ([], the reason not to listen). A server whose
|
|
place is no longer wanted is stopped, and a new one listens at each new place; a place that
|
|
cannot be bound is tried again the next round. Every new message is logged with log. Does
|
|
not return.
|
|
"""
|
|
servers, reported = {}, None
|
|
while True:
|
|
targets, reason = wanted()
|
|
for target in [target for target in servers if target not in targets]:
|
|
server, thread = servers.pop(target)
|
|
server.shutdown()
|
|
server.server_close()
|
|
thread.join()
|
|
messages = [reason] if reason else []
|
|
for target in targets:
|
|
if target in servers:
|
|
continue
|
|
try:
|
|
server = ThreadingHTTPServer(target, handler)
|
|
except OSError as exc:
|
|
messages.append(f'cannot listen on {target[0]}:{target[1]}: {exc}')
|
|
continue
|
|
thread = threading.Thread(target=server.serve_forever, name=f'server {target[0]}', daemon=True)
|
|
thread.start()
|
|
servers[target] = (server, thread)
|
|
if servers:
|
|
messages.append('serving on ' + ', '.join(f'http://{address}:{port}' for address, port in servers))
|
|
message = '; '.join(messages)
|
|
if message and message != reported:
|
|
log(message)
|
|
reported = message
|
|
time.sleep(interval)
|