Closed Bug 1860293 Opened 1 year ago Closed 1 year ago

Request constructor uses wrong body when given another request as input and options with a body

Categories

(Core :: DOM: Networking, defect, P2)

Firefox 118
defect

Tracking

()

RESOLVED FIXED
124 Branch
Tracking Status
firefox124 --- fixed

People

(Reporter: jarens, Assigned: dotoole)

References

(Blocks 1 open bug)

Details

(Whiteboard: [necko-triaged][necko-priority-queue], [wptsync upstream])

Attachments

(1 file)

When creating a new Request in JavaScript, an input and options may be passed as arguments to the Request's constructor. If input is another Request instance where its body has been consumed (disrupted or locked), and options contains a body property, the new Request instance will use the body from input, and not from options, causing an an error to be thrown because the input request's body can no longer be used.

The fetch request class spec, starting at step 34 under "The new Request(input, init) constructor steps are: " defines the steps that should be taken regarding body when constructing a new Request. In particular, step 38 essentially says that if init has a body and it's non-null then it should take precedence over input.

In Request.cpp starting at line 280 it looks like the constructor checks if aInput is a request instance, then gets and uses the body from it without first checking if aInit has a body.

Chrome, Safari, and Node.js >= 18 all seem to conform to the spec, whereas Firefox does not.

Reproduction steps:

async function test() {
  const req1 = new Request('/', {
    body: 'req1',
    method: 'POST',
  });

  await req1.text(); // Consume the Request's body.

  // The `req2` body passed in options should take precedence, and the
  // `Request` constructor should not throw an error.
  const req2 = new Request(req1, { body: 'req2' });
}

test()
  .then(() => { console.log('test completed with no errors'); })
  .catch((err) => { console.error('test failed:', err.message); });

For context, my use case is that I'd like to "reuse" a request after it's been fetched. For example, retrying a request with a refreshed CSRF token header when the first request receives a 401 response. Constructing a new request instance out of the original with the initial body passed in through the constructor's options would be the best option because all of the original request's properties would be passed along to the new request. This is not trivial to do in JavaScript because Request is not enumerable, and properties like headers have interior mutability, which would need to be tracked or manually copied from the original request into the new request's options.

Use case:

// This is somewhat simplified from what I'm trying to do in the real
// world, but this is the basic idea.

const body = JSON.stringify({ foo: 'bar' });

let request = new Request('/', {
  body,
  headers: {
    'x-csrf-token': 'expired token value',
  },
  method: 'POST',
});

let response = await fetch(request);

if (response.status === 401) {
  request = new Request(request, { body });

  request.headers.set('x-csrf-token', 'refreshed token value');

  response = await fetch(request);
}

Thank you for the great bug report. This seems straightforward to fix.

Blocks: fetch
Severity: -- → S3
Status: UNCONFIRMED → NEW
Ever confirmed: true
Priority: -- → P2
Whiteboard: [necko-triaged][necko-priority-new]
Whiteboard: [necko-triaged][necko-priority-new] → [necko-triaged][necko-priority-next]
See Also: → 1387483
Assignee: nobody → dotoole
Whiteboard: [necko-triaged][necko-priority-next] → [necko-triaged][necko-priority-queue]
Pushed by dotoole@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/effe0ba31234 Request::Contructor init body now takes precendence over input body. r=valentin
Created web-platform-tests PR https://github.com/web-platform-tests/wpt/pull/44500 for changes under testing/web-platform/tests
Whiteboard: [necko-triaged][necko-priority-queue] → [necko-triaged][necko-priority-queue], [wptsync upstream]
Upstream PR merged by moz-wptsync-bot
Status: NEW → RESOLVED
Closed: 1 year ago
Resolution: --- → FIXED
Target Milestone: --- → 124 Branch
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: