Skip to content

feat(node:http2) Implement HTTP2 server support - #14286

Merged
Jarred-Sumner merged 151 commits into
mainfrom
ciro/http2-server
Oct 15, 2024
Merged

Jarred-Sumner merged 151 commits into
mainfrom
ciro/http2-server

Conversation

@cirospaciari

@cirospaciari cirospaciari commented Oct 2, 2024 •

Copy link
Copy Markdown
Member

What does this PR do?

Major Client compatibility fixes + Server implementation

Fix: #8823
Fix: #14249
Fix: #8549
Fix: #9228

Notes

Push (not supported in most browsers):

  • ServerHttp2Stream.pushStream (temporary reverted)
  • ServerHttp2Stream.pushAllowed (mocked always return false)

HTTP1 compatibility aka ALPN negotiation (blocked on http module implementation):

  • event 'unknownProtocol'
  • options.allowHTTP1
  • options.unknownProtocolTimeout

WebSocket Proxing using RFC8441 (disable by default on node.js);

ALTSVC using RFC7838:

In-Progress:

  • ServerHttp2Stream.respondWithFile
  • ServerHttp2Stream.respondWithFD
  • Event 'frameError' (temporary reverted)
  • Event order
  • abort behavior
  • Http2ServerResponse.writeEarlyHints (missing validations)
  • Http2Server Event: 'timeout'
  • Http2Server Event: checkContinue
  • Http2Server.updateSettings
  • Http2Stream.bufferSize (must include native backpressure and corked values too)

Progress:

Class: ServerHttp2Session

  • serverhttp2session.altsvc(alt, originOrStream)
  • serverhttp2session.origin(...origins)

Class: ClientHttp2Session

Class: Http2Stream

  • Event: 'aborted'
  • Event: 'close'
  • Event: 'error'
  • Event: 'frameError'
  • Event: 'ready'
  • Event: 'timeout'
  • Event: 'trailers'
  • Event: 'wantTrailers'
  • http2stream.aborted
  • http2stream.bufferSize
  • http2stream.close(code[, callback])
  • http2stream.closed
  • http2stream.destroyed
  • http2stream.endAfterHeaders
  • http2stream.id
  • http2stream.pending
  • http2stream.priority(options)
  • http2stream.rstCode
  • http2stream.sentHeaders
  • http2stream.sentInfoHeaders
  • http2stream.sentTrailers
  • http2stream.session
  • http2stream.setTimeout(msecs, callback)
  • http2stream.state
  • http2stream.sendTrailers(headers)

Class: ClientHttp2Stream

  • Event: 'continue'
  • Event: 'headers'
  • Event: 'push'
  • Event: 'response'

Class: ServerHttp2Stream

  • http2stream.additionalHeaders(headers)
  • http2stream.headersSent
  • http2stream.pushAllowed
  • http2stream.pushStream(headers[, options], callback)
  • http2stream.respond([headers[, options]])
  • http2stream.respondWithFD(fd[, headers[, options]])
  • http2stream.respondWithFile(path[, headers[, options]])

Class: Http2Server

  • Event: 'checkContinue'
  • Event: 'connection'
  • Event: 'request'
  • Event: 'session'
  • Event: 'sessionError'
  • Event: 'stream'
  • Event: 'timeout'
  • server.close([callback])
  • serverSymbol.asyncDispose
  • server.setTimeout([msecs][, callback])
  • server.timeout
  • server.updateSettings([settings])

Class: Http2SecureServer

  • Event: 'checkContinue'
  • Event: 'connection'
  • Event: 'request'
  • Event: 'session'
  • Event: 'sessionError'
  • Event: 'stream'
  • Event: 'timeout'
  • Event: 'unknownProtocol'
  • server.close([callback])
  • server.setTimeout([msecs][, callback])
  • server.timeout
  • server.updateSettings([settings])

Module:

  • http2.createServer([options][, onRequestHandler])
  • http2.createSecureServer(options[, onRequestHandler])
  • http2.connect(authority[, options][, listener])
  • http2.constants
  • http2.getDefaultSettings()
  • http2.getPackedSettings([settings])
  • http2.getUnpackedSettings(buf)
  • http2.sensitiveHeaders

Compatibility API

Class: http2.Http2ServerRequest

  • Event: 'aborted'
  • Event: 'close'
  • request.aborted
  • request.authority
  • request.complete
  • request.connection
  • request.destroy([error])
  • request.headers
  • request.httpVersion
  • request.method
  • request.rawHeaders
  • request.rawTrailers
  • request.scheme
  • request.setTimeout(msecs, callback)
  • request.socket
  • request.stream
  • request.trailers
  • request.url

Class: http2.Http2ServerResponse

  • Event: 'close'
  • Event: 'finish'
  • response.addTrailers(headers)
  • response.connection
  • response.createPushResponse(headers, callback)
  • response.end([data[, encoding]][, callback])
  • response.finished
  • response.getHeader(name)
  • response.getHeaderNames()
  • response.getHeaders()
  • response.hasHeader(name)
  • response.headersSent
  • response.removeHeader(name)
  • response.req
  • response.sendDate
  • response.setHeader(name, value)
  • response.setTimeout(msecs[, callback])
  • response.socket
  • response.statusCode
  • response.statusMessage
  • response.stream
  • response.writableEnded
  • response.write(chunk[, encoding][, callback])
  • response.writeContinue()
  • response.writeEarlyHints(hints)
  • response.writeHead(statusCode[, statusMessage][, headers])
  • Documentation or TypeScript types (it's okay to leave the rest blank in this case)
  • Code changes

How did you verify your code works?

@robobun

robobun commented Oct 2, 2024 •

Copy link
Copy Markdown
Collaborator

❌ @cirospaciari, your commit b1582f4 has 9 failures in #4884:

  • test/bundler/bundler_compile.test.ts - 1 failing on 🐧 20.04 x64-baseline
  • test/cli/install/bun-install.test.ts - 1 failing on 🐧 22.04 x64
  • test/cli/install/bun-install.test.ts - 1 failing on 🐧 20.04 x64
  • test/cli/install/bun-install.test.ts - 1 failing on 🐧 20.04 x64-baseline
  • test/cli/install/bun-install.test.ts - 1 failing on 🐧 22.04 x64-baseline
  • test/cli/install/bun-install.test.ts - 1 failing on 🐧 20.04 aarch64
  • test/cli/install/bun-install.test.ts - 1 failing on 🐧 22.04 aarch64
  • test/cli/run/require-cache.test.ts - 1 failing on 🪟 x64-baseline
  • test/js/node/test/parallel/http2-server-rst-before-respond.test.js - 1 failing on 🍎 13 aarch64
  • test/js/node/test/parallel/http2-server-rst-before-respond.test.js - 1 failing on 🍎 14 aarch64
  • test/js/node/test/parallel/http2-server-rst-before-respond.test.js - 1 failing on 🪟 x64
  • test/js/node/test/parallel/http2-server-rst-before-respond.test.js - 1 failing on 🪟 x64-baseline
  • test/js/node/test/parallel/http2-server-rst-before-respond.test.js - 1 failing on 🐧 22.04 x64
  • test/js/node/test/parallel/http2-server-rst-before-respond.test.js - 1 failing on 🐧 12 x64
  • test/js/node/test/parallel/http2-server-rst-before-respond.test.js - 1 failing on 🐧 20.04 x64
  • test/js/node/test/parallel/http2-server-rst-before-respond.test.js - 1 failing on 🍎 14 x64
  • test/js/node/test/parallel/http2-server-rst-before-respond.test.js - 1 failing on 🍎 13 x64
  • test/js/node/test/parallel/http2-server-rst-before-respond.test.js - 1 failing on 🐧 12 x64-baseline
  • test/js/node/test/parallel/http2-server-rst-before-respond.test.js - 1 failing on 🐧 20.04 x64-baseline
  • test/js/node/test/parallel/http2-server-rst-before-respond.test.js - 1 failing on 🐧 22.04 x64-baseline
  • test/js/node/test/parallel/http2-server-rst-before-respond.test.js - 1 failing on 🐧 20.04 aarch64
  • test/js/node/test/parallel/http2-server-rst-before-respond.test.js - 1 failing on 🐧 22.04 aarch64
  • test/js/node/test/parallel/http2-server-rst-before-respond.test.js - 1 failing on 🐧 12 aarch64
  • test/js/node/test/parallel/http2-large-write-close.test.js - 1 failing on 🍎 13 aarch64
  • test/js/node/test/parallel/http2-large-write-close.test.js - 1 failing on 🍎 14 aarch64
  • test/js/node/test/parallel/http2-large-write-close.test.js - 1 failing on 🐧 20.04 x64
  • test/js/node/test/parallel/http2-large-write-close.test.js - 1 failing on 🐧 12 x64
  • test/js/node/test/parallel/http2-large-write-close.test.js - 1 failing on 🐧 22.04 x64
  • test/js/node/test/parallel/http2-large-write-close.test.js - 1 failing on 🐧 20.04 x64-baseline
  • test/js/node/test/parallel/http2-large-write-close.test.js - 1 failing on 🐧 22.04 x64-baseline
  • test/js/node/test/parallel/http2-large-write-close.test.js - 1 failing on 🐧 12 x64-baseline
  • test/js/node/test/parallel/http2-large-write-close.test.js - 1 failing on 🍎 14 x64
  • test/js/node/test/parallel/http2-large-write-close.test.js - 1 failing on 🍎 13 x64
  • test/js/node/test/parallel/http2-large-write-close.test.js - 1 failing on 🐧 22.04 aarch64
  • test/js/node/test/parallel/http2-large-write-close.test.js - 1 failing on 🐧 12 aarch64
  • test/js/node/test/parallel/http2-large-write-close.test.js - 1 failing on 🐧 20.04 aarch64
  • test/js/node/test/parallel/http2-compat-write-early-hints.test.js - timeout on 🍎 13 aarch64
  • test/js/node/test/parallel/http2-compat-write-early-hints.test.js - timeout on 🍎 14 aarch64
  • test/js/node/test/parallel/http2-compat-write-early-hints.test.js - timeout on 🪟 x64
  • test/js/node/test/parallel/http2-compat-write-early-hints.test.js - timeout on 🪟 x64-baseline
  • test/js/node/test/parallel/http2-compat-write-early-hints.test.js - timeout on 🐧 22.04 x64
  • test/js/node/test/parallel/http2-compat-write-early-hints.test.js - timeout on 🐧 12 x64
  • test/js/node/test/parallel/http2-compat-write-early-hints.test.js - timeout on 🐧 20.04 x64
  • test/js/node/test/parallel/http2-compat-write-early-hints.test.js - timeout on 🍎 14 x64
  • test/js/node/test/parallel/http2-compat-write-early-hints.test.js - timeout on 🍎 13 x64
  • test/js/node/test/parallel/http2-compat-write-early-hints.test.js - timeout on 🐧 12 x64-baseline
  • test/js/node/test/parallel/http2-compat-write-early-hints.test.js - timeout on 🐧 20.04 x64-baseline
  • test/js/node/test/parallel/http2-compat-write-early-hints.test.js - timeout on 🐧 22.04 x64-baseline
  • test/js/node/test/parallel/http2-compat-write-early-hints.test.js - timeout on 🐧 20.04 aarch64
  • test/js/node/test/parallel/http2-compat-write-early-hints.test.js - timeout on 🐧 22.04 aarch64
  • test/js/node/test/parallel/http2-compat-write-early-hints.test.js - timeout on 🐧 12 aarch64
  • test/cli/hot/watch.test.ts - 1 failing on 🪟 x64-baseline
  • test/cli/hot/watch.test.ts - 1 failing on 🪟 x64
  • test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js - 1 failing on 🍎 14 aarch64
  • test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js - 1 failing on 🪟 x64-baseline
  • test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js - 1 failing on 🍎 13 aarch64
  • test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js - 1 failing on 🪟 x64
  • test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js - 1 failing on 🐧 20.04 x64
  • test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js - 1 failing on 🐧 12 x64
  • test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js - 1 failing on 🐧 22.04 x64
  • test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js - 1 failing on 🐧 20.04 x64-baseline
  • test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js - 1 failing on 🐧 22.04 x64-baseline
  • test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js - 1 failing on 🐧 12 x64-baseline
  • test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js - 1 failing on 🍎 14 x64
  • test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js - 1 failing on 🐧 22.04 aarch64
  • test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js - 1 failing on 🐧 12 aarch64
  • test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js - 1 failing on 🍎 13 x64
  • test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js - 1 failing on 🐧 20.04 aarch64
  • test/cli/watch/watch.test.ts - 2 failing on 🪟 x64
  • test/cli/watch/watch.test.ts - 2 failing on 🪟 x64-baseline
  • @cirospaciari
    cirospaciari force-pushed the ciro/http2-server branch 4 times, most recently from 13d248c to 4f6b936 Compare October 8, 2024 18:45
    @cirospaciari cirospaciari changed the title WIP: feat(node:http2) Implement HTTP2 server support eat(node:http2) Implement HTTP2 server support Oct 11, 2024
    @cirospaciari
    cirospaciari marked this pull request as ready for review October 11, 2024 21:48
    @cirospaciari cirospaciari changed the title eat(node:http2) Implement HTTP2 server support feat(node:http2) Implement HTTP2 server support Oct 11, 2024
    @yashpurplesectors

    Copy link
    Copy Markdown

    please merge this. waiting with fingers crossed 🤞

    @pablojimpas

    Copy link
    Copy Markdown

    I tested this PR against my demo repository to reproduce #14249 last Friday (oct 11) using the prebuilt binary with bunx bun-pr 14286. The issue seemed to be resolved, but I've tested again right now and the error shows up once again.

    These are the two binaries I've tested:

    -rwxr-xr-x 1  97M oct 14 15:44 bun-b30e238d9be315441a07e3ebab8c9e6f6de55037-pr14286
    -rwxr-xr-x 1  96M oct 11 07:55 bun-e16c1fcc052a188724fb3a019bada915077c4fa3-pr14286

    Comment thread src/js/internal/errors.ts Outdated
    Comment thread src/js/node/http2.ts Outdated
    Comment thread src/js/node/http2.ts Outdated
    Comment thread src/js/node/http2.ts Outdated
    Comment thread src/js/node/http2.ts Outdated
    Comment thread src/js/node/http2.ts Outdated
    Comment thread src/js/node/http2.ts Outdated
    Comment thread src/js/node/http2.ts Outdated
    Comment thread src/js/node/http2.ts Outdated
    Comment thread src/js/node/http2.ts Outdated
    Comment thread src/js/node/http2.ts Outdated
    Comment thread src/js/node/http2.ts Outdated
    Comment thread src/js/node/http2.ts Outdated
    @aayushpurplesectors

    Copy link
    Copy Markdown

    @cirospaciari: everything seems to be working well when I am using bun run but get the errors as shown in #14133 and #7630 when I run the compiled lib using bun build

    @efstathiosntonas

    efstathiosntonas commented Nov 17, 2024 •

    Copy link
    Copy Markdown

    I still get this on firebase messaging.sendEach on bun@1.1.34:

    Error while making request: Stream closed with error code NGHTTP2_PROTOCOL_ERROR. Error code: ERR_HTTP2_STREAM_ERROR

    built with:
    bun build --minify --sourcemap ./src/jobs/xxx_job.ts --compile --outfile producers/xxx_job

    when testing locally with jest (no bun build) everything works fine.

    @coupez

    coupez commented Dec 4, 2024

    Copy link
    Copy Markdown

    Everything works fine with bun run but things go wrong with bun build and then running the output.

    robobun pushed a commit that referenced this pull request Nov 24, 2025
    The tests for TXT service config resolution were using `grpctest.kleinsch.com`,
    which is no longer available. These tests were originally skipped when added
    in #14286, but were accidentally un-skipped in #20051.
    
    This restores the skip status to match upstream grpc-node, and fixes the
    flaky "should not keep repeating failed resolutions" test.
    
    ## To re-enable these tests in the future
    
    Bun could set up its own DNS TXT record. According to the gRPC spec (A2),
    the record should be:
    
    - DNS name: `_grpc_config.grpctest.bun.sh` (for server `grpctest.bun.sh`)
    - Record type: TXT
    - Value: `grpc_config=[{"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"service":"MyService","method":"Foo"}],"waitForReady":true}]}}]`
    
    The server also needs an A record pointing to any valid IP.
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    Co-Authored-By: Claude <noreply@anthropic.com>
    cirospaciari pushed a commit that referenced this pull request Nov 24, 2025
    …25039)
    
    ## Summary
    
    - Skip 2 tests that use `grpctest.kleinsch.com` (domain no longer
    exists)
    - Fix flaky "should not keep repeating failed resolutions" test
    
    These tests were originally skipped when added in #14286, but were
    accidentally un-skipped in #20051. This restores them to match upstream
    grpc-node.
    
    ## To re-enable these tests in the future
    
    Bun could set up its own DNS TXT record at `*.bun.sh`. According to the
    [gRPC A2
    spec](https://github.com/grpc/proposal/blob/master/A2-service-configs-in-dns.md):
    
    **DNS Setup needed:**
    1. A record: `grpctest.bun.sh` → any valid IP (e.g., `127.0.0.1`)
    2. TXT record: `_grpc_config.grpctest.bun.sh` with value:
       ```
    
    grpc_config=[{"serviceConfig":{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"service":"MyService","method":"Foo"}],"waitForReady":true}]}}]
       ```
    
    Then update the tests to use `grpctest.bun.sh` instead.
    
    ## Test plan
    
    - [x] `bun bd test test/js/third_party/grpc-js/test-resolver.test.ts`
    passes (20 pass, 3 skip, 1 todo, 0 fail)
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    Co-authored-by: Claude Bot <claude-bot@bun.sh>
    Co-authored-by: Claude <noreply@anthropic.com>
    Jarred-Sumner added a commit that referenced this pull request Aug 18, 2026
    …ockets, and the builtin-name tables (#38900)
    
    Removes 775 lines that nothing references (8 lines of signature, import
    and comment adjustments added), across node:http2, the HTTP/2 frame
    parser, the JSC/WebCore bindings, uSockets, four Rust crates, the
    builtin-name tables and scripts/. No behavior change. A 153-line source
    lint pins every removed symbol.
    
    ### Problem
    - `src/js/node/http2.ts` binds every entry of `constants` as a local
    (`const { ... } = constants;`); 178 of the 240 bindings have been unused
    since the block was added in #14286 (every call site reads
    `constants.X`). `const Socket = net.Socket` and the `#url` / `#isServer`
    fields of `ServerHttp2Session` are never read either (`tsc
    --noUnusedLocals`, re-checked by grep).
    - `_nativeAssertSettings` (`$newRustFunction("h2_frame_parser.rs",
    "jsAssertSettings")`) has been unused since #28074 gave http2.ts its own
    `assertSettings()`. That made `js_assert_settings` in
    `src/runtime/api/bun/h2_frame_parser.rs` (139 lines) and its facade
    re-export in `src/runtime/api.rs` unreachable: the js2native codegen was
    the only thing that named it. The three
    `BUN_DECLARE_HOST_FUNCTION(BUN__HTTP2_*)` lines in `ZigGlobalObject.cpp`
    are declarations of the Zig-era exports for the same helpers and have no
    definition anywhere.
    - `InspectorHTTPServerAgent::{startListening, stopListening,
    getRequestBody, getResponseBody}` are TODO stubs that nothing can
    dispatch to: the generated `HTTPServerBackendDispatcherHandler` in the
    pinned WebKit only declares `enable()` / `disable()`, so these `virtual
    ... final` methods override nothing and have no callers.
    - Declaration-only leftovers with no definition anywhere:
    `Bun__DNSResolver__new` / `Bun__DNSResolver__cancel` (`BunObject.cpp`),
    `jsBufferConstructorFunction_isBuffer` (`JSBuffer.cpp`,
    `Buffer.isBuffer` is a JS builtin), `getStringOption` (`CryptoUtil.h`).
    Plus the `IsIDLEnumeration` helper template (`IDLTypes.h`) and the
    `globalBuiltinFunction` macro (`ZigGlobalObject.cpp`), each with zero
    uses.
    - uSockets: `us_internal_ssl_sni_userdata` and
    `us_internal_ssl_handshake_abort` were added in #29932 and never called
    (`us_socket_server_name_userdata`, which the first one wrapped, stays).
    `us_cleanup_security_framework` (darwin) had no caller; the loader's
    failure paths are the only thing that ever frees a `SecurityFramework`,
    and they still do. The `SecTrustSettingsResult` typedef next to it was
    unused too.
    - Rust items no crate reaches (hawk `dead_public`, intersected over
    linux-gnu, linux-musl, android, freebsd, darwin and windows-msvc, then
    re-checked by hand): `ExprData::is_e_string`,
    `ArrayHashMap::get_adapted`, the debug-only `Behavior::eq`, and
    `js_parser::FunctionKind`, whose `Stmt` variant was never constructed.
    `validate_function_name` only ever ran for function expressions, so the
    parameter is gone and the one call site passes nothing; the error text
    already said "function expression".
    - Builtin-name tables: `Loader`, `byobRequest`, `controller`, `post`,
    `resume`, `started`, `state`, `textDecoder` and `view` in
    `BunBuiltinNames.h` have no `$name` / `@name` use in any builtin and no
    `namePublicName()` / `namePrivateName()` use in C++ (each entry costs
    two identifiers at VM startup). Their `builtins.d.ts` declarations go
    with them, as do the six `$stream*` `--define`s in `replacements.ts` (no
    builtin reads them; their only trace was the `.d.ts`), the `"Loader"`
    entry of `globalsToPrefix` (the only bare `Loader` in src/js is inside a
    block comment), and a duplicated `"Buffer"` entry.
    - `scripts/find-dead-exports.ts` (303 lines, #31254) was a textual
    approximation of the analysis `tools/hawk/` + `hawk.toml` now do
    (#36184); nothing references it.
    
    ### Fix
    - Deletes the items above; the only additions are the shorter
    `validate_function_name` signature, one comment in `semver/lib.rs` that
    named `get_adapted` (now names the surviving `get_index_adapted`), and
    the re-export line in `api.rs`.
    - Removing the `$newRustFunction` call is what makes the Rust function
    removable: `generate-js2native.ts` emits the
    `crate::api::bun::h2_frame_parser::js_assert_settings` thunk only for
    calls it finds in src/js, so after this change nothing generated names
    it either. `bundle-functions.ts` regenerates `BunBuiltinNames+extras.h`
    from the names builtins actually use, so a builtin-name entry that were
    still needed would be re-added by the build rather than break it; none
    was.
    - Verified: `bun bd` builds; `bun run rust:check-all` passes on all ten
    target triples; `cargo fmt --check` and prettier are clean. `bun bd
    test` passes on `test/js/node/http2/node-http2.test.js` (357),
    `test/js/node/http2/node-http2-continuation.test.ts`,
    `test/js/node/buffer.test.js`,
    `test/js/node/crypto/node-crypto.test.js`,
    `test/js/node/tls/node-tls-connect.test.ts`,
    `test/js/web/streams/streams.test.js` and
    `test/bundler/bundler_minify.test.ts`;
    `test/js/node/tls/node-tls-server.test.ts` passes 69/70, the remaining
    one (`SNICallback runs even when the requested servername matches the
    bind hostname`) fails identically with the released binary in this
    container (it binds `localhost` and connects to 127.0.0.1). The four
    function-name diagnostics `validate_function_name` emits are unchanged
    between the released binary and this build for both statement and
    expression positions. `bun test test/internal/source-lints/` passes (158
    tests); the new `dead-symbols-http2-settings-inspector-stubs.test.ts`
    fails on main and passes here.
    - Cross-checked against the 22 open dead-code PRs at the symbol level
    (their diffs, not just file lists): nothing here is removed by any of
    them. Two hunks are adjacent to open ones and will need a trivial rebase
    on whichever side lands second: the `BUN__HTTP2_*` lines sit right under
    the `Bun__NodeUtil__jsParseArgs` line #37788 removes, and the
    `InspectorHTTPServerAgent` hunks are a few lines above the event
    dispatchers #37454 removes. One item found dead here,
    `JSValkeyClient::close_subscription_ctx`, is the renamed form of the
    `SubscriptionCtx::close` that #38439 already removes, so it was left to
    that PR.
    
    ### Background
    - js2native: `$rust("file.rs", "name")` / `$newRustFunction(...)` calls
    in src/js are collected at build time by
    `src/codegen/generate-js2native.ts`, which emits one thunk per call into
    `generated_js2native.rs` / `GeneratedJS2Native.h`. A Rust host function
    is therefore reachable only while some builtin names it; the `api.rs`
    facade module exists so the generated thunks have a stable path to call.
    - `BunBuiltinNames.h` is the list of private identifiers (`@name` in
    builtin JS, `builtinNames.namePrivateName()` in C++) that
    `BunBuiltinNames` interns when a VM starts. `bundle-functions.ts` diffs
    the names builtins use against this list and writes the difference to
    `BunBuiltinNames+extras.h`, which is why removing an entry can only ever
    remove startup work, never break a builtin.
    - Inspector agents: the WebKit inspector protocol generator turns each
    domain's JSON into a `*BackendDispatcherHandler` interface; a command
    reaches an agent only through that interface. The HTTPServer domain bun
    ships in its WebKit defines `enable` and `disable`, so methods an agent
    declares beyond those are unreachable by construction.
    - hawk (`tools/hawk/README.md`) is the workspace-wide reachability
    analysis the repo uses for `pub` items, since rustc's `dead_code` treats
    every `pub` item of a library crate as a root. Its per-target reports
    were intersected so that anything live under some `cfg` on any shipped
    platform was kept; the repr(C) FFI struct fields and flag/errno table
    entries it also reports were left alone on purpose, as in #38703.
    
    <details>
    <summary>Found dead but left alone (follow-ups, not in this
    diff)</summary>
    
    - `bun_sys::ErrorCase::LeakFdOnFail` is never constructed (every
    `make_lib_uv_owned_for_syscall` caller passes `CloseOnFail`), but
    removing it means dropping the parameter at 13 call sites, several of
    them in files open PRs are editing.
    - `libusockets.h` still declares `us_udp_socket_receive`,
    `us_udp_buffer_set_packet_payload`, `us_create_udp_packet_buffer` and
    `us_udp_socket_bind` with no definitions, and `udp.c` carries a
    commented-out `us_udp_packet_buffer_ecn`; both spots are adjacent to
    hunks in #37454.
    - `NapiEnv::currentFinalizer()` became unused in #37075 a week ago; left
    for that work to settle, as #37454 did with its siblings.
    - `replacements.ts` also emits a `$ImportKindLabelToId` define nothing
    reads, but it comes out of the same loop as three live tables.
    - `ws.js` `WebSocket.prototype.setSocket` (a throwing stub) and
    `async_hooks` `AsyncResource.emitBefore/emitAfter` have no in-tree users
    but are on exported classes.
    - `scripts/packer/build-image.pkr.hcl` and `scripts/trace.sh` have no
    invoker, but they are CI infrastructure rather than code; flagged for
    whoever owns those.
    - Everything else this pass turned up (about 270 C++ symbols and about
    100 Rust items) is already deleted by one of the open dead-code PRs, and
    the roughly 400 never-read Rust fields hawk reports are all repr(C)
    mirrors of C structs.
    </details>
    
    ---------
    
    Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Labels

    None yet

    Projects

    None yet

    8 participants