A persistent pdfTeX process that speaks HTTP/1.1. The C parent binds 127.0.0.1 TCP and splices accepted connections onto named FIFOs — not a second HTTP implementation.
client → TCP → splice → FIFOs → pdftex
The listen socket stays bound. Extra connections wait in the accept queue. One pdfTeX handles them one at a time.
Routing, methods, MIME types, status phrases, and the calendar are tables. The parser turns bytes into a Request record (ok | bad | end); handlers never look at the wire.
| method | path | handler |
|---|---|---|
| GET, HEAD | / |
www/index.html |
| GET, HEAD | /about |
generated |
| GET, HEAD | /status |
hit count, uptime |
| GET, HEAD | /health |
ok |
| GET, HEAD | /teapot |
418 |
| POST | /echo |
form/query keys |
| GET, HEAD | other | www/ if the extension is in the MIME table |
cc(POSIX)pdftex(TeX Live)
make
./bin/texhttpd
# http://127.0.0.1:8080/Optional port (0 asks the kernel for a free one; the bound port is printed):
./bin/texhttpd 8080make test builds the server and tests/smoke, then plays tests/cases.tsv.
- One request in flight. The listen socket stays up; browsers may queue.
- One pdfTeX child. No keep-alive, TLS, or prefork. A second process on the same port gets
EADDRINUSE. - Static files are disk bytes. The MIME table is the allow-list (
html css js json txt svg xml png gif jpeg jpg ico webp woff woff2 wasm bin). Missing row is 404. - pdfTeX is started with
-8bitso\writeemits bytes 0–255.\write's extra LF is not inContent-Length; clients stop at the entity. - A static file larger than 100000 bytes is 500 (
buf_sizeis 200000; dump hex is 2× the file). Dateis\pdfcreationdateplus\pdfelapsedtime(no shell, no bashdate). Elapsed time wraps after about nine hours.- Header lines end with LF. TeX
\writecannot emit a raw CR, so CRLF is unrepresentable without leaving TeX. - TeX
buf_sizecaps a line; URIs longer than 2048 are 414.
0BSD. See LICENSE.