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

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:

https://github.com/square/okhttp/issues/2099#issuecomment-366757161

Related issues:
https://github.com/facebook/react-native/issues/8237

[Android][fixed] - Prevent fetch() POST requests on Android from appending `charset=utf-8` to `Content-Type` header.
Pull Request resolved: https://github.com/facebook/react-native/pull/23580

Differential Revision: D14180849

Pulled By: cpojer

fbshipit-source-id: b84cadf83361331a9f64d1ff5f2e6399a55527a6
This commit is contained in:
Nate Hunzaker
2019-02-21 22:36:06 -08:00
committed by Facebook Github Bot
parent 665954efcc
commit 4a807761a4
2 changed files with 39 additions and 2 deletions
@@ -247,7 +247,7 @@ public class NetworkingModuleTest {
body.putString("string", "This is request body");
mockEvents();
networkingModule.sendRequest(
"POST",
"http://somedomain/bar",
@@ -309,6 +309,43 @@ public class NetworkingModuleTest {
assertThat(requestHeaders.get("User-Agent")).isEqualTo("React test agent/1.0");
}
@Test
public void testPostJsonContentTypeHeader() throws Exception {
OkHttpClient httpClient = mock(OkHttpClient.class);
when(httpClient.newCall(any(Request.class))).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Call callMock = mock(Call.class);
return callMock;
}
});
OkHttpClient.Builder clientBuilder = mock(OkHttpClient.Builder.class);
when(clientBuilder.build()).thenReturn(httpClient);
when(httpClient.newBuilder()).thenReturn(clientBuilder);
NetworkingModule networkingModule =
new NetworkingModule(mock(ReactApplicationContext.class), "", httpClient);
JavaOnlyMap body = new JavaOnlyMap();
body.putString("string", "{ \"key\": \"value\" }");
networkingModule.sendRequest(
"POST",
"http://somedomain/bar",
0,
JavaOnlyArray.of(JavaOnlyArray.of("Content-Type", "application/json")),
body,
/* responseType */ "text",
/* useIncrementalUpdates*/ true,
/* timeout */ 0,
/* withCredentials */ false);
ArgumentCaptor<Request> argumentCaptor = ArgumentCaptor.forClass(Request.class);
verify(httpClient).newCall(argumentCaptor.capture());
// Verify okhttp does not append "charset=utf-8"
assertThat(argumentCaptor.getValue().body().contentType().toString()).isEqualTo("application/json");
}
@Test
public void testMultipartPostRequestSimple() throws Exception {
PowerMockito.mockStatic(RequestBodyUtil.class);