Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] Added progress updates for all XMLHttpRequest upload types #16541

Closed
wants to merge 2 commits into from

Conversation

allengleyzer
Copy link
Contributor

@allengleyzer allengleyzer commented Oct 25, 2017

Motivation

Previously, only form-data request bodies emitted upload progress updates. Now,
other request body types will also emit updates.

Addresses issues:
#15724
#11853

This is a bug fix for functionality that's missing on Android. These events are already working correctly on iOS.

Test Plan

Run the following code on Android, and ensure that events are being sent:

const fileUri = 'file:///my_file.dat';
const url = 'http://my_post_url.com/';
const xhr = new XMLHttpRequest();

xhr.upload.onprogress = (event) => {
    console.log('progress: ' + event.loaded + ' / ' + event.total);
}

xhr.onreadystatechange = () => {if (xhr.readyState === 4) console.log('done');} 

console.log('start');

xhr.open('POST', url);

// sending a file (wasn't sending progress)
xhr.setRequestHeader('Content-Type', 'image/jpeg');
xhr.send({ uri: fileUri });

// sending a string
xhr.setRequestHeader('Content-Type', 'text/plain');
xhr.send("some big string"); // sending a string (wasn't sending progress)

// sending form data
xhr.setRequestHeader('Content-Type', 'multipart/form-data');
const formData = new FormData(); formData.append('test', 'data');
xhr.send(formData); // sending form data (was already working)

Release Notes

[ANDROID] [BUGFIX] [XMLHttpRequest] - Added progress updates for all XMLHttpRequest upload types

Previously, only form-data request bodies emitted upload progress updates. Now,
other request body types will also emit updates.

Addresses issues:
#15724
#11853

Previously, only form-data request bodies emitted upload progress updates. Now,
other request body types will also emit updates.

Addresses issues:
facebook#15724
facebook#11853
@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 25, 2017
Fixes UnsatisfiedLinkError while running unit test
Copy link
Contributor

@janicduplessis janicduplessis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, test plan and code looks good to me. Also CI is green 👍

@fengyibo
Copy link

when to shipit @janicduplessis @satya164

@janicduplessis
Copy link
Contributor

@facebook-github-bot shipit

@facebook-github-bot facebook-github-bot added the Import Started This pull request has been imported. This does not imply the PR has been approved. label Nov 14, 2017
Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@janicduplessis is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@iamtekeste
Copy link

@janicduplessis Do you know on what version of RN this would be available?

cdlewis pushed a commit to cdlewis/react-native that referenced this pull request Nov 19, 2017
Summary:
Previously, only form-data request bodies emitted upload progress updates. Now,
other request body types will also emit updates.

Addresses issues:
facebook#15724
facebook#11853

This is a bug fix for functionality that's missing on Android. These events are already working correctly on iOS.

Run the following code on Android, and ensure that events are being sent:
```
const fileUri = 'file:///my_file.dat';
const url = 'http://my_post_url.com/';
const xhr = new XMLHttpRequest();

xhr.upload.onprogress = (event) => {
    console.log('progress: ' + event.loaded + ' / ' + event.total);
}

xhr.onreadystatechange = () => {if (xhr.readyState === 4) console.log('done');}

console.log('start');

xhr.open('POST', url);

xhr.send({ uri: fileUri }); // sending a file (wasn't sending progress)
xhr.send("some big string"); // sending a string (wasn't sending progress)

const formData = new FormData(); formData.set('test', 'data');
xhr.send(formData); // sending form data (was already working)
```

[ANDROID] [BUGFIX] [XMLHttpRequest] - Added progress updates for all XMLHttpRequest upload types

Previously, only form-data request bodies emitted upload progress updates. Now,
other request body types will also emit updates.

Addresses issues:
facebook#15724
facebook#11853
Closes facebook#16541

Differential Revision: D6325252

Pulled By: hramos

fbshipit-source-id: 4fe617216293e6f451e2a1af4fa872e8f56d4f93
bowerman0 pushed a commit to bowerman0/react-native that referenced this pull request Dec 5, 2017
Summary:
Previously, only form-data request bodies emitted upload progress updates. Now,
other request body types will also emit updates.

Addresses issues:
facebook#15724
facebook#11853

This is a bug fix for functionality that's missing on Android. These events are already working correctly on iOS.

Run the following code on Android, and ensure that events are being sent:
```
const fileUri = 'file:///my_file.dat';
const url = 'http://my_post_url.com/';
const xhr = new XMLHttpRequest();

xhr.upload.onprogress = (event) => {
    console.log('progress: ' + event.loaded + ' / ' + event.total);
}

xhr.onreadystatechange = () => {if (xhr.readyState === 4) console.log('done');}

console.log('start');

xhr.open('POST', url);

xhr.send({ uri: fileUri }); // sending a file (wasn't sending progress)
xhr.send("some big string"); // sending a string (wasn't sending progress)

const formData = new FormData(); formData.set('test', 'data');
xhr.send(formData); // sending form data (was already working)
```

[ANDROID] [BUGFIX] [XMLHttpRequest] - Added progress updates for all XMLHttpRequest upload types

Previously, only form-data request bodies emitted upload progress updates. Now,
other request body types will also emit updates.

Addresses issues:
facebook#15724
facebook#11853
Closes facebook#16541

Differential Revision: D6325252

Pulled By: hramos

fbshipit-source-id: 4fe617216293e6f451e2a1af4fa872e8f56d4f93
bowerman0 pushed a commit to bowerman0/react-native that referenced this pull request Dec 5, 2017
Summary:
Previously, only form-data request bodies emitted upload progress updates. Now,
other request body types will also emit updates.

Addresses issues:
facebook#15724
facebook#11853

This is a bug fix for functionality that's missing on Android. These events are already working correctly on iOS.

Run the following code on Android, and ensure that events are being sent:
```
const fileUri = 'file:///my_file.dat';
const url = 'http://my_post_url.com/';
const xhr = new XMLHttpRequest();

xhr.upload.onprogress = (event) => {
    console.log('progress: ' + event.loaded + ' / ' + event.total);
}

xhr.onreadystatechange = () => {if (xhr.readyState === 4) console.log('done');}

console.log('start');

xhr.open('POST', url);

xhr.send({ uri: fileUri }); // sending a file (wasn't sending progress)
xhr.send("some big string"); // sending a string (wasn't sending progress)

const formData = new FormData(); formData.set('test', 'data');
xhr.send(formData); // sending form data (was already working)
```

[ANDROID] [BUGFIX] [XMLHttpRequest] - Added progress updates for all XMLHttpRequest upload types

Previously, only form-data request bodies emitted upload progress updates. Now,
other request body types will also emit updates.

Addresses issues:
facebook#15724
facebook#11853
Closes facebook#16541

Differential Revision: D6325252

Pulled By: hramos

fbshipit-source-id: 4fe617216293e6f451e2a1af4fa872e8f56d4f93
@fengyibo
Copy link

Who knows on what version of RN this would be available?

fengyibo referenced this pull request Dec 13, 2017
Reviewed By: fred2028

Differential Revision: D6539205

fbshipit-source-id: c97d4f3dbd457f59968991b043d7106e551effad
@RSNara
Copy link
Contributor

RSNara commented Dec 13, 2017

There was a surge in crashes with a recent release of one of our apps. We traced the crashes down to this diff and consequently reverted it. Once the next release ships, we'll know for sure if this diff was the cause.

@allengleyzer, The crash reports state that an IllegalStateException was raised. Do you have idea why the changes in this diff could raise it?

@allengleyzer
Copy link
Contributor Author

allengleyzer commented Dec 13, 2017

@RSNara If I had to guess, it's probably happening while trying to read an uploaded file's size, since this is an IO operation that wasn't happening before. Looking around, it seems that if an InputStream gets closed unexpectedly (e.g. due to a loss in a user's internet connection), this exception might get thrown.

Do you allow users to upload certain forms of data in your app?

I'll investigate some more to confirm if this is the cause.

Thanks for the report, sorry about the issues you've been having.

@fred2028
Copy link
Contributor

Hi @allengleyzer , yes, in Facebook Ads Manager, a user can upload photos and videos as part of their ad creation/edit process. It is definitely a possibility that a user can lose his or her Internet connection during this process.

@allengleyzer
Copy link
Contributor Author

allengleyzer commented Dec 14, 2017

@RSNara @fred2028 I wasn't able to reproduce the issue. Disconnecting in the middle of an upload didn't raise any exceptions on my device, and I haven't experienced problems with letting uploads go through normally either. Tried in both release and debug builds (though, on a debug build, if I was also connected to the JS debugger, then disconnecting at any point usually resulted in an IllegalStateException, though this problem existed before this PR and is due to the JS debugging client, and so wouldn't happen in a release build).

Any chance you have more information, such as a stack trace or file/line number? Also, which RN version does this happen on, and is it isolated to a particular OS version, or does it happen on all versions? The app I'm working on is still in production, so I haven't been able to test this change on a large scale.

@allengleyzer
Copy link
Contributor Author

allengleyzer commented Dec 20, 2017

I'm going to guess that if this PR was the cause, then most likely the app crashes for the same reason that #10423 was already happening. This PR might cause the issue to happen more frequently due to a ProgressRequestBody being created for all uploads containing a body, instead of just multipart form-data, as was the case before (and hence the bug this PR fixes).

The fix for the crash bug would probably just be to insert a try-catch handler on the write operation, to prevent the app from crashing, since there's no way to check to see whether the sink is open beforehand.

@LEEY19
Copy link

LEEY19 commented Dec 21, 2017

@janicduplessis @allengleyzer guys, are you sure that this commit has fixed the xhr.upload.progress issue? I have copied pasted your codes but issue still persisted.

@allengleyzer
Copy link
Contributor Author

@LEEY19 These changes aren't in any release yet, and if you're using the latest master, these changes were recently reverted. I'm not sure what you're RN setup looks like, but that might be the issue?

@allengleyzer
Copy link
Contributor Author

FYI, I figured out a way to reproduce the crash/exception that's in 10423, and was able to confirm that my changes would now cause this same crash to happen more often, since it wouldn't just be form-data bodies that cause it, but all requests that have a request body.

I'm opening a new PR with the changes made here, and an additional commit to fix the crash.

@janicduplessis
Copy link
Contributor

👍 Thanks @allengleyzer for being on top of this.

@fred2028 Could you confirm that the crash reported in #10423 was the one you were seeing?

@LEEY19
Copy link

LEEY19 commented Dec 22, 2017

@allengleyzer I just modified the files NetworkingModule.java and NetworkingModuleTest.java by copy-pasting your code. Since I built my RN from source changes made to native code will be reflected.

      xhr.upload.onprogress = (evt) => {
        console.log('progress: ' + evt.loaded + ' / ' + evt.total);
      }

Nothing appeared in console for the above code on Android. It works as expected in iOS

@allengleyzer
Copy link
Contributor Author

allengleyzer commented Dec 22, 2017

@LEEY19 Sorry about that, the updates don't send if you don't set the Content-Type header on the request. I've updated the test example with setting the Content-Type header.

Another issue I noticed, however, is that the progress updates don't work particularly well for strings/form data, as it usually just sends one progress update at the very end. Unfortunately, this is due in part with the implementation used for sending the updates and/or Okio's implementation for their buffers. There are some possible changes/workarounds, but I think it's out of the scope of this PR. Also, the main use case (IMO) of file updates is working well.

I made some additional changes in the new PR (referenced above), so you should try that one out, and if you have any issues, let's move the discussion to that one instead of here.

facebook-github-bot pushed a commit that referenced this pull request Feb 3, 2018
…h on closed connection

Summary:
This PR includes the same changes made in #16541, for addressing issues #11853/#15724. It adds upload progress updates for uploads with any request body type, and not just form-data.

Additionally, this PR also includes a commit for fixing an `IllegalStateException` when a user's connection gets closed or times out (issues #10423/#11016). Since this exception was occurring within the progress updates logic, it started being thrown more frequently as a result of adding progress updates to all uploads, which was why the original PR was reverted.

To test the upload progress updates, run the following JS to ensure events are now being dispatched:
```
const fileUri = 'file:///my_file.dat';
const url = 'http://my_post_url.com/';
const xhr = new XMLHttpRequest();

xhr.upload.onprogress = (event) => {
    console.log('progress: ' + event.loaded + ' / ' + event.total);
}

xhr.onreadystatechange = () => {if (xhr.readyState === 4) console.log('done');}

console.log('start');

xhr.open('POST', url);

// sending a file (wasn't sending progress)
xhr.setRequestHeader('Content-Type', 'image/jpeg');
xhr.send({ uri: fileUri });

// sending a string (wasn't sending progress)
xhr.setRequestHeader('Content-Type', 'text/plain');
xhr.send("some big string");

// sending form data (was already working)
xhr.setRequestHeader('Content-Type', 'multipart/form-data');
const formData = new FormData(); formData.append('test', 'data');
xhr.send(formData);
```

To test the crash fix:
In the RN Android project, before this change, set a breakpoint at `mRequestBody.writeTo(mBufferedSink);` of `ProgressRequestBody`, and wait a short while for a POST request with a non-null body to time out before resuming the app. Once resumed, if the connection was closed (the `closed` variable will be set to true in `RealBufferedSink`), an `IllegalStateException` will be thrown, which crashes the app. After the changes, an `IOException` will get thrown instead, which is already being properly handled.

As mentioned above, includes the same changes as #16541, with an additional commit.

[ANDROID] [BUGFIX] [XMLHttpRequest] - Added progress updates for all XMLHttpRequest upload types / fix crash on closed connection

Previously, only form-data request bodies emitted upload progress updates. Now, other request body types will also emit updates. Also, Android will no longer crash on certain requests when user has a poor connection.

Addresses issues: 11853/15724/10423/11016
Closes #17312

Differential Revision: D6712377

Pulled By: mdvacca

fbshipit-source-id: bf5adc774703e7e66f7f16707600116f67201425
Plo4ox pushed a commit to Plo4ox/react-native that referenced this pull request Feb 17, 2018
…h on closed connection

Summary:
This PR includes the same changes made in facebook#16541, for addressing issues facebook#11853/facebook#15724. It adds upload progress updates for uploads with any request body type, and not just form-data.

Additionally, this PR also includes a commit for fixing an `IllegalStateException` when a user's connection gets closed or times out (issues facebook#10423/facebook#11016). Since this exception was occurring within the progress updates logic, it started being thrown more frequently as a result of adding progress updates to all uploads, which was why the original PR was reverted.

To test the upload progress updates, run the following JS to ensure events are now being dispatched:
```
const fileUri = 'file:///my_file.dat';
const url = 'http://my_post_url.com/';
const xhr = new XMLHttpRequest();

xhr.upload.onprogress = (event) => {
    console.log('progress: ' + event.loaded + ' / ' + event.total);
}

xhr.onreadystatechange = () => {if (xhr.readyState === 4) console.log('done');}

console.log('start');

xhr.open('POST', url);

// sending a file (wasn't sending progress)
xhr.setRequestHeader('Content-Type', 'image/jpeg');
xhr.send({ uri: fileUri });

// sending a string (wasn't sending progress)
xhr.setRequestHeader('Content-Type', 'text/plain');
xhr.send("some big string");

// sending form data (was already working)
xhr.setRequestHeader('Content-Type', 'multipart/form-data');
const formData = new FormData(); formData.append('test', 'data');
xhr.send(formData);
```

To test the crash fix:
In the RN Android project, before this change, set a breakpoint at `mRequestBody.writeTo(mBufferedSink);` of `ProgressRequestBody`, and wait a short while for a POST request with a non-null body to time out before resuming the app. Once resumed, if the connection was closed (the `closed` variable will be set to true in `RealBufferedSink`), an `IllegalStateException` will be thrown, which crashes the app. After the changes, an `IOException` will get thrown instead, which is already being properly handled.

As mentioned above, includes the same changes as facebook#16541, with an additional commit.

[ANDROID] [BUGFIX] [XMLHttpRequest] - Added progress updates for all XMLHttpRequest upload types / fix crash on closed connection

Previously, only form-data request bodies emitted upload progress updates. Now, other request body types will also emit updates. Also, Android will no longer crash on certain requests when user has a poor connection.

Addresses issues: 11853/15724/10423/11016
Closes facebook#17312

Differential Revision: D6712377

Pulled By: mdvacca

fbshipit-source-id: bf5adc774703e7e66f7f16707600116f67201425
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Import Started This pull request has been imported. This does not imply the PR has been approved.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants