Skip to content

Commit 743aa89

Browse files
authored
refactor: reuse wrapRequestBody in RedirectHandler (#4992)
Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com>
1 parent 04eee2d commit 743aa89

3 files changed

Lines changed: 49 additions & 51 deletions

File tree

lib/handler/redirect-handler.js

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,13 @@
11
'use strict'
22

33
const util = require('../core/util')
4-
const { kBodyUsed } = require('../core/symbols')
54
const assert = require('node:assert')
65
const { InvalidArgumentError } = require('../core/errors')
7-
const EE = require('node:events')
86

97
const redirectableStatusCodes = [300, 301, 302, 303, 307, 308]
108

11-
const kBody = Symbol('body')
12-
139
const noop = () => {}
1410

15-
class BodyAsyncIterable {
16-
constructor (body) {
17-
this[kBody] = body
18-
this[kBodyUsed] = false
19-
}
20-
21-
async * [Symbol.asyncIterator] () {
22-
assert(!this[kBodyUsed], 'disturbed')
23-
this[kBodyUsed] = true
24-
yield * this[kBody]
25-
}
26-
}
27-
2811
class RedirectHandler {
2912
static buildDispatch (dispatcher, maxRedirections) {
3013
if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) {
@@ -44,43 +27,10 @@ class RedirectHandler {
4427
this.location = null
4528
const { maxRedirections: _, ...cleanOpts } = opts
4629
this.opts = cleanOpts // opts must be a copy, exclude maxRedirections
30+
this.opts.body = util.wrapRequestBody(this.opts.body)
4731
this.maxRedirections = maxRedirections
4832
this.handler = handler
4933
this.history = []
50-
51-
if (util.isStream(this.opts.body)) {
52-
// TODO (fix): Provide some way for the user to cache the file to e.g. /tmp
53-
// so that it can be dispatched again?
54-
// TODO (fix): Do we need 100-expect support to provide a way to do this properly?
55-
if (util.bodyLength(this.opts.body) === 0) {
56-
this.opts.body
57-
.on('data', function () {
58-
assert(false)
59-
})
60-
}
61-
62-
if (typeof this.opts.body.readableDidRead !== 'boolean') {
63-
this.opts.body[kBodyUsed] = false
64-
EE.prototype.on.call(this.opts.body, 'data', function () {
65-
this[kBodyUsed] = true
66-
})
67-
}
68-
} else if (this.opts.body && typeof this.opts.body.pipeTo === 'function') {
69-
// TODO (fix): We can't access ReadableStream internal state
70-
// to determine whether or not it has been disturbed. This is just
71-
// a workaround.
72-
this.opts.body = new BodyAsyncIterable(this.opts.body)
73-
} else if (
74-
this.opts.body &&
75-
typeof this.opts.body !== 'string' &&
76-
!ArrayBuffer.isView(this.opts.body) &&
77-
util.isIterable(this.opts.body) &&
78-
!util.isFormDataLike(this.opts.body)
79-
) {
80-
// TODO: Should we allow re-using iterable if !this.opts.idempotent
81-
// or through some other flag?
82-
this.opts.body = new BodyAsyncIterable(this.opts.body)
83-
}
8434
}
8535

8636
onRequestStart (controller, context) {

test/interceptors/redirect.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,32 @@ for (const factory of [
554554
await t.completed
555555
})
556556

557+
test('should stop following redirects once async iterable request bodies are disturbed', async t => {
558+
t = tspl(t, { plan: 3 })
559+
560+
const server = await startRedirectingServer()
561+
562+
const {
563+
statusCode,
564+
headers,
565+
body: bodyStream
566+
} = await request(t, server, undefined, `http://${server}/301`, {
567+
method: 'PUT',
568+
body: (async function * () {
569+
yield 'REQUEST'
570+
})(),
571+
maxRedirections: 10
572+
})
573+
574+
const body = await bodyStream.text()
575+
576+
t.strictEqual(statusCode, 301)
577+
t.strictEqual(headers.location, `http://${server}/301/2`)
578+
t.strictEqual(body.length, 0)
579+
580+
await t.completed
581+
})
582+
557583
test('should not follow redirects when using Readable request bodies', async t => {
558584
t = tspl(t, { plan: 3 })
559585

test/redirect-request.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,28 @@ for (const factory of [
500500
await t.completed
501501
})
502502

503+
test('should stop following redirects once async iterable request bodies are disturbed', async t => {
504+
t = tspl(t, { plan: 3 })
505+
506+
const server = await startRedirectingServer()
507+
508+
const { statusCode, headers, body: bodyStream } = await request(t, server, undefined, `http://${server}/301`, {
509+
method: 'PUT',
510+
body: (async function * () {
511+
yield 'REQUEST'
512+
})(),
513+
maxRedirections: 10
514+
})
515+
516+
const body = await bodyStream.text()
517+
518+
t.strictEqual(statusCode, 301)
519+
t.strictEqual(headers.location, `http://${server}/301/2`)
520+
t.strictEqual(body.length, 0)
521+
522+
await t.completed
523+
})
524+
503525
test('should not follow redirects when using Readable request bodies', async t => {
504526
t = tspl(t, { plan: 3 })
505527

0 commit comments

Comments
 (0)