Fix onprogress not firing when Content-Length is not available for XMLHttpRequest (#44899)

Summary:
When an XMLHttpRequest is performed, the `onprogress` event it is not invoked when the `Content-Length` header is missing in the response. This is the case when we are calling an endpoint that responds with `transfer-encoding: chunked` (https://tools.ietf.org/html/rfc9112#section-7.1), preventing the user to keep track of the progress while the server is sending chunks. Despite we will never know the total length of the content (because it will not be known due to the RFC specification, so it will be always `-1`), we will now be able to keep track of the loaded data.

Note that in Android, this is the current default behaviour.

To address this issue:
- I removed the condition where the `downloadProgressBlock` was dispatched only when  `response.expectedContentLength` was greater than 0
- I created a new test case for `XMLHttpRequest` in the tester app to download a chunked file

## Changelog:

[IOS] [CHANGED] - fire `onprogress` event for `XMLHttpRequest` even when the `Content-Length` header is missing in the response headers

Pull Request resolved: https://github.com/facebook/react-native/pull/44899

Test Plan:
|before|after|
|----------|:-------------:|
|https://github.com/facebook/react-native/assets/37150312/6da3518f-eed3-4808-a2f8-abe26e5c7487|https://github.com/facebook/react-native/assets/37150312/ed1da300-dcf7-4874-a941-a2289f1cb777

Reviewed By: cortinico

Differential Revision: D58562088

Pulled By: NickGerleman

fbshipit-source-id: 23a1cafa49ddcd25fa0db7d04fae845126771425
This commit is contained in:
alicata
2024-06-14 13:58:14 -07:00
committed by Facebook GitHub Bot
parent 9d637e4622
commit 457d14bd1b
2 changed files with 27 additions and 8 deletions
@@ -190,7 +190,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)init)
incrementalDataBlock(data, length, total);
}];
}
if (_downloadProgressBlock && total > 0) {
if (_downloadProgressBlock) {
RCTURLRequestProgressBlock downloadProgressBlock = _downloadProgressBlock;
[self dispatchCallback:^{
downloadProgressBlock(length, total);
@@ -40,6 +40,7 @@ class XHRExampleDownload extends React.Component<{...}, Object> {
readystateHandler: false,
progressHandler: true,
arraybuffer: false,
chunked: false,
};
xhr: ?XMLHttpRequest = null;
@@ -107,12 +108,19 @@ class XHRExampleDownload extends React.Component<{...}, Object> {
Alert.alert('Error', xhr.responseText);
}
};
xhr.open(
'GET',
'http://aleph.gutenberg.org/cache/epub/100/pg100-images.html.utf8',
);
// Avoid gzip so we can actually show progress
xhr.setRequestHeader('Accept-Encoding', '');
if (this.state.chunked) {
xhr.open(
'GET',
'https://filesamples.com/samples/ebook/azw3/Around%20the%20World%20in%2028%20Languages.azw3',
);
} else {
xhr.open(
'GET',
'http://aleph.gutenberg.org/cache/epub/100/pg100-images.html.utf8',
);
// Avoid gzip so we can actually show progress
xhr.setRequestHeader('Accept-Encoding', '');
}
xhr.send();
this.setState({downloading: true});
@@ -133,7 +141,11 @@ class XHRExampleDownload extends React.Component<{...}, Object> {
) : (
<TouchableHighlight style={styles.wrapper} onPress={this._download}>
<View style={styles.button}>
<Text>Download 7MB Text File</Text>
<Text>
{this.state.chunked
? 'Download 10MB File'
: 'Download 19KB pdf File'}
</Text>
</View>
</TouchableHighlight>
);
@@ -188,6 +200,13 @@ class XHRExampleDownload extends React.Component<{...}, Object> {
onValueChange={arraybuffer => this.setState({arraybuffer})}
/>
</View>
<View style={styles.configRow}>
<Text>transfer-encoding: chunked</Text>
<Switch
value={this.state.chunked}
onValueChange={chunked => this.setState({chunked})}
/>
</View>
{button}
{readystate}
{progress}