Skip to content

Instantly share code, notes, and snippets.

@bartlomieju
Created April 11, 2026 15:08
Show Gist options
  • Select an option

  • Save bartlomieju/797659a33c1aa906c411502b71d1416d to your computer and use it in GitHub Desktop.

Select an option

Save bartlomieju/797659a33c1aa906c411502b71d1416d to your computer and use it in GitHub Desktop.
Deno vs Node.js node:http implementation differences - exhaustive comparison

Deno vs Node.js node:http Implementation Differences

Summary of Critical/High-Risk Issues

  1. _http_outgoing.ts line 996: toLowerCasee() typo -- CRITICAL. Breaks uniqueHeaders feature entirely.
  2. _http_outgoing.ts: write() does not set kNeedDrain -- HIGH. Backpressure/drain events broken.
  3. _http_outgoing.ts: addTrailers() not implemented -- HIGH. Throws on use.
  4. _http_outgoing.ts: Cookie header joining missing -- HIGH. Multiple cookie values sent as separate headers.
  5. _http_outgoing.ts: _removedConnection forces close -- HIGH. Connection always closed when default headers removed.
  6. _http_server.js: Missing requestTimeout/headersTimeout -- HIGH. No slow-loris protection.
  7. _http_server.js: Missing clientError event and error responses -- HIGH. No graceful handling of malformed requests.
  8. _http_server.js: writeHead() missing _hasBody = false for 204/304 -- HIGH. Bodies on 204/304 responses allowed.
  9. _http_agent.mjs: defaultPort/protocol ignore options -- HIGH. HTTPS agent subclassing broken.
  10. _http_agent.mjs: maxSockets effectively ignored -- HIGH. No connection pooling limits.
  11. _http_agent.mjs: keepSocketAlive() ignores server Keep-Alive hints -- HIGH. ECONNRESET risk.
  12. _http_agent.mjs: ObjectValues not defined -- HIGH. keylog listener throws ReferenceError.

1. _http_common (Deno .ts vs Node .js)

isLenient()

  • Node: Reads --insecure-http-parser CLI flag via getOptionValue(), emits a one-time warning.
  • Deno: Always returns false.
  • Bug risk: Medium.

checkIsHttpToken()

  • Node: Optimized lookup table for short strings (< 10 chars); regex fallback.
  • Deno: Always uses regex.
  • Bug risk: Low (perf only).

freeParser() -- setImmediate vs setTimeout

  • Node: Uses setImmediate(closeParserInstance, parser).
  • Deno: Uses setTimeout(closeParserInstance, 0, parser).
  • Bug risk: Low.

2. _http_agent (Deno .mjs vs Node .js)

Agent() constructor -- defaultPort and protocol

  • Node: this.defaultPort = this.options.defaultPort || 80 and this.protocol = this.options.protocol || 'http:' -- reads from options.
  • Deno: Hardcoded this.defaultPort = 80 and this.protocol = "http:".
  • Bug risk: HIGH. HTTPS agent subclassing broken.

Agent() constructor -- options.noDelay

  • Node: Sets this.options.noDelay = true if undefined.
  • Deno: Missing.
  • Bug risk: Medium. Nagle's algorithm not disabled by default.

Agent() constructor -- agentKeepAliveTimeoutBuffer

  • Node: Configurable (default 1000ms).
  • Deno: Missing.
  • Bug risk: Medium.

addRequest() -- maxSockets check

  • Node: Checks sockLen < this.maxSockets.
  • Deno: Checks this.maxSockets != 0 (effectively ignores maxSockets).
  • Bug risk: HIGH.

keepSocketAlive() -- Keep-Alive header hint

  • Node: Parses Keep-Alive: timeout=N header, adjusts timeout, may return false.
  • Deno: Simply calls socket.setKeepAlive(true, ...), always returns true.
  • Bug risk: HIGH. ECONNRESET risk.

createSocket() -- timeout/keepAlive propagation

  • Node: Copies req.timeout, this.keepAlive, this.keepAliveMsecs into socket options.
  • Deno: Does not propagate.
  • Bug risk: Medium.

ObjectValues not defined

  • Deno: maybeEnableKeylog references ObjectValues which is never imported.
  • Bug risk: HIGH. Throws ReferenceError when keylog listener added.

3. _http_client (Deno .js vs Node .js)

Missing: diagnostics_channel integration

  • Node: Publishes to 4 diagnostics channels.
  • Deno: None.
  • Bug risk: Medium.

Missing: HTTPClientAsyncResource

  • Node: Creates async resource for context tracking.
  • Deno: Passes {}.
  • Bug risk: Medium.

Missing: ClientRequest.prototype._finish() override

  • Node: Triggers perf observers, diagnostics, trace events.
  • Deno: No override. Has OTel in _implicitHeader instead.
  • Bug risk: Low.

responseKeepAlive() -- parser cleanup divergence

  • Deno: Adds parser.finish() and freeParser() before emitFreeNT.
  • Node: Does NOT free parser in responseKeepAlive.
  • Bug risk: Potential behavioral divergence. Needs investigation.

responseKeepAlive() -- async context

  • Node: Uses defaultTriggerAsyncIdScope(asyncId, process.nextTick, ...).
  • Deno: Uses nextTick(emitFreeNT, req) without async context.
  • Bug risk: Medium.

4. _http_server (Deno .js vs Node .js)

Missing: storeHTTPOptions() -- many server options

  • Node: Processes requestTimeout (default 300000ms), headersTimeout (default 60000ms), keepAliveTimeoutBuffer, connectionsCheckingInterval, optimizeEmptyRequests, joinDuplicateHeaders, shouldUpgradeCallback.
  • Deno: Most missing or hardcoded differently. No requestTimeout, no headersTimeout.
  • Bug risk: HIGH. No slow-loris protection.

Missing: ConnectionsList and timeout checking

  • Node: Uses ConnectionsList + setInterval checker for enforcing timeouts.
  • Deno: Simple Set for connections, no timeout checking.
  • Bug risk: HIGH.

socketOnError() -- clientError event

  • Node: Emits clientError on server, sends HTTP error responses (400, 408, 431, 413).
  • Deno: Just destroys the socket. No clientError event.
  • Bug risk: HIGH.

socketOnEnd() -- error routing

  • Node: Calls socketOnError.call(socket, ret) which emits clientError.
  • Deno: Calls socket.destroy(ret) directly.
  • Bug risk: HIGH.

writeHead() -- missing _hasBody = false for 204/304

  • Node: Enforces no-body for 204, 304, 1xx.
  • Deno: Missing.
  • Bug risk: HIGH.

Missing: writeEarlyHints()

  • Bug risk: Medium.

resOnFinish() -- clearIncoming() logic

  • Node: Checks parser.incoming === req, waits for readableEnded, resets parser.incoming.
  • Deno: Simplified. Possible memory leaks.
  • Bug risk: Medium.

Missing: socketOnDrain() -- message drain

  • Node: Emits drain on the HTTP message.
  • Deno: Does not.
  • Bug risk: Medium. Backpressure signaling incomplete.

connectionListenerInternal() -- missing parser consume

  • Node: Uses parser.consume(socket._handle) to bypass readable stream.
  • Deno: Uses normal socket.on('data').
  • Bug risk: Medium (perf).

5. _http_outgoing (Deno .ts vs Node .js)

parseUniqueHeadersOption() -- TYPO

  • Deno: headers[i].toLowerCasee() (extra 'e').
  • Node: headers[i].toLowerCase().
  • Bug risk: CRITICAL. Throws TypeError whenever uniqueHeaders is used.

Missing: strictContentLength enforcement

  • Bug risk: Medium.

Missing: kChunkedBuffer / kChunkedLength

  • Bug risk: Medium.

Missing: kSocket symbol indirection

  • Bug risk: Medium.

write() -- kNeedDrain not set

  • Node: Sets this[kNeedDrain] = true on false return from write_().
  • Deno: Does not set.
  • Bug risk: HIGH. Drain events broken.

write_() -- missing error checks for finished/destroyed

  • Bug risk: Medium.

write_() -- no auto-corking

  • Bug risk: Low-medium.

addTrailers() -- throws notImplemented

  • Bug risk: HIGH.

_storeHeader() -- Cookie header joining missing

  • Node: Joins multiple Cookie values with '; '.
  • Deno: Sends as separate header lines.
  • Bug risk: HIGH. RFC 6265 violation.

_storeHeader() -- _removedConnection forces close

  • Node: this._last = !this.shouldKeepAlive.
  • Deno: this._last = true; this.shouldKeepAlive = false.
  • Bug risk: HIGH.

Missing: setHeaders() method

  • Bug risk: Medium.

6. _http_incoming (Deno .js vs Node .js)

Nearly identical. No significant functional differences.


7. http (Deno .ts vs Node .js)

METHODS not sorted

  • Bug risk: Low.

maxHeaderSize hardcoded

  • Bug risk: Low.

Missing: setMaxIdleHTTPParsers(), setGlobalProxyFromEnv(), WebSocket exports

  • Bug risk: Low.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment