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

Prevent okhttp from adding ;charset=utf8 to ContentType Header #23580

Closed
wants to merge 4 commits into from

Conversation

nhunzaker
Copy link
Contributor

@nhunzaker nhunzaker commented Feb 21, 2019

Summary

Before this commit, fetch() calls append "charset=utf8" to the Content-Type header on Android (and not on iOS). This is because of an implementation detail in the okhttp library. This means that you can make a call on the JavaScript side like:

let body = JSON.stringify({ key: "value" });

let headers = {
  "Content-Type": "application/json"
};

fetch("http://10.0.2.2:3000", { method: "POST", body, headers });

However the resulting request appends the utf8 character:

POST - 13:34:32:
  content-type: application/json; charset=utf-8
  content-length: 15
  host: 10.0.2.2:3000
  connection: Keep-Alive
  accept-encoding: gzip
  user-agent: okhttp/3.12.1

Passing byte array into the RequestBody avoids this, as recommended by a maintainer of okhttp:

square/okhttp#2099 (comment)

Related issues:
#8237

Changelog

[Android][fixed] - Prevent fetch() POST requests on Android from appending charset=utf-8 to Content-Type header.

Test Plan

I setup a test project here: https://github.com/nhunzaker/rn-charset-issue. It includes a simple node server that logs header output from requests. With this change, the server no longer reports the addition of charset=utf-8 in the Content-Type header.

Before this commit, fetch() calls include "charset=utf8" on
Android. This is because of an implementation detail in the okhttp
library.

Passing byte array into the RequestBody avoids this, as recommended by
a maintainer of okhttp:

square/okhttp#2099 (comment)

Related issues:
facebook#8237
@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 Feb 21, 2019
@@ -309,6 +309,55 @@ public Object answer(InvocationOnMock invocation) throws Throwable {
assertThat(requestHeaders.get("User-Agent")).isEqualTo("React test agent/1.0");
}

@Test
public void testPostJsonContentTypeHeader() throws Exception {
Copy link
Contributor Author

@nhunzaker nhunzaker Feb 21, 2019

Choose a reason for hiding this comment

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

I copied this from previous tests. It's possible more of the mocks below can be removed. Any thoughts?

@react-native-bot react-native-bot added the 🌐Networking Related to a networking API. label Feb 21, 2019
@hey99xx
Copy link

hey99xx commented Feb 21, 2019

This is actually a pretty simple fix for a long going problem for many 👍
Can we still specify utf8 as the charset for the bytes though?

import com.facebook.react.common.StandardCharsets;

body.getBytes(StandardCharsets.UTF_8)

For unit tests, you're heavily mocking OkHttp. Was the test actually failing as intended when you undo your fix?

@nhunzaker
Copy link
Contributor Author

nhunzaker commented Feb 21, 2019

@hey99xx good deal, 👍. I wondered about that, or passing in the character set as defined by MediaType.parse.

The new test fails without this change. I get the following failure:

org.junit.ComparisonFailure: expected:<null> but was:<UTF-8>
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)

I'd love to actually assert the headers that OkHTTP is building. This was the closest I could manage to get.

@cpojer
Copy link
Contributor

cpojer commented Feb 22, 2019

@nhunzaker I'm not sure I understand your last comment. Can you confirm that @hey99xx's concern is addressed?

I was able to drop mocks for events and I limited the test assertions
strictly to testing the content type.
@nhunzaker
Copy link
Contributor Author

Regarding:

For unit tests, you're heavily mocking OkHttp. Was the test actually failing as intended when you undo your fix?

Yes, I believe I have. While the test heavily mocks OkHttp, the important part to test is the stringification of the MediaType class, which is not mocked.

I sent out another commit that I hope simplifies the test and makes that clearer (https://github.com/facebook/react-native/pull/23580/files#diff-0a0a7fd9969f1f5f68d80bf421fcc092R346).

Without my change, this fails, returning "application/json; charset=utf-8"

@cpojer
Copy link
Contributor

cpojer commented Feb 22, 2019

@nhunzaker I mean this part: "Can we still specify utf8 as the charset for the bytes though?" – I assume the answer is yes, just wanna hear the confirmation.

@nhunzaker
Copy link
Contributor Author

Copy link
Contributor

@cpojer cpojer left a comment

Choose a reason for hiding this comment

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

Awesome, thanks for clarifying. Let's ship it.

@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 Feb 22, 2019
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.

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

Copy link

@hey99xx hey99xx left a comment

Choose a reason for hiding this comment

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

Not sure if my approval matters after @cpojer 's, but here you go anyways

@cpojer
Copy link
Contributor

cpojer commented Feb 22, 2019

Yes it definitely does matter ☺️

@react-native-bot
Copy link
Collaborator

@nhunzaker merged commit 4a80776 into facebook:master.

@facebook facebook locked as resolved and limited conversation to collaborators Feb 22, 2019
@react-native-bot react-native-bot added the Merged This PR has been merged. label Feb 22, 2019
@hramos hramos removed Import Started This pull request has been imported. This does not imply the PR has been approved. labels Feb 23, 2019
grabbou pushed a commit that referenced this pull request Feb 27, 2019
Summary:
Before this commit, `fetch()` calls append `"charset=utf8"` to the `Content-Type` header on Android (and not on iOS). This is because of an implementation detail in the okhttp library. This means that you can make a call on the JavaScript side like:

```javascript
let body = JSON.stringify({ key: "value" });

let headers = {
  "Content-Type": "application/json"
};

fetch("http://10.0.2.2:3000", { method: "POST", body, headers });
```

However the resulting request appends the utf8 character:

```
POST - 13:34:32:
  content-type: application/json; charset=utf-8
  content-length: 15
  host: 10.0.2.2:3000
  connection: Keep-Alive
  accept-encoding: gzip
  user-agent: okhttp/3.12.1
```

Passing byte array into the RequestBody avoids this, as recommended by a maintainer of okhttp:

square/okhttp#2099 (comment)

Related issues:
#8237

[Android][fixed] - Prevent fetch() POST requests on Android from appending `charset=utf-8` to `Content-Type` header.
Pull Request resolved: #23580

Differential Revision: D14180849

Pulled By: cpojer

fbshipit-source-id: b84cadf83361331a9f64d1ff5f2e6399a55527a6
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. 🌐Networking Related to a networking API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants