Lib/http/ (part 2)
Source:
cpython 3.14 @ ab2d84fe1023/Lib/http/server.py
This annotation covers the server-side HTTP request handler. See lib_http_detail for http.client.HTTPConnection, HTTPResponse, and http.cookies.
Map
| Lines | Symbol | Role |
|---|---|---|
| 1-120 | HTTPServer | TCPServer subclass that binds a socket and serves requests |
| 121-350 | BaseHTTPRequestHandler | Parse request line and headers; dispatch to do_* methods |
| 351-560 | SimpleHTTPRequestHandler | Serve files from the local filesystem |
| 561-700 | CGIHTTPRequestHandler | Execute CGI scripts via subprocess |
| 701-1000 | send_response / send_header / end_headers | Write HTTP response headers |
Reading
BaseHTTPRequestHandler
# CPython: Lib/http/server.py:380 BaseHTTPRequestHandler
class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
"""HTTP request handler.
Subclass and override do_GET, do_POST, etc. to handle specific methods.
"""
def handle_one_request(self):
"""Handle a single HTTP request."""
try:
self.raw_requestline = self.rfile.readline(65537)
except TimeoutError:
self.close_connection = True
return
if not self.parse_request():
return
mname = 'do_' + self.command # e.g. 'do_GET', 'do_POST'
if not hasattr(self, mname):
self.send_error(
HTTPStatus.NOT_IMPLEMENTED,
f"Unsupported method ({self.command!r})")
return
method = getattr(self, mname)
method()
self.wfile.flush()
parse_request splits the request line into self.command, self.path, and self.request_version. Headers are parsed into self.headers (a http.client.HTTPMessage object).
send_response
# CPython: Lib/http/server.py:480 send_response
def send_response(self, code, message=None):
"""Add the response header to the headers buffer and log the response code."""
if self.request_version != 'HTTP/0.9':
if message is None:
if code in self.responses:
message = self.responses[code][0]
else:
message = ''
self._headers_buffer.append(("%s %d %s\r\n" %
(self.protocol_version, code, message)).encode("latin-1"))
self.send_header('Server', self.version_string())
self.send_header('Date', self.date_time_string())
def end_headers(self):
"""Send the blank line ending the MIME headers."""
if self.request_version != 'HTTP/0.9':
self._headers_buffer.append(b"\r\n")
self.flush_headers()
send_response + zero or more send_header calls + end_headers writes the HTTP response head. The body is then written directly to self.wfile.
SimpleHTTPRequestHandler
# CPython: Lib/http/server.py:670 SimpleHTTPRequestHandler
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
"""Serve files from the current directory and below."""
def do_GET(self):
f = self.send_head()
if f:
try:
self.copyfile(f, self.wfile)
finally:
f.close()
def send_head(self):
path = self.translate_path(self.path)
if os.path.isdir(path):
return self.list_directory(path)
ctype = self.guess_type(path)
f = open(path, 'rb')
self.send_response(HTTPStatus.OK)
self.send_header("Content-type", ctype)
self.send_header("Content-Length", str(os.fstat(f.fileno()).st_size))
self.end_headers()
return f
SimpleHTTPRequestHandler is the backend for python -m http.server. translate_path maps URL paths to filesystem paths, stripping .. components to prevent directory traversal attacks.
Range requests in SimpleHTTPRequestHandler
# CPython: Lib/http/server.py:740 send_head (range)
# If Range header is present, serve partial content (HTTP 206)
if 'Range' in self.headers:
ranges = self.headers['Range'] # e.g. 'bytes=0-1023'
...
self.send_response(HTTPStatus.PARTIAL_CONTENT)
self.send_header('Content-Range', ...)
HTTP Range requests allow resumable downloads. SimpleHTTPRequestHandler supports single-range requests from Python 3.11+.
gopy notes
http.server is pure Python over socketserver.TCPServer. HTTPServer.socket is objects.Socket. BaseHTTPRequestHandler.rfile/wfile are io.BufferedRWPair wrappers. SimpleHTTPRequestHandler.copyfile uses shutil.copyfileobj which calls io.RawIOBase.read.