server: take StaticRoute, FileRoute, HTMLBundle and DirectoryRoute to zero unsafe

Merged
JA
3mo ago
+922-1217

What

Same treatment as #40055 for the Bun.serve route objects: StaticRoute.rs 29 → 0, HTMLBundle.rs 24 → 0, FileRoute.rs 7 → 0, DirectoryRoute.rs 2 → 0 (plus −27 across ServerConfig.rs, mod.rs, server_body.rs, DevServer.rs, js_bundle_completion_task.rs), by fixing the layer underneath instead of annotating call sites:

  • Typed uws userdata. bun_uws_sys gets AnyResponse::on_writable_this / on_aborted_this and App::method_this / any_this (H1 and H3): the registrant passes the ThisPtr<U> it was dispatched with and keeps a ref while registered; the trampoline hands the handler a ThisPtr<U> (the one unsafe per trampoline lives there). ServerConfig's hand-written per-route trampolines are gone; StaticRouteLike takes ThisPtr<Self>.
  • Ownership. AnyRoute::{Static,File,Directory,Html} hold RefPtr; constructors return RefPtr; StaticRoute::clone no longer needs &mut *ptr. The ref that in-flight responses hold on a StaticRoute is an explicit RefPtr taken by the first outstanding response and released with the last (pending_responses), rather than a bare ref_() whose matching deref_ trusted the userdata pointer. FileRoute/DirectoryRoute hand FileResponseStream a StreamOwner::{FileRoute,DirectoryRoute}(RefPtr) instead of (ctx, fn, fn, fn); RequestContext keeps the raw Ctx form.
  • HTMLBundle. PendingResponses are stored by value and found by AnyResponse equality on abort (userdata is the route); the build task owns a RefPtr<Route> released after on_complete; State::Html holds a RefPtr<StaticRoute>; State::Building drops its task pointer (the cancel-and-deref arm in State::deinit was unreachable — the task's ref keeps the route alive while building — and would have double-released the enqueue ref if reached). Route::drop now releases its HTMLBundle ref (the IntrusiveRc field previously leaked it).
  • JSBundleCompletionTask::new() + schedule(self) replace create_and_schedule_completion_task + post-hoc raw field writes; poll_ref.ref_ moves to just before enqueue.
  • JSValue::as_class_this_ptr::<T>() sibling of as_class_ref for refcounted m_ctx payloads.

Testing

Debug+ASAN: every bun-serve-static* / bun-serve-html* test, bun-serve-routes, bun-serve-file, serve-directory-routes, serve-http3, test/bake/dev/html.test.ts pass; serve.test.ts 296/297 (the uid-0 privileged-port case fails in the sandbox regardless). Hand-driven under the debug binary: GET/HEAD/304 on a static route, full and aborted (AbortController + raw socket destroy) reads of a 2 MB static body, server.reload({routes}) mid-stream + GC, file route incl. HEAD/Range, HTML import route incl. concurrent requests, abort during build, reload, development: {hmr:false} — exit 0, no ASAN output. rust-check-all for windows-msvc / apple-darwin passes.