Skip to content

Node.js http/https response callback not called in ShadowRealm #25608

@darkleaf

Description

@darkleaf

What version of Bun is used?

1.3.4

What platform is your computer?

Darwin arm64 (macOS)

What steps can reproduce the bug?

import https from 'https'

// Test 1: Node.js http WITHOUT ShadowRealm - WORKS
console.log('Test 1: Node.js https.request WITHOUT ShadowRealm')
await new Promise((resolve) => {
  const req = https.request('https://httpbin.org/get', { method: 'GET' }, (res) => {
    console.log('✓ Response callback, status:', res.statusCode)
    resolve()
  })
  req.on('error', (e) => console.error('✗ Error:', e))
  req.end()
})

// Test 2: Native fetch in ShadowRealm - WORKS
console.log('\nTest 2: Native fetch in ShadowRealm')
const realm = new ShadowRealm()
realm.evaluate(`
fetch('https://httpbin.org/get')
  .then(r => r.json())
  .then(data => console.log('✓ Native fetch works'))
  .catch(e => console.error('✗ Error:', e))
1
`)
await Bun.sleep(3000)

// Test 3: Node.js http IN ShadowRealm - BROKEN
console.log('\nTest 3: Node.js https.request IN ShadowRealm')
realm.evaluate(`
import('https').then(https => {
  const req = https.request('https://httpbin.org/get', { method: 'GET' }, (res) => {
    console.log('✓ Response callback, status:', res.statusCode)  // NEVER CALLED
  })
  req.on('error', (e) => console.error('✗ Error:', e))
  req.end()
})
1
`)
await Bun.sleep(5000)

What is the expected behavior?

All three tests should print their success messages. The response callback in Test 3 should be called just like in Test 1.

What do you see instead?

Test 3 callback is never called. With BUN_CONFIG_VERBOSE_FETCH=curl, I can see the HTTP response IS received (< 200 OK), but the callback never fires.

Test 1 (no realm): ✓ works
Test 2 (realm + fetch): ✓ works
Test 3 (realm + http): ✗ callback never fires

Additional information

This breaks any library that uses Node.js http/https inside ShadowRealm (e.g., happy-dom's fetch implementation).

Output

% bun run bun-shadowrealm-bug.js 
Test 1: Node.js https.request WITHOUT ShadowRealm
[fetch] $ curl --http1.1 "https://httpbin.org/get" -H "Connection: keep-alive" -H "User-Agent: Bun/1.3.4" -H "Accept: */*" -H "Host: httpbin.org"
[fetch] > HTTP/1.1 GET https://httpbin.org/get
[fetch] > Connection: keep-alive
[fetch] > User-Agent: Bun/1.3.4
[fetch] > Accept: */*
[fetch] > Host: httpbin.org

[fetch] < 200 OK
[fetch] < Date: Fri, 19 Dec 2025 14:54:06 GMT
[fetch] < Content-Type: application/json
[fetch] < Content-Length: 252
[fetch] < Connection: keep-alive
[fetch] < Server: gunicorn/19.9.0
[fetch] < Access-Control-Allow-Origin: *
[fetch] < Access-Control-Allow-Credentials: true

✓ Response callback, status: 200

Test 2: Native fetch in ShadowRealm
[fetch] $ curl --http1.1 "https://httpbin.org/get" -H "Connection: keep-alive" -H "User-Agent: Bun/1.3.4" -H "Accept: */*" -H "Host: httpbin.org" -H "Accept-Encoding: gzip, deflate, br, zstd" --compressed
[fetch] > HTTP/1.1 GET https://httpbin.org/get
[fetch] > Connection: keep-alive
[fetch] > User-Agent: Bun/1.3.4
[fetch] > Accept: */*
[fetch] > Host: httpbin.org
[fetch] > Accept-Encoding: gzip, deflate, br, zstd

[fetch] < 200 OK
[fetch] < Date: Fri, 19 Dec 2025 14:54:08 GMT
[fetch] < Content-Type: application/json
[fetch] < Content-Length: 303
[fetch] < Connection: keep-alive
[fetch] < Server: gunicorn/19.9.0
[fetch] < Access-Control-Allow-Origin: *
[fetch] < Access-Control-Allow-Credentials: true

✓ Native fetch works, origin: 95.31.232.55

Test 3: Node.js https.request IN ShadowRealm
  Making request...
  Request sent, waiting for callback...
[fetch] $ curl --http1.1 "https://httpbin.org/get" -H "Connection: keep-alive" -H "User-Agent: Bun/1.3.4" -H "Accept: */*" -H "Host: httpbin.org"
[fetch] > HTTP/1.1 GET https://httpbin.org/get
[fetch] > Connection: keep-alive
[fetch] > User-Agent: Bun/1.3.4
[fetch] > Accept: */*
[fetch] > Host: httpbin.org

[fetch] < 200 OK
[fetch] < Date: Fri, 19 Dec 2025 14:54:10 GMT
[fetch] < Content-Type: application/json
[fetch] < Content-Length: 252
[fetch] < Connection: keep-alive
[fetch] < Server: gunicorn/19.9.0
[fetch] < Access-Control-Allow-Origin: *
[fetch] < Access-Control-Allow-Credentials: true


--- Summary ---
Test 1 (no realm): ✓ works
Test 2 (realm + fetch): ✓ works
Test 3 (realm + http): ✗ callback never fires

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions