import socket
from pyhttp.requests import BaseHttpRequestHandler
import threading
[docs]class BaseServer(object):
"""
Basic class for a simple http server. Accepts client connections and passes off
to a :class:`RequestHandler <BaseHttpRequestHandler>` class to for processing and responding.
:param host: Host address to listen on
:type host: str
:param port: Port to listen on
:type port: int
"""
def __init__(self, host='localhost', port=8888,
request_handler=BaseHttpRequestHandler):
self.host = host
self.port = port
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.requestHandler = request_handler
[docs] def serve_forever(self):
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind((self.host, self.port))
self.socket.listen(1)
print(f'Serving at {self.host}:{self.port}')
try:
while True:
client_conn, client_addr = self.socket.accept()
self.process_request(client_conn, client_addr)
except KeyboardInterrupt:
print("Received keyboard interrupt")
self.close()
def process_request(self, conn, addr):
"""
Calls finish request. Serves as one layer of indirection
to allow for overriding by a threading mixin.
:param conn: Open client socket
:param addr: Client socket address
"""
self.finish_request(conn, addr)
def finish_request(self, conn, addr):
self.requestHandler(conn, addr, self)
def close(self):
# Not sure that we need this first line
# self.socket.shutdown(socket.SHUT_RDWR)
self.socket.close()
[docs]class ThreadingMixin:
"""
A threading mixing class that overrides the :func:`process_request`
function. Rather than immediately calling the request handler class,
it creates a processing thread whose target function is
:func:`threaded_process_request`. Also keeps track of all threads created
and waits for them to finish before closing the connection.
"""
_threads = None
[docs] def process_request(self, conn, addr):
"""
Overrides :func:`process_request` in :class:`BaseServer`. Passes
arguments to a function to be run in separate thread. Stores the thread
in the class attribute list :attr:`_threads`.
:param conn: Open client socket
:param addr: Client socket address
"""
thread = threading.Thread(target=self.threaded_process_request,
args=(conn, addr))
thread.daemon = False
if self._threads is None:
self._threads = []
self._threads.append(thread)
thread.start()
def threaded_process_request(self, conn, addr):
self.finish_request(conn, addr)
[docs] def close(self):
"""
Calls :func:`BaseServer.close`. Joins threads before shutting down.
"""
super(ThreadingMixin, self).close()
threads = self._threads
self._threads = None
if threads is not None:
for thread in threads:
thread.join()
[docs]class ThreadedServer(ThreadingMixin, BaseServer):
"""
Uses the basic functionality and call api of BaseServer.
Overrides the initial processing of a request to put it into a thread.
For details see :class:`ThreadingMixin <pyhttp.ThreadingMixin>`
"""
pass