from nbdev import *
from fastcore.utils import *
import time,socket
Python's socketserver
classes call handle
in a BaseRequestHandler
subclass that you pass in to its constructor. You use a BaseRequestHandler
with any of the server classes/mixins provided by socketserver
.
It's easiest to use allow_reuse_address
to avoid having to wait for sockets to close, especially when testing. This class adds that functionality to ThreadingTCPServer
.
Here's an example of using Python's standard library features along with ReuseThreadingServer
:
host,port = 'localhost',8000
class _TestHandler(StreamRequestHandler):
def handle(self):
print('received', self.rfile.readline())
self.wfile.write(bytes(f'pong {self.client_address[0]}\r\n', 'utf8'))
@startthread
def _f():
with ReuseThreadingServer((host,port), _TestHandler) as srv: srv.handle_request()
time.sleep(0.5) # wait for server to start
c = start_client(port,host)
c.send(b'ping\r\n')
c.recv(1024)
MinimalHTTPHandler
parses the HTTP command and headers, and sets command
, path
, request_version
, and headers
. It is based on the code in Python's BaseHTTPHandler
, but is greatly simplified, and made consistent with the other socketserver
servers.
To send a response, call send_response(code)
, optionally send_header
a few times, then end_headers
, and finally write to wfile
. For instance:
class _TestHandler(MinimalHTTPHandler):
def handle(self):
print(f'Command/path/version: {self.command} {self.path} {self.request_version}')
print(self.headers)
self.send_response(200)
self.send_header("Content-Type", "text/plain")
self.send_header('Content-Length', '2')
self.end_headers()
self.wfile.write(b'ok')
@startthread
def _f():
with ReuseThreadingServer(('localhost',8000), _TestHandler) as httpd: httpd.handle_request()
time.sleep(0.5) # wait for server to start
test_eq(urlread("http://localhost:8000"), b'ok')