Skip to content

Conversation

tawseefnabi
Copy link
Contributor

@tawseefnabi tawseefnabi commented Aug 8, 2025

  • Add try-catch blocks around all session.request() calls in writeH2
  • Implement automatic retry logic for ERR_HTTP2_INVALID_CONNECTION_HEADERS
  • Handle expectContinue, regular request, and CONNECT method paths
  • Destroy session and retry with new connection on first error
  • Abort gracefully if retry also fails
  • Add comprehensive test coverage for error handling

Fixes #4356

Status

- Add try-catch blocks around all session.request() calls in writeH2
- Implement automatic retry logic for ERR_HTTP2_INVALID_CONNECTION_HEADERS
- Handle expectContinue, regular request, and CONNECT method paths
- Destroy session and retry with new connection on first error
- Abort gracefully if retry also fails
- Add comprehensive test coverage for error handling

Fixes nodejs#4356
Copy link
Member

@metcoder95 metcoder95 left a comment

Choose a reason for hiding this comment

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

Good work implementing this, left few comments.

Overall, I'm a bit over the fence of the approach proposed in the issue itself; my concern with constant retries is that we need to keep the state of the request persisted which is not idea. Each request should be independent and fail or get done, not retried internally.
As well, server might never recover, leading to constant similar failures.

I propose the following, if this issue occurs, undici:

  1. Wraps the error in an Undici error
  2. Aborts the request
  3. Drops the session
  4. Resumes the queue

In that way, we add possibility for interceptors and callers to decide how and wether or not to retry the request.

'client-h2.js should contain session.request calls'
)

console.log('✅ client-h2.js contains the necessary error handling code')
Copy link
Member

Choose a reason for hiding this comment

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

let's remove this line


test('undici should recover from invalid HTTP2 headers', async (t) => {
const server = createInvalidHeaderServer(() => {
// console.log('Server listening');
Copy link
Member

Choose a reason for hiding this comment

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

leftover?

callCount++
if (callCount === 1) {
// First request: send invalid HTTP/1 header in HTTP2 response
console.log('[SERVER] Sending invalid header response')
Copy link
Member

Choose a reason for hiding this comment

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

leftover?

stream.end('hello')
} else {
// Second request (retry): send valid response
console.log('[SERVER] Sending valid response')
Copy link
Member

Choose a reason for hiding this comment

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

ditto

const { Client } = require('..')
const pem = require('https-pem')

const PORT = 5678
Copy link
Member

Choose a reason for hiding this comment

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

No need to hardcode port, when listening without a port, a random port will be picked; we can get it later through server.address method.

const server = http2.createSecureServer(pem)
let callCount = 0
server.on('stream', (stream, headers) => {
console.log('[SERVER] Received stream, callCount:', callCount + 1)
Copy link
Member

Choose a reason for hiding this comment

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

leftover?

assert.ok(errorCaught, 'Error should be catchable')
assert.strictEqual(errorCode, 'ERR_HTTP2_INVALID_CONNECTION_HEADERS', 'Error code should match')

console.log('✅ ERR_HTTP2_INVALID_CONNECTION_HEADERS can be caught and handled')
Copy link
Member

Choose a reason for hiding this comment

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

let's remove it

client[kHTTP2Session] = null
}
// Leave request in queue for retry
setImmediate(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why did you use setImmediate?

@Uzlopak
Copy link
Contributor

Uzlopak commented Aug 10, 2025

@metcoder95
Looks like AI slop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Invalid HTTP2 headers should be recoverable
3 participants