Real loopback HTTP/1.1 framing experiment, macOS27.0 arm64/Python3.14.4. Run python3 lab.py for offline checks plus network cases; --self-check is strictly offline and needs no listening socket. No dependencies installed, external traffic, system network configuration or production endpoints used. All wire/application bytes are retained as hex in run.jsonl. Successful network case:127.0.0.1:59744, one accept, two actual request lines: GET /fixed HTTP/1.1 and GET /chunked HTTP/1.1. Each includes Host with the actual loopback port. Client sends both requests before reading responses; this is HTTP pipelining in request order, not two independent connections or a measured proxy. Server actually parses both requests and sends the two responses in order. Server request recv calls1, client response recv calls1. These are application recv return counts, NOT TCP segment counts or packet capture. Future reruns may have different counts without changing correctness. First response is Content-Length5 and actual body hello (68656c6c6f). Second is Transfer-Encoding:chunked, chunks2:wo and3:rld, zero last chunk, trailer X-Lab:done, then final empty CRLF line. Body world (776f726c64) is separate from trailers [['x-lab','done']]. After the first response the entire second115-byte response remains buffered; after parsing it, buffer is empty. The response pair is158 bytes in this run. Content-Length counts body octets; chunk sizes count each data chunk's octets, excluding chunk syntax/trailers. The parser does not consume the next response as part of the previous body. SO_KEEPALIVE readback is0 on both client and accepted server socket. Two HTTP requests/responses still use that same TCP connection. This demonstrates the separation of HTTP connection persistence and the TCP keepalive socket option in this case; it does not test keepalive timers/probes or dead-peer detection. The second response includes Connection:close; the server ends the connection after that response. No HTTP/2 multiplexing or concurrent-response claim. Malformed real response case:127.0.0.1:59746, one accept/one GET /bad. Server sends actual headers containing both Content-Length0 and Transfer-Encoding:chunked. Client's teaching policy rejects this ambiguity with FramingError and exits its socket context. Server's subsequent recv(1) actually returns empty bytes, retained as received_after_response_hex:"". Thus rejection and local peer EOF are observed; no TCP packet capture or claim about a particular FIN/RST packet is made. This is a deliberately strict handling policy, not a complete implementation of every RFC recipient or intermediary recovery option. No unsafe request is forwarded anywhere. Offline checks are separately labelled offline-input-splits-not-TCP-segments: feed the same158 bytes in widths1,2,7 and158; all yield hello/world and the same independently stored trailer. Buffered remainder after first response varies with feed width; full-width test requires the whole second response remain. These splits validate parser input boundaries, not operating-system TCP segmentation or fragmentation. HEAD test provides Content-Length999 but no body, followed immediately by a normal response. HEAD consumes headers only and the subsequent hello response still parses. No-body method/status handling precedes body-length selection. The teaching parser also treats1xx/204/304 as bodyless but does not implement an informational-response aggregation workflow or validate every forbidden header combination for those statuses. HEAD was verified offline, not by an actual network HEAD request in this run. Offline rejected cases: CL+TE, Content-Length body cut short, final empty line missing after chunk trailers, bare LF, duplicate Content-Length fields, and oversized Content-Length. Incomplete inputs raise unexpected EOF rather than returning a successful short body. Duplicate fields are all rejected even where broader HTTP rules allow combination; this is an intentionally stricter subset. Chunk extensions and transfer codings other than plain chunked are rejected; trailers are restricted to X-Lab instead of attempting a general security-sensitive trailer policy. Unsupported close-delimited responses are rejected, not silently interpreted as bodyless. No CONNECT, Upgrade, proxy, TLS, decompression, retry policy or production message-smuggling guarantee. Bounds: socket accept/connect/recv/send timeouts3s, server thread join4s; line max8192B, field section8192B/max32 fields, buffer131072B, body65536B, chunk loop max128. Strict CRLF and printable-ASCII field subset (plus HTAB), no obs-fold. Limits and timeout checks cover the local finite scripts, not a demonstrated resistance to arbitrary hostile slow peers or all HTTP input. Reader is frozen as an object but deliberately owns mutable buffer/read trace; application bytes come from actual recv calls, never reconstructed from source strings. Expected constants are used solely to generate responses and assert what the client parsed. Actual network run succeeded on first authorized execution, exit0. The tool required previously established local-socket authorization; no rejected write or prohibited program was bypassed. Both server threads joined, all listener/ client/accepted sockets exited context managers. Ports are ephemeral each run. CLI --help0, --self-check0, --bad2. Python no-excuse checker zero violations; diff --check passed. No commit/push by this worker. Primary source checked during implementation: https://www.rfc-editor.org/rfc/rfc9112.html (sections6.3,7.1,9.3) Parent chapter also checked RFC9110 request semantics and Connection fields.