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

Add responseType as a concept to RCTNetworking, send binary data as base64 #8324

Closed
wants to merge 1 commit into from

Conversation

philikon
Copy link
Contributor

@philikon philikon commented Jun 22, 2016

In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A xhr.responseType of '' or 'text' translates to a native response type of 'text'. A xhr.responseType of arraybuffer translates to a native response type of base64, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS string back to a TypedArray, which is what's currently going on.

Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the 'progress' event is designed for, so this change also implements that. This change also follows the XHR spec with regards to xhr.response and xhr.responseText:

  • if the response type is 'text', xhr.responseText can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an onreadystatechange or onprogress handler on the XHR.
  • if the response type is not 'text', xhr.responseText can't be accessed and xhr.response remains null until the response is fully received. 'progress' events containing response details (total bytes, downloaded so far) are dispatched if there's an onprogress handler.

Once Blobs are landed, xhr.responseType of 'blob' will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a Blob object from that for consumers.

Test Plan: This change comes with extensive changes and expansions to the XHRExamples in UIExplorer. onprogress handler, onreadystatechange handler, and responseType = 'arraybuffer' can be switched on independently. I tested all combinations on both iOS and Android.

@ghost
Copy link

ghost commented Jun 22, 2016

By analyzing the blame information on this pull request, we identified @davidaurelio and @nicklockwood to be potential reviewers.

@ghost ghost added GH Review: review-needed CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. labels Jun 22, 2016
@davidaurelio
Copy link
Contributor

@philikon amazing work

cc @javache

(args) => this.__didReceiveData(...args)
(args) => this.__didReceiveData(...args)
));
this._subscriptions.push(RCTNetworking.addListener(
Copy link
Member

Choose a reason for hiding this comment

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

I'm not super familiar with all of this code: does this mean we always send all the incremental data chunks and the complete response when the request finishes? That seems wasteful.

Copy link
Contributor Author

@philikon philikon Jun 23, 2016

Choose a reason for hiding this comment

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

A valid question! But rest assured, we definitely don't send data twice to JS.

@davidaurelio
Copy link
Contributor

Edge case question: what happens if the requestType property is changed in-flight? That is legal until the request headers are received.

@philikon
Copy link
Contributor Author

Edge case question: what happens if the requestType property is changed in-flight? That is legal until the request headers are received.

Ah, good point. I failed to address that, both in code and the PR comments. I know the spec says that it's legal until request headers are received, but how useful is that really? I think we should make it illegal to change after the request has been sent -- this PR here would fail horribly otherwise anyway. Supporting the narrow gap between request sent and headers received seems very little benefit for a whole lot of code complexity...

@davidaurelio
Copy link
Contributor

Yeah, I agree that it’s not worth implementing this. Just throw, or print a warning, that should be enough

@philikon
Copy link
Contributor Author

Yeah, I agree that it’s not worth implementing this. Just throw, or print a warning, that should be enough

Done.

Who else should we get to review this so we can get it landed?

@philikon
Copy link
Contributor Author

Ping?

@mkonicek
Copy link
Contributor

@davidaurelio By assigning the PR it will show up in Pieter's diff queue - more visibility for this PR from our core contributor Philipp.

responseType: 'text' | 'base64',
incrementalUpdates: boolean,
timeout: number,
callback: (requestId: number) => any
Copy link
Member

Choose a reason for hiding this comment

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

m1nep7h

@javache
Copy link
Member

javache commented Jun 28, 2016

Objective-C and JS changes LGTM, maybe @andreicoman11 or @foghina can help out with the native Android changes.

@davidaurelio
Copy link
Contributor

lgtm, let’s wait for @foghina or @andreicoman11

@philikon
Copy link
Contributor Author

Thanks for the review, @javache! Addressed your comments.

public Response intercept(Interceptor.Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
ProgressResponseBody responseBody = new ProgressResponseBody(
originalResponse.body(), new ProgressListener() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Formatting around here seems wrong. Should either be:

... = new ProgressResponseBody(..., ... {
  long last = ...

if it doesn't exceed 100 chars width, or

... = new ProgressResponseBody(
  ...,
  ... {
    long last = ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I can fix that.

@foghina
Copy link
Contributor

foghina commented Jun 30, 2016

My comments are mostly questions because apparently our networking module is now a commentless monstrosity and I don't get how things work. Code looks good though. Please make sure to test uploading with progress as well (I think that was supported?).

@philikon
Copy link
Contributor Author

My comments are mostly questions because apparently our networking module is now a commentless monstrosity and I don't get how things work.

:(

I can try and help document things some time.

Please make sure to test uploading with progress as well (I think that was supported?).

Yep. Still works. UIExplorer examples FTW :)

…ase64

In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on.

Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`:

- if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR.

- if the response type is not `'text'`, `xhr.responseText` can't be access and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler.

Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers.

**Test Plan:** This change comes with extensive changes and expansions to the XHRExamples in UIExplorer. `onprogress` handler, `onreadystatechange` handler, and `responseType = 'arraybuffer'` can be switched on independently. I tested all combinations on both iOS and Android.
@philikon
Copy link
Contributor Author

Thanks for the reviews and questions, everyone. Really appreciate it. If there are no more concerns or questions, would love it if somebody could pull the trigger and import it.

@javache
Copy link
Member

javache commented Jul 1, 2016

@facebook-github-bot import

@ghost
Copy link

ghost commented Jul 1, 2016

Thanks for importing. If you are an FB employee go to Phabricator to review.

private BufferedSource mBufferedSource;
private long mTotalBytesRead;

public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {
Copy link
Member

Choose a reason for hiding this comment

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

I get an Infer warning here:

There may be a Field Not Initialized: Field ProgressResponseBody.mBufferedSource is not initialized in the constructor and is not declared @Nullable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yeah, guess we should mark it @Nullable.

I can amend the commit here on github, but it seems @davidaurelio is already amending the diff in FB land. If David doesn't add it while he's in there, I can send a trivial PR after this lands.

@davidaurelio
Copy link
Contributor

@philikon I’m on the internal failures. Making progress.

@philikon
Copy link
Contributor Author

philikon commented Jul 1, 2016

@philikon I’m on the internal failures. Making progress.

Eeek ok. Thanks! You know how to reach me if I can help with anything :)

@philikon
Copy link
Contributor Author

philikon commented Jul 5, 2016

@davidaurelio any updates? anything i can help with?

@davidaurelio
Copy link
Contributor

I’ve added a comment here: #8324 (comment)

hard to spot, as it didn’t show up at the bottom. Any idea what could cause that?

@davidaurelio
Copy link
Contributor

@philikon any idea what the underlying issue might be? @foghina can you take a look? --> #8324 (comment)

@philikon
Copy link
Contributor Author

philikon commented Jul 8, 2016

Thanks @davidaurelio! I replied to the comment.

@ghost
Copy link

ghost commented Jul 12, 2016

Thank you for your pull request. As you may know, we require contributors to sign our Contributor License Agreement, and we don't seem to have you on file and listed as active anymore. In order for us to review and merge your code, please email cla@fb.com with your details so we can update your status.

@ghost ghost closed this in 08c375f Jul 13, 2016
@philikon philikon deleted the xhr_responseType branch July 14, 2016 02:28
rozele pushed a commit to microsoft/react-native-windows that referenced this pull request Jul 26, 2016
…ase64

Summary:
In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on.

Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`:

- if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR.

- if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler.

Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers.

Closes facebook/react-native#8324

Reviewed By: javache

Differential Revision: D3508822

Pulled By: davidaurelio

fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
samerce pushed a commit to iodine/react-native that referenced this pull request Aug 23, 2016
…ase64

Summary:
In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on.

Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`:

- if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR.

- if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler.

Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers.

Closes facebook#8324

Reviewed By: javache

Differential Revision: D3508822

Pulled By: davidaurelio

fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
mpretty-cyro pushed a commit to HomePass/react-native that referenced this pull request Aug 25, 2016
…ase64

Summary:
In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on.

Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`:

- if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR.

- if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler.

Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers.

Closes facebook#8324

Reviewed By: javache

Differential Revision: D3508822

Pulled By: davidaurelio

fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
@philikon philikon mentioned this pull request Nov 24, 2016
7 tasks
tungdo194 pushed a commit to tungdo194/rn-test that referenced this pull request Apr 28, 2024
…ase64

Summary:
In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on.

Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`:

- if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR.

- if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler.

Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers.

Closes facebook/react-native#8324

Reviewed By: javache

Differential Revision: D3508822

Pulled By: davidaurelio

fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
This pull request was closed.
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.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants