Skip to content

Bun.serve: register the files of an HTML import as literal routes - #46009

Open
robobun wants to merge 3 commits into
mainfrom
robobun/45b38bb6/literal-bundled-file-routes
Open

robobun wants to merge 3 commits into
mainfrom
robobun/45b38bb6/literal-bundled-file-routes

Conversation

@robobun

@robobun robobun commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

  • Bun.serve registers each bundled file of a production HTML import as a route pattern. A file named :smile:.png answers GET and POST /about with the PNG and an immutable cache header. A dependency can supply the name.
  • Two such files are one route, and a user route "/:id" is replaced.
  • Cause: HttpRouter::add (packages/bun-uws/src/HttpRouter.h:280) reads every key as a pattern.

Fix

  • StaticRouteEntry.path is a RouteKey: Pattern for a key of routes, Literal for a bundled file (push_bundled_file).
  • apply_static_route registers a Literal in app.literal_routes(..). There, HttpRouter::add puts a key that a pattern cannot hold under a container node, whose handler compares the whole URL.
  • Other keys register as before. route() and executeHandlers() are not edited.
  • Verified: bun-serve-html.test.ts (7 new), bun-serve-html-manifest.test.ts (3), bundler_html_server.test.ts (1). Self-reviewed: 32 concerns raised, 25 addressed (Notes).

Background

  • uWS::HttpRouter is the route tree behind routes and the fetch fallback, on HTTP/1.1, HTTP/2 and HTTP/3.
  • A production HTML import, or a built server (--compile, --target=bun), adds one static route for each output file.
  • Open question: the other fix renames such an output file in the bundler, as Rollup does, with no router change (Notes).

Downsides

Notes

Repro (1.4.3-canary 367d939 and a release build of main 017b243, Linux x64). index.html holds <img src="./:smile:.png">, and :smile:.png is a file next to it.

import page from "./index.html";

const server = Bun.serve({
  port: 0,
  development: false,
  routes: { "/": page, "/:id": () => new Response("user route") },
  fetch: () => new Response("no route", { status: 404 }),
});
async function show(path: string, init?: RequestInit) {
  const res = await fetch(new URL(path, server.url), init);
  console.log(init?.method ?? "GET", path, res.status, res.headers.get("content-type"), res.headers.get("cache-control"));
}
await show("/about"); // the user route
await show("/"); // the first request for the page builds it and registers its files
await show("/about");
await show("/about", { method: "POST" });
await server.stop(true);
GET /about 200 text/plain;charset=utf-8 null
GET / 200 text/html;charset=utf-8 no-cache
GET /about 200 image/png public, max-age=31536000, immutable
POST /about 200 image/png public, max-age=31536000, immutable

With this PR the last two lines are 200 text/plain;charset=utf-8 null.

Other effects on main. Each one has a test here.

  • An image *.png answers every path at any depth.
  • With :a.png and :b.png, both URLs return the bytes of :a.png.
  • A stylesheet in node_modules with url("./*.png"), and an image over 128 KB, makes GET and POST /about return the image.
  • NODE_ENV=production bun ./index.html with an image *.png returns the image at / from the second request. The test mounts the page at /*, as that command does.
  • bun build --target=bun --asset-naming '[dir]/[name].[ext]': :v/b.png answers /other/b.png, and *dir/a.png is not served at all.
  • A file named * takes the place of the fetch handler for GET.
  • A path of 100 segments or more also answers a longer path that starts with it. The router reads 100 segments of a URL.

Not affected: development: true. The dev server serves assets under /_bun/asset/.

There is no user report and no issue. A review of #41793 found the defect. It has been there since #16395. The case for a fix is the effect: wrong bytes with a one-year cache header on URLs of the app, in production only, and a dependency can cause it.

The open question: router or bundler.

This PR: literal keys in the router Rewrite the name in the bundler
Change 14 source files, +252/-91. HttpRouter.h +93/-6 an estimated 25 to 40 lines in path_template_print (src/bundler/options.rs), next to the .. rewrite. Not built
File names on disk do not change :smile:-<hash>.png gets a new name
A server that an older Bun built fixed not fixed
Route keys that user code builds from output names not fixed fixed where the names come from the naming template (not checked here)
--compile no special case a compiled entry point is found by a name that comes from its specifier (src/resolver/standalone_module_graph.rs). The .. rewrite is off under --compile for that reason, so this rewrite needs the same care
Router route() and executeHandlers() untouched, new code in add() and remove() no change, and a file name is still a pattern

The self-review of this PR preferred the bundler rewrite, or a maintainer's answer before any PR. The design work before the code chose the router, because the registration is the step that reads a file name as a pattern. If the answer is the bundler, the tests here still apply. They read the URLs from the served page, and only the expected names change.

Self-review: 32 concerns, 25 addressed. Not taken:

Not fixed here. Each has a different cause.

  • bun ./:page.html ./other.html builds the route key /:page in src/js/internal/html.ts. /about returns that page.
  • The recipe in docs/bundler/executables.mdx builds routes keys from Bun.embeddedFiles names.
  • The page of a runtime import is also registered under its output path. For a page outside the working directory that key has no leading ./, and the router drops its first byte: ./pages/index.html answers at /ages/index.html. This PR does not change that. The alias of a page in a directory like a:b answered every /<x>/index.html. It now answers no path.
  • On HTTP/2 and HTTP/3 a user route with a method key on a parameter path, such as "/:id": { GET }, answers the URL of every file route. A file with an ordinary name is included. An any-method route is under the * method node there (Http2Context.h:751, Http3Context.h:86), and route() tries the node of the request method first. HTTP/1.1 puts an any-method route under each method (HttpContext.h:1194). On 1.4.3-canary, routes: { "/logo.png": new Response("file"), "/:id": { GET } } returns the file for GET /logo.png on HTTP/1.1, and the user route on HTTP/2 and HTTP/3.

A user route with the same bytes as the path of such a file. The file answers first at that one path. The user route answers the other paths of its pattern.

  • Runtime import (the file route is any-method, and the key has the hash of the file, so only a generated key can have these bytes). On main a user HEAD route answered HEAD at the path of the file. With this PR the file answers GET and HEAD there on HTTP/1.1. On HTTP/2 and HTTP/3 the user HEAD route still answers HEAD, because of the method node order above.
  • Built server (the file route is GET and HEAD). On main the HEAD route of the file replaces a user HEAD route with the same bytes, and the user route never runs. With this PR the file answers HEAD at its own path, and the user route answers the other paths.
  • The check for a user HEAD route (path_has_user_head_route, src/runtime/server/mod.rs:2490) compares the bytes and not the kind. A file with an ordinary name is in the same tree node as a user key with the same bytes, and there a HEAD route for the file would replace the user route.

#41793. Both PRs add StaticRouteEntry::push_bundled_file at the same place, so git reports a conflict for the one that lands second. The merged function registers both keys as literals:

if let Cow::Owned(encoded) = strings::percent_encode_url_path(path) {
    routes.push(StaticRouteEntry { path: RouteKey::Literal(encoded.into_boxed_slice()), route: route.clone(), method });
}
routes.push(StaticRouteEntry { path: RouteKey::Literal(Box::from(path)), route, method });

If #41793 lands first, there is no time in which :a b.png returns 404.

Other open PRs on HttpRouter.h (#45400, #35248, #44224, #35249, #37615). The container is a child of a method node with isHighPriority and the name */. It has to stay after the WebSocket upgrade routes and before every other child.

How the router part works. In literal mode, add() asks needsLiteralNode(key): does a segment start with : or *, or does the key have 100 segments. If not, the key goes into the tree as before. If so, the key becomes one child, named by the whole key, of a container node under each method. executeHandlers() already runs a child whose name starts with * as a wildcard, so it runs the one handler of the container for each request. That handler compares the whole URL with the keys and runs the handlers of the key that is equal. On a miss it returns false, and the router goes on. needsLiteralNode splits the key by itself. A version that called getUrlSegment stopped the inlining of getUrlSegment into the HTTP/2 and HTTP/3 executeHandlers in the release build.

Measurements.

Release builds of main 017b243 and of this branch (ThinLTO, Linux x64):

  • Request path: executeHandlers (HTTP/1: 124 instructions, HTTP/2 and HTTP/3: 198), getUrlSegment (113) and the 6 functions that hold the inlined route() are identical after address normalization (llvm-objdump -d). 0 differing instructions.
  • size: text 106,739,870 to 106,745,758 (+5,888 bytes). data and bss do not change. 3 new C functions.
  • Hello-world server, ab -k -c 1, user-mode instructions of the server for each request (300,000 requests minus 100,000): main 18,061 to 18,179 in 6 runs, this branch 18,075 to 18,185 in 3 runs. The difference is inside the spread of main (118).

Harness over HttpRouter.h with the release flags of libuwsockets.cpp (clang 23, -O3 -march=nehalem, libstdc++), and the same with ThinLTO. User-mode instructions from perf_event_open, 200,000 route() calls for each row:

route() on a server with no such file, -O3. 0 of 14 rows differ.

A fetch-only          GET /about               main   201  PR   201  (+0)
A fetch-only          GET /a/b/c/d             main   209  PR   209  (+0)
B 60 static+param+*   GET first static         main   324  PR   324  (+0)
B 60 static+param+*   GET last static          main  2471  PR  2471  (+0)
B 60 static+param+*   GET /users/123           main   436  PR   436  (+0)
B 60 static+param+*   GET /miss -> fallback    main  1255  PR  1255  (+0)
C small app           GET /api/users/42        main   603  PR   603  (+0)
C small app           GET /health              main   316  PR   316  (+0)
C small app           GET /nope -> fallback    main   244  PR   244  (+0)
C small app           GET /                    main   237  PR   237  (+0)
F exact '/' + '/*'    GET /                    main   237  PR   237  (+0)
F exact '/' + '/*'    GET /about -> fallback   main   210  PR   210  (+0)
G any '/' + '/*'      GET /                    main   237  PR   237  (+0)
H trailing slash      GET /api/                main   395  PR   395  (+0)

route() on the same servers with one file that is registered as HEAD + any, -O3. The three rows where main runs the wrong handler are the bug.

RA+ordinary file      GET /about               main   218  PR   218  (+0)
RA+ordinary file      GET the file             main   290  PR   290  (+0)
RA+':' file           GET /about               main   278  PR   265  (-13)  main: wrong handler
RA+':' file           GET /a/b/c/d             main   359  PR   273  (-86)
RA+':' file           GET the file             main   278  PR   266  (-12)
RA+':' file           HEAD the file            main   364  PR   352  (-12)
RB+':' file           GET first static         main   324  PR   388  (+64)
RB+':' file           GET last static          main  2471  PR  2535  (+64)
RB+':' file           GET /users/123           main   436  PR   500  (+64)
RB+':' file           GET /miss -> fallback    main  1332  PR  1319  (-13)  main: wrong handler
RB+':' file           GET the file             main  1332  PR   266  (-1066)
RC+':' file           GET /api/users/42        main   603  PR   667  (+64)
RC+':' file           GET /health              main   316  PR   380  (+64)
RC+':' file           GET /nope -> fallback    main   321  PR   308  (-13)  main: wrong handler
RC+':' file           GET /                    main   237  PR   301  (+64)
RC+':' file           GET the file             main   321  PR   266  (-55)
  • With ThinLTO the 14 rows are also equal, and the cost on a server with such a file is +67.
  • Struct sizes: sizeof(Node) 88 to 88, sizeof(HttpRouter) 3360 to 3360 (probe with the release flags). size_of::<StaticRouteEntry>() 48 to 56 (const assert).
  • Registration: a table of 63 routes (60 of them files as any) costs 5,288,638 instructions on main and 5,345,666 here (+1.1%). With the 60 files in literal mode: 5,351,142. Allocations for one file (HEAD + any): 186 and 186 for an ordinary name. 257 for the first : file (149 on main, as a pattern), 145 for the next (109).
  • Differential, main header against this header with the literal mode off: 160,000 random add/remove/route calls, 0 mismatches. Property check with literal keys, yielding handlers, upgrade routes, re-add and remove: seeds 1 and 7, 1,200,000 route() calls each, 0 violations. Both under ASAN and UBSAN.
  • needsLiteralNode against the split of getUrlSegment: 2,000,000 random keys, 0 mismatches.
  • A literal key of 99, 100 and 102 segments answers only its own URL. As a pattern, the key of 100 segments also answers a longer URL.

Suites run on the debug build of the final code. All pass: bun-serve-html.test.ts, bun-serve-html-manifest.test.ts, bundler_html_server.test.ts, bun-serve-routes.test.ts, bun-serve-static.test.ts, serve-directory-routes.test.ts, serve-http2.test.ts, serve-http3.test.ts, websocket-server.test.ts, test/bake/dev/html.test.ts, and the source lints in test/internal/source-lints. In the last run, 3 tests in bun-serve-file.test.ts (pollable Bun.file), 3 in serve-protocols.test.ts (HTTP/3 bodies) and 1 in bun-server.test.ts (TLS before the handshake) hit the 5 s timeout on a slow machine. None of them has a bundled file route, and the three files passed in full on the first version of this change. cargo check -p bun_runtime passes for x86_64-pc-windows-msvc and aarch64-apple-darwin. After the rebase onto main 5738e23, the three test files of this PR, bun-serve-static.test.ts and bun-serve-routes.test.ts pass on a new debug build. main removed the router of a server name (currentRouter), so the HTTP/1.1 setter now sets the one router of the context.


no test proof · iteration 1 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/js/bun/http/bun-serve-html.test.ts, test/js/bun/http/bun-serve-html-manifest.test.ts, test/bundler/bundler_html_server.test.ts

@robobun

robobun commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 11:04 PM PT - Oct 21st, 2026

✅ @robobun, your commit be1b52a551ca328d3a79ad03adfc6aa75479ccca passed in Build #130527! 🎉


🧪   To try this PR locally:

bunx bun-pr 46009

That installs a local version of the PR into your bun-46009 executable, so you can run:

bun-46009 --bun

@robobun

robobun commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

Status

Reproduced on 1.4.3-canary (367d939) and on a release build of main (017b243). A production HTML import with an image named :smile:.png makes GET and POST /about return the PNG, and it replaces a user route "/:id". The repro script is in the Notes of the description. The new tests fail on 1.4.3-canary and pass on a debug build of this branch.

Reviewed: the self-review preferred a rename of such output files in the bundler, or a maintainer's answer on the level before a PR. This PR is open so that a maintainer can choose. The Notes compare the two fixes.

@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: oven-sh/bun/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 190fe66c-be57-44c0-84a2-e2dc8ac2da75
📥 Commits

Reviewing files that changed from the base of the PR and between 328a5f2 and be1b52a.

📒 Files selected for processing: 7
  • packages/bun-uws/src/App.h
  • src/runtime/server/HTMLBundle.rs
  • src/runtime/server/mod.rs
  • src/runtime/server/server_body.rs
  • src/uws_sys/App.rs
  • test/js/bun/http/bun-serve-html-manifest.test.ts
  • test/js/bun/http/bun-serve-html.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.

Walkthrough

The change adds literal-path registration to HTTP routers and uses it for bundled files. Route keys distinguish literal bundled-file paths from pattern routes. Tests cover literal asset paths and route behavior across server configurations and protocols.

Changes

Literal bundled-file routes

Layer / File(s) Summary
Literal-key matching in HttpRouter
packages/bun-uws/src/HttpRouter.h
The router stores eligible paths as literal keys and matches complete URLs byte-for-byte. Route removal uses the same classification.
Literal-route API across HTTP versions
packages/bun-uws/src/App.h, packages/bun-uws/src/Http2App.h, packages/bun-uws/src/Http3App.h, src/uws_sys/App.rs, src/uws_sys/h2.rs, src/uws_sys/h3.rs, src/uws_sys/libuwsockets*.cpp
HTTP/1, HTTP/2, and HTTP/3 APIs expose literal-route registration. Rust callbacks and C ABI wrappers toggle the mode around route registration.
Bundled-file route registration
src/runtime/server/ServerConfig.rs, src/runtime/server/mod.rs, src/runtime/server/HTMLBundle.rs, src/runtime/server/server_body.rs
Static routes distinguish pattern keys from literal keys. Bundled-file routes use literal keys, and shared registration applies them to HTTP app variants.
Bundled asset and server coverage
test/bundler/bundler_html_server.test.ts, test/js/bun/http/bun-serve-html*.test.ts
Tests check literal asset paths, fallback responses, methods, deep paths, user routes, bundler backends, and HTTP protocol variants.

Suggested reviewers: jarred-sumner

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to be1b5

The reviewed changes are mergeable after normal checks; no concrete routing or test-coverage issue remains identified.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely describes the primary change: registering files from an HTML import as literal routes.
Description check Passed The description provides a detailed explanation of the problem, implementation, verification, performance impact, limitations, and test status. It does not use the exact template headings, but it incl…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description provides a detailed explanation of the problem, implementation, verification, performance impact, limitations, and test status. It does not use the exact template headings, but it includes the required purpose and verification information and is substantially complete.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Beyond the inline findings, I also checked three things in packages/bun-uws/src/HttpRouter.h and ruled them out: the container handler's urlSegmentVector[0].data() - 1 reconstruction is safe because executeHandlers parses segment 0 before it walks the method node's children and an empty URL stops before the container runs; route reloads go through clearRoutes() (src/runtime/server/server_body.rs:1902, 2005), so a literal entry is never looked up by remove() while literalKeys is off; and pre-creating the container for every method before the handler-id loop does keep the id identical across methods, so cullNode's id shifting stays consistent.

Extended reasoning...

The change adds a literal-key mode to the vendored uWS HttpRouter and routes bundled HTML-import files through it on the h1, h2 and h3 apps, with StaticRouteEntry.path becoming a RouteKey enum; it touches request routing (which URL serves which bytes) but no auth or crypto surface. Four inline findings already signal that a human must look; this note only records the adjacent router lifetime and id-consistency concerns that were examined and did not reproduce.

Additional findings (outside the current diff — GitHub can't attach inline comments there):

  • 🟡 src/runtime/server/mod.rs — A user HEAD route whose pattern bytes equal a bundled literal key makes HEAD requests for that file answer from the file's LOW any-method handler instead of the user's handler, and the user's explicit HEAD handler is silently bypassed. src/runtime/server/mod.rs:2491 compares route.route.path.as_bytes() with the Literal bytes without checking the key kind, so path_has_user_head_route is true and the HEAD registration is skipped, but the literal container (HttpRouter.h:81, priority -1 but HIGH) still runs ahead of the user's MEDIUM ":" route and serves on_request for HEAD. Fix: compare kinds as well as bytes (only a RouteKey::Pattern can equal a user pattern), and when a user HEAD route exists for the same URL, do not let the literal container answer HEAD for that key.

    Why this was flagged

    Trigger: a routes entry with a HEAD handler whose pattern is byte-equal to a bundled output path, e.g. a --compile server with --asset-naming '[dir]/[name].[ext]' and a :v/b.png file. mod.rs:2485-2494 sees RouteMethod::Specific(HEAD) with equal bytes and sets path_has_user_head_route, so ServerConfig.rs:430-431 skips the file's HEAD MEDIUM handler. But ServerConfig.rs:435 still registers any_this for the Literal, which on h1 lands under the HEAD method node's literal container (HttpContext.h:1190 expands "*"); HttpRouter.h:97-102 sorts that container (isHighPriority) before the user's MEDIUM node, so HEAD requests run the file's on_request and the user handler never runs. On the base branch the same user HEAD route and file both lived in the pattern tree and the user's MEDIUM ":" handler ran before the file's LOW handler. Remedy: match kinds in path_has_user_head_route (RouteKey::is_pattern) and, when a user HEAD route covers the key, exclude HEAD from the literal any registration.

    Verification: Trigger: a routes key with an explicit HEAD handler whose bytes equal a bundled output path that has a :/* segment. src/runtime/server/mod.rs:2485-2494 computes path_has_user_head_route, so a user HEAD pattern byte-equal to a RouteKey::Literal sets it true. The user's HEAD handler is never reached. On the base branch the user's HEAD handler won.

Comment thread packages/bun-uws/src/HttpRouter.h
Comment thread test/bundler/bundler_html_server.test.ts Outdated
Comment thread src/runtime/server/ServerConfig.rs Outdated
@robobun
robobun force-pushed the robobun/45b38bb6/literal-bundled-file-routes branch from 0882a79 to 8f3621e Compare October 20, 2026 16:06
Comment thread src/runtime/server/ServerConfig.rs Outdated
Comment thread src/runtime/server/ServerConfig.rs Outdated
Comment thread src/runtime/server/ServerConfig.rs Outdated
Comment thread src/runtime/server/ServerConfig.rs Outdated
Comment thread src/uws_sys/App.rs Outdated

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no issues

No high-confidence issues detected in this change.

Still open from earlier reviews (2):

  • Unresolved: 2 minor or pre-existing.

@robobun

robobun commented Oct 20, 2026

Copy link
Copy Markdown
Collaborator Author

On the review note about path_has_user_head_route (src/runtime/server/mod.rs:2485): two probes, and no change to the check.

Built server. bun build --target=bun --asset-naming '[dir]/[name].[ext]', a file :v/b.png, and a user route "/:v/b.png": { HEAD }. This is the trigger in the note. The file route has the methods GET and HEAD there.

1.4.3-canary this PR
HEAD /:v/b.png file file
HEAD /x/b.png file user route
GET /x/b.png file 404 from fetch

On main the HEAD route of the file replaces the user HEAD route, and the user route never runs. With this PR the user route answers the other paths of its pattern. HTTP/1.1, HTTP/2 and HTTP/3 give the same rows.

Runtime import. The file route is any-method. The key has the hash of the file (/:smile:-<hash>.png), so only a generated key can have the same bytes.

1.4.3-canary this PR, HTTP/1.1 this PR, HTTP/2 and HTTP/3
HEAD the file user route file user route
GET the file file file file
HEAD /other user route user route user route
GET /other file 404 from fetch 404 from fetch

The note is right for this case on HTTP/1.1: the file now answers HEAD at its own path. That agrees with the rest of the router. The file answers GET at that path, and a file with an ordinary name also answers HEAD before a user route "/:id": { HEAD }, because the static node goes first. On HTTP/2 and HTTP/3 the user route still answers HEAD, because the router tries the node of the request method first there.

The check has to compare the bytes and not the kind. A file with an ordinary name stays in the pattern tree, in the same node as a user key with the same bytes. With a check of the kind, the HEAD route of the file would be registered there, and uWS would replace the user HEAD route with it. The Notes of the description have these results.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no issues

No high-confidence issues detected in this change.

A production HTML import, and a server built with --target=bun or
--compile, serves each bundled file under the output path of the file.
HttpRouter read that path as a route pattern, so a name that starts a
segment with ':' or '*' was a parameter or a wildcard. The file answered
paths that are not its own, one file answered for another, and a user
route of the same shape was replaced.

HttpRouter gets a literal mode for add() and remove(). A literal key
that a pattern cannot hold byte for byte is one child of a container
node per method, and the handler of the container compares the whole
URL. route() and executeHandlers() do not change. StaticRouteEntry now
says if its path is a pattern or a literal, and every bundled file goes
in through StaticRouteEntry::push_bundled_file.
itBundled.skip lists the case in the run. A plain if left it out.
apply_static_route stays where the HTTP/1.1 function was, with its
comment unchanged. The HEAD check moves out of the closure.
@robobun
robobun force-pushed the robobun/45b38bb6/literal-bundled-file-routes branch from 328a5f2 to be1b52a Compare October 22, 2026 00:36

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no issues

No high-confidence issues detected in this change.

This branch has not been deployed

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant