@@ -286,50 +286,94 @@ export function getH2HeadersAfterModification(
286
286
} ;
287
287
}
288
288
289
- // Helper to handle content-length nicely for you when rewriting requests with callbacks
290
- export function getContentLengthAfterModification (
289
+ // When modifying requests, we ensure you always have correct framing, as it's impossible
290
+ // to send a request with framing that doesn't match the body.
291
+ export function getRequestContentLengthAfterModification (
291
292
body : string | Uint8Array | Buffer ,
292
293
originalHeaders : Headers | RawHeaders ,
293
294
replacementHeaders : Headers | RawHeaders | undefined ,
294
- mismatchAllowed : boolean = false
295
+ context : {
296
+ httpVersion : 1 | 2
297
+ // N.b. we ignore the method though - you can proxy requests that include a body
298
+ // even if they really shouldn't, as long as it's plausibly parseable.
299
+ }
295
300
) : string | undefined {
296
301
// If there was a content-length header, it might now be wrong, and it's annoying
297
302
// to need to set your own content-length override when you just want to change
298
- // the body. To help out, if you override the body but don't explicitly override
299
- // the (now invalid) content-length, then we fix it for you.
303
+ // the body. To help out, if you override the body in a way that results in invalid
304
+ // content-length headers, we fix them for you.
305
+
306
+ // For HTTP/2, framing is optional/advisory so we can just skip this entirely.
307
+ if ( context . httpVersion !== 1 ) return undefined ;
308
+
309
+ const resultingHeaders = replacementHeaders || originalHeaders ;
300
310
301
- if ( getHeaderValue ( originalHeaders , 'content-length' ) === undefined ) {
302
- // Nothing to override - use the replacement value, or undefined
303
- return getHeaderValue ( replacementHeaders || { } , 'content-length' ) ;
311
+ if ( getHeaderValue ( resultingHeaders , 'transfer-encoding' ) ?. includes ( 'chunked' ) ) {
312
+ return undefined ; // No content-length header games needed
304
313
}
305
314
306
- if ( ! replacementHeaders ) {
307
- // There was a length set, and you've provided a body but not changed it.
308
- // You probably just want to send this body and have it work correctly,
309
- // so we should fix the content length for you automatically.
310
- return byteLength ( body ) . toString ( ) ;
315
+ const expectedLength = byteLength ( body ) . toString ( ) ;
316
+ const contentLengthHeader = getHeaderValue ( resultingHeaders , 'content-length' ) ;
317
+
318
+ if ( contentLengthHeader === expectedLength ) return undefined ;
319
+ if ( contentLengthHeader === undefined ) return expectedLength ; // Differs from responses
320
+
321
+ // The content-length is expected, but it's wrong or missing.
322
+
323
+ // If there is a wrong content-length set, and it's not just leftover from the original headers (i.e.
324
+ // you intentionally set it) then we show a warning since we're ignoring your (invalid) instructions.
325
+ if ( contentLengthHeader && contentLengthHeader !== getHeaderValue ( originalHeaders , 'content-length' ) ) {
326
+ console . warn ( `Invalid request content-length header was ignored - resetting from ${
327
+ contentLengthHeader
328
+ } to ${
329
+ expectedLength
330
+ } `) ;
311
331
}
312
332
313
- // There was a content length before, and you're replacing the headers entirely
314
- const lengthOverride = getHeaderValue ( replacementHeaders , 'content-length' ) ?. toString ( ) ;
333
+ return expectedLength ;
334
+ }
315
335
316
- // If you're setting the content-length to the same as the origin headers, even
317
- // though that's the wrong value, it *might* be that you're just extending the
318
- // existing headers, and you're doing this by accident (we can't tell for sure).
319
- // We use invalid content-length as instructed, but print a warning just in case.
320
- if (
321
- lengthOverride === getHeaderValue ( originalHeaders , 'content-length' ) &&
322
- lengthOverride !== byteLength ( body ) . toString ( ) &&
323
- ! mismatchAllowed // Set for HEAD responses
324
- ) {
325
- console . warn ( oneLine `
326
- Passthrough modifications overrode the body and the content-length header
327
- with mismatched values, which may be a mistake. The body contains
328
- ${ byteLength ( body ) } bytes, whilst the header was set to ${ lengthOverride } .
329
- ` ) ;
336
+ // When modifying responses, we ensure you always have correct framing, but in a slightly more
337
+ // relaxed way than for requests: we allow no framing and HEAD responses, we just block invalid values.
338
+ export function getResponseContentLengthAfterModification (
339
+ body : string | Uint8Array | Buffer ,
340
+ originalHeaders : Headers | RawHeaders ,
341
+ replacementHeaders : Headers | RawHeaders | undefined ,
342
+ context : {
343
+ httpMethod : string
344
+ httpVersion : 1 | 2
345
+ }
346
+ ) : string | undefined {
347
+ // For HEAD requests etc, you can set an arbitrary content-length header regardless
348
+ // of the empty body, so we don't bother checking anything. For HTTP/2, framing is
349
+ // optional/advisory so we can just skip this entirely.
350
+ if ( context . httpVersion !== 1 || context . httpMethod === 'HEAD' ) return undefined ;
351
+
352
+ const resultingHeaders = replacementHeaders || originalHeaders ;
353
+
354
+ if ( getHeaderValue ( resultingHeaders , 'transfer-encoding' ) ?. includes ( 'chunked' ) ) {
355
+ return undefined ; // No content-length header games needed
356
+ }
357
+
358
+ const expectedLength = byteLength ( body ) . toString ( ) ;
359
+ const contentLengthHeader = getHeaderValue ( resultingHeaders , 'content-length' ) ;
360
+
361
+ if ( contentLengthHeader === expectedLength ) return undefined ;
362
+ if ( contentLengthHeader === undefined ) return undefined ; // Differs from requests - we do allow this for responses
363
+
364
+ // The content-length is set, but it's wrong.
365
+
366
+ // If there is a wrong content-length set, and it's not just leftover from the original headers (i.e.
367
+ // you intentionally set it) then we show a warning since we're ignoring your (invalid) instructions.
368
+ if ( contentLengthHeader && contentLengthHeader !== getHeaderValue ( originalHeaders , 'content-length' ) ) {
369
+ console . warn ( `Invalid response content-length header was ignored - resetting from ${
370
+ contentLengthHeader
371
+ } to ${
372
+ expectedLength
373
+ } `) ;
330
374
}
331
375
332
- return lengthOverride ;
376
+ return expectedLength ;
333
377
}
334
378
335
379
// Function to check if we should skip https errors for the current hostname and port,
0 commit comments