Skip to content

Commit

Permalink
Prevent okhttp from adding ;charset=utf8 to ContentType Header (#23580)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
nhunzaker authored and facebook-github-bot committed Feb 22, 2019
1 parent 665954e commit 4a80776
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ public void onProgress(long bytesWritten, long contentLength, boolean done) {
return;
}
} else {
requestBody = RequestBody.create(contentMediaType, body);
requestBody = RequestBody.create(contentMediaType, body.getBytes(StandardCharsets.UTF_8));
}
} else if (data.hasKey(REQUEST_BODY_KEY_BASE64)) {
if (contentType == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable {
body.putString("string", "This is request body");

mockEvents();

networkingModule.sendRequest(
"POST",
"http://somedomain/bar",
Expand Down Expand Up @@ -309,6 +309,43 @@ public Object answer(InvocationOnMock invocation) throws Throwable {
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);
Expand Down

0 comments on commit 4a80776

Please sign in to comment.