Skip to content

Commit 0d5aebb

Browse files
nhunzakerfacebook-github-bot
authored andcommittedFeb 23, 2019
Use existing character set in POST body when possible (#23603)
Summary: This commit fixes a bug introduced in a previous attempt (#23580) to address an issue where okhttp appended `charset=utf-8` to the Content-Type header when otherwise not specified. In that commit, I converted all characters to UTF-8, however it should instead use an existing encoding when possible. Related issues: #8237 (comment) [Android][fixed] - Respect existing character set when specified in fetch() POST request Pull Request resolved: #23603 Differential Revision: D14191750 Pulled By: hramos fbshipit-source-id: 11c1bfd98ccd33cd8e54ea426285b7d2ce9c2d7c
1 parent c1349b5 commit 0d5aebb

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed
 

‎ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,13 @@ public void onProgress(long bytesWritten, long contentLength, boolean done) {
372372
return;
373373
}
374374
} else {
375-
requestBody = RequestBody.create(contentMediaType, body.getBytes(StandardCharsets.UTF_8));
375+
// Use getBytes() to convert the body into a byte[], preventing okhttp from
376+
// appending the character set to the Content-Type header when otherwise unspecified
377+
// https://github.com/facebook/react-native/issues/8237
378+
Charset charset = contentMediaType == null
379+
? StandardCharsets.UTF_8
380+
: contentMediaType.charset(StandardCharsets.UTF_8);
381+
requestBody = RequestBody.create(contentMediaType, body.getBytes(charset));
376382
}
377383
} else if (data.hasKey(REQUEST_BODY_KEY_BASE64)) {
378384
if (contentType == null) {

‎ReactAndroid/src/test/java/com/facebook/react/modules/network/NetworkingModuleTest.java

+89
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.facebook.react.bridge.ReactContext;
1919
import com.facebook.react.bridge.WritableArray;
2020
import com.facebook.react.bridge.WritableMap;
21+
import com.facebook.react.common.StandardCharsets;
2122
import com.facebook.react.common.network.OkHttpCallUtil;
2223
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
2324

@@ -346,6 +347,94 @@ public Object answer(InvocationOnMock invocation) throws Throwable {
346347
assertThat(argumentCaptor.getValue().body().contentType().toString()).isEqualTo("application/json");
347348
}
348349

350+
@Test
351+
public void testRespectsExistingCharacterSet() throws Exception {
352+
RCTDeviceEventEmitter emitter = mock(RCTDeviceEventEmitter.class);
353+
ReactApplicationContext context = mock(ReactApplicationContext.class);
354+
when(context.getJSModule(any(Class.class))).thenReturn(emitter);
355+
356+
OkHttpClient httpClient = mock(OkHttpClient.class);
357+
when(httpClient.newCall(any(Request.class))).thenAnswer(new Answer<Object>() {
358+
@Override
359+
public Object answer(InvocationOnMock invocation) throws Throwable {
360+
Call callMock = mock(Call.class);
361+
return callMock;
362+
}
363+
});
364+
OkHttpClient.Builder clientBuilder = mock(OkHttpClient.Builder.class);
365+
when(clientBuilder.build()).thenReturn(httpClient);
366+
when(httpClient.newBuilder()).thenReturn(clientBuilder);
367+
NetworkingModule networkingModule = new NetworkingModule(context, "", httpClient);
368+
369+
JavaOnlyMap body = new JavaOnlyMap();
370+
body.putString("string", "Friðjónsson");
371+
372+
mockEvents();
373+
374+
networkingModule.sendRequest(
375+
"POST",
376+
"http://somedomain/bar",
377+
0,
378+
JavaOnlyArray.of(JavaOnlyArray.of("Content-Type", "text/plain; charset=utf-16")),
379+
body,
380+
/* responseType */ "text",
381+
/* useIncrementalUpdates*/ true,
382+
/* timeout */ 0,
383+
/* withCredentials */ false);
384+
385+
ArgumentCaptor<Request> argumentCaptor = ArgumentCaptor.forClass(Request.class);
386+
verify(httpClient).newCall(argumentCaptor.capture());
387+
388+
Buffer contentBuffer = new Buffer();
389+
argumentCaptor.getValue().body().writeTo(contentBuffer);
390+
assertThat(contentBuffer.readString(StandardCharsets.UTF_16)).isEqualTo("Friðjónsson");
391+
}
392+
393+
@Test
394+
public void testGracefullyRecoversFromInvalidContentType() throws Exception {
395+
RCTDeviceEventEmitter emitter = mock(RCTDeviceEventEmitter.class);
396+
ReactApplicationContext context = mock(ReactApplicationContext.class);
397+
when(context.getJSModule(any(Class.class))).thenReturn(emitter);
398+
399+
OkHttpClient httpClient = mock(OkHttpClient.class);
400+
when(httpClient.newCall(any(Request.class))).thenAnswer(new Answer<Object>() {
401+
@Override
402+
public Object answer(InvocationOnMock invocation) throws Throwable {
403+
Call callMock = mock(Call.class);
404+
return callMock;
405+
}
406+
});
407+
OkHttpClient.Builder clientBuilder = mock(OkHttpClient.Builder.class);
408+
when(clientBuilder.build()).thenReturn(httpClient);
409+
when(httpClient.newBuilder()).thenReturn(clientBuilder);
410+
NetworkingModule networkingModule = new NetworkingModule(context, "", httpClient);
411+
412+
JavaOnlyMap body = new JavaOnlyMap();
413+
body.putString("string", "test");
414+
415+
mockEvents();
416+
417+
networkingModule.sendRequest(
418+
"POST",
419+
"http://somedomain/bar",
420+
0,
421+
JavaOnlyArray.of(JavaOnlyArray.of("Content-Type", "invalid")),
422+
body,
423+
/* responseType */ "text",
424+
/* useIncrementalUpdates*/ true,
425+
/* timeout */ 0,
426+
/* withCredentials */ false);
427+
428+
ArgumentCaptor<Request> argumentCaptor = ArgumentCaptor.forClass(Request.class);
429+
verify(httpClient).newCall(argumentCaptor.capture());
430+
431+
Buffer contentBuffer = new Buffer();
432+
argumentCaptor.getValue().body().writeTo(contentBuffer);
433+
434+
assertThat(contentBuffer.readString(StandardCharsets.UTF_8)).isEqualTo("test");
435+
assertThat(argumentCaptor.getValue().header("Content-Type")).isEqualTo("invalid");
436+
}
437+
349438
@Test
350439
public void testMultipartPostRequestSimple() throws Exception {
351440
PowerMockito.mockStatic(RequestBodyUtil.class);

0 commit comments

Comments
 (0)
Please sign in to comment.