Skip to content

jhttpd: a web server you could actually run, and what that needs that a harness does not #239

Description

@xoloki

net::http::server went from a loopback harness to something that routes,
caches, ranges and serves a directory (#217-#227). The next thing it wants is a
program: jhttpd, a web server you could point at a port and leave running.

This issue is the shape of that, and an honest assessment of the stretch goals,
because one of them is not like the others.

What the library already gives it

Most of a static server, in fact:

  • request parsing against the pasted RFC 9112 grammar, refusing both smuggling
    primitives
  • keep-alive with three separate timeouts, chunked output, HEAD, 405 with
    Allow, conditional requests, byte ranges
  • files() -- containment by realpath, streamed a block at a time, counted
    rather than chunked, weak validators from stat
  • route patterns with captures and a most-specific-wins rule
  • TLS, and an async server where a connection is a coroutine rather than a
    thread

What it needs before anyone should run it

Roughly in the order that matters:

Each of those is an issue of its own when somebody starts; listing them here so
the size is visible.

The stretch goals

Reading nginx and Apache config -- achievable, and a lot of work

Both are custom formats and both are parseable. nginx's is a small grammar --
blocks, directives, semicolons -- and jlib has an ABNF engine that would eat
it. Apache's is line-oriented with <Section> containers.

Parsing is the easy half. nginx has several hundred directives across its
core modules; Apache has more. A config file is only "read" to the extent the
directives in it are implemented, and a server that parses gzip on; and
ignores it is worse than one that refuses the file, because the operator
believes something untrue.

So the useful version is bounded: a stated subset -- listen, server_name,
root, location, index, try_files, ssl_certificate, access_log --
that covers a static site, and a hard error on any directive not
implemented
. That is genuinely useful and honest. "Reads nginx.conf" without
that qualifier is not a goal that can be met.

Apache modules -- not reachable, and worth saying why

An Apache module is a shared object compiled against Apache's headers. Loading
one means providing, binary-compatibly:

  • APR (Apache Portable Runtime) and APR-util -- pools, tables, buckets,
    a whole allocator discipline the module calls into constantly
  • the hook system: ap_hook_handler, ap_hook_translate_name,
    ap_hook_fixups and a few dozen more, each with its ordering semantics
  • request_rec, conn_rec, server_rec -- large structs whose layout is the
    ABI
  • bucket brigades, which is how every output filter works

That is not a feature of a web server; it is a reimplementation of Apache's
internals with byte-compatible structures. mod_perl and mod_php are written
against it in detail. This one should be dropped, and what replaces it is
worth having:

A jlib-native module interface. dlopen a shared object exporting a known
entry point, hand it a chance to register routes and filters against jlib's own
types. That gets the actual value -- third-party code extending the server
without recompiling it -- with an interface this tree can define and document,
instead of one it would have to reverse-engineer and could never quite match.

"Drop-in replacement" -- worth restating as something achievable

For a static site, this is realistic: same config subset, same document
root, same TLS certificates, same log format. Somebody could stop nginx, start
jhttpd, and not notice.

For anything with modules, rewrite rules, FastCGI pools or .htaccess, it is
not, and pursuing it would turn a personal library into a compatibility project
with an unbounded surface.

The thing this changes upstream

http_server.hh says "It is not hardened for a public port", and
sys/server.hh says the same. Those sentences were load-bearing -- they are
the reason several features were deliberately left out.

jhttpd is a decision to face a public port. That does not make the sentences
false today, but it means they stop being a scope statement and become a
list of work: the things they excuse are exactly #237, #238, and the
hardening above. Whoever starts jhttpd should update both headers in the same
branch, or the code will claim a modesty the program no longer has.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions