Skip to content

Commit 58bf844

Browse files
zmunmmeta-codesync[bot]
authored andcommitted
Fix XMLHttpRequest.setRequestHeader to append duplicate headers per spec
Summary: `XMLHttpRequest.setRequestHeader()` overwrites the previous value when called multiple times with the same header name. Per the [XHR Living Standard §4.5.2](https://xhr.spec.whatwg.org/#the-setrequestheader()-method), duplicate headers should be appended with `, `. Real-world example: [Sentry JS SDK assumes append behavior](https://github.com/getsentry/sentry-javascript/blob/10.38.0/packages/browser/src/tracing/request.ts#L440-L445) when setting the `baggage` header, which causes previously set baggage values to be silently dropped on React Native. ## Changelog: <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests --> [GENERAL] [FIXED] - Fix XMLHttpRequest.setRequestHeader to append duplicate headers per spec > **Note:** This is technically a breaking change for code that relied on the previous overwrite behavior (e.g., calling `setRequestHeader` twice to replace a value). However, that behavior was non-compliant with the XHR spec, so such code should use a single call with the desired final value instead. X-link: #56394 Reviewed By: huntie, cipolleschi Differential Revision: D100144689 Pulled By: fabriziocucci fbshipit-source-id: 0697d88e02f76285ff50e339b0c25cda8337c36c
1 parent aadcade commit 58bf844

2 files changed

Lines changed: 37 additions & 1 deletion

File tree

packages/react-native/Libraries/Network/XMLHttpRequest.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,10 @@ class XMLHttpRequest extends EventTarget {
513513
if (this.readyState !== this.OPENED) {
514514
throw new Error('Request has not been opened');
515515
}
516-
this._headers[header.toLowerCase()] = String(value);
516+
const key = header.toLowerCase();
517+
const existing = this._headers[key];
518+
this._headers[key] =
519+
existing !== undefined ? existing + ', ' + String(value) : String(value);
517520
}
518521

519522
/**

packages/react-native/Libraries/Network/__tests__/XMLHttpRequest-test.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,39 @@ describe('XMLHttpRequest', function () {
268268
);
269269
});
270270

271+
it('should append values when setRequestHeader is called with the same header', function () {
272+
xhr.open('GET', 'blabla');
273+
xhr.setRequestHeader('X-Custom', 'value1');
274+
xhr.setRequestHeader('X-Custom', 'value2');
275+
276+
// $FlowFixMe[prop-missing]
277+
expect(xhr._headers['x-custom']).toBe('value1, value2');
278+
});
279+
280+
it('should append to a header previously set to an empty string', function () {
281+
xhr.open('GET', 'blabla');
282+
xhr.setRequestHeader('X-Custom', '');
283+
xhr.setRequestHeader('X-Custom', 'value');
284+
285+
// $FlowFixMe[prop-missing]
286+
expect(xhr._headers['x-custom']).toBe(', value');
287+
});
288+
289+
it('should lowercase header names in setRequestHeader', function () {
290+
xhr.open('GET', 'blabla');
291+
xhr.setRequestHeader('Content-Type', 'application/json');
292+
xhr.setRequestHeader('content-type', 'text/plain');
293+
294+
// $FlowFixMe[prop-missing]
295+
expect(xhr._headers['content-type']).toBe('application/json, text/plain');
296+
});
297+
298+
it('should throw when setRequestHeader is called before open', function () {
299+
expect(() => {
300+
xhr.setRequestHeader('foo', 'bar');
301+
}).toThrow('Request has not been opened');
302+
});
303+
271304
it('should sort and lowercase response headers', function () {
272305
// Derived from XHR Web Platform Test: https://github.com/web-platform-tests/wpt/blob/master/xhr/getallresponseheaders.htm
273306
xhr.open('GET', 'blabla');

0 commit comments

Comments
 (0)