diff --git a/packages/react-native/Libraries/Network/FormData.js b/packages/react-native/Libraries/Network/FormData.js index d1ec8116ad41b9..91735c6b033b19 100644 --- a/packages/react-native/Libraries/Network/FormData.js +++ b/packages/react-native/Libraries/Network/FormData.js @@ -82,7 +82,9 @@ class FormData { // content type (cf. web Blob interface.) if (typeof value === 'object' && !Array.isArray(value) && value) { if (typeof value.name === 'string') { - headers['content-disposition'] += '; filename="' + value.name + '"'; + headers['content-disposition'] += `; filename="${ + value.name + }"; filename*=utf-8''${encodeURI(value.name)}`; } if (typeof value.type === 'string') { headers['content-type'] = value.type; diff --git a/packages/react-native/Libraries/Network/__tests__/FormData-test.js b/packages/react-native/Libraries/Network/__tests__/FormData-test.js index b708a04263d083..ee741afb7c983b 100644 --- a/packages/react-native/Libraries/Network/__tests__/FormData-test.js +++ b/packages/react-native/Libraries/Network/__tests__/FormData-test.js @@ -48,7 +48,29 @@ describe('FormData', function () { type: 'image/jpeg', name: 'photo.jpg', headers: { - 'content-disposition': 'form-data; name="photo"; filename="photo.jpg"', + 'content-disposition': + 'form-data; name="photo"; filename="photo.jpg"; filename*=utf-8\'\'photo.jpg', + 'content-type': 'image/jpeg', + }, + fieldName: 'photo', + }; + expect(formData.getParts()[0]).toMatchObject(expectedPart); + }); + + it('should return blob with the correct utf-8 handling', function () { + formData.append('photo', { + uri: 'arbitrary/path', + type: 'image/jpeg', + name: '测试photo.jpg', + }); + + const expectedPart = { + uri: 'arbitrary/path', + type: 'image/jpeg', + name: '测试photo.jpg', + headers: { + 'content-disposition': + 'form-data; name="photo"; filename="测试photo.jpg"; filename*=utf-8\'\'%E6%B5%8B%E8%AF%95photo.jpg', 'content-type': 'image/jpeg', }, fieldName: 'photo', diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/HeaderUtil.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/HeaderUtil.java index 07485deffca4c6..99ce6b0fa96ef8 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/HeaderUtil.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/HeaderUtil.java @@ -28,18 +28,4 @@ public static String stripHeaderName(String name) { } return modified ? builder.toString() : name; } - - public static String stripHeaderValue(String value) { - StringBuilder builder = new StringBuilder(value.length()); - boolean modified = false; - for (int i = 0, length = value.length(); i < length; i++) { - char c = value.charAt(i); - if ((c > '\u001f' && c < '\u007f') || c == '\t') { - builder.append(c); - } else { - modified = true; - } - } - return modified ? builder.toString() : value; - } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java index 4b23bdde76c32a..ca37fb2ed6524c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java @@ -761,11 +761,11 @@ public void removeListeners(double count) {} return null; } String headerName = HeaderUtil.stripHeaderName(header.getString(0)); - String headerValue = HeaderUtil.stripHeaderValue(header.getString(1)); + String headerValue = header.getString(1); if (headerName == null || headerValue == null) { return null; } - headersBuilder.add(headerName, headerValue); + headersBuilder.addUnsafeNonAscii(headerName, headerValue); } if (headersBuilder.get(USER_AGENT_HEADER_NAME) == null && mDefaultUserAgent != null) { headersBuilder.add(USER_AGENT_HEADER_NAME, mDefaultUserAgent); diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/network/HeaderUtilTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/network/HeaderUtilTest.kt index 7696779d7d66c9..f844afaa19e381 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/network/HeaderUtilTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/network/HeaderUtilTest.kt @@ -27,46 +27,21 @@ class HeaderUtilTest { assertEquals(ALPHABET_TEST, HeaderUtil.stripHeaderName(ALPHABET_TEST)) } - @Test - fun valueStripKeepsLetters() { - assertEquals(ALPHABET_TEST, HeaderUtil.stripHeaderValue(ALPHABET_TEST)) - } - @Test fun nameStripKeepsNumbers() { assertEquals(NUMBERS_TEST, HeaderUtil.stripHeaderName(NUMBERS_TEST)) } - @Test - fun valueStripKeepsNumbers() { - assertEquals(NUMBERS_TEST, HeaderUtil.stripHeaderValue(NUMBERS_TEST)) - } - - @Test - fun valueStripKeepsSpecials() { - assertEquals(SPECIALS_TEST, HeaderUtil.stripHeaderValue(SPECIALS_TEST)) - } - @Test fun nameStripKeepsSpecials() { assertEquals(SPECIALS_TEST, HeaderUtil.stripHeaderName(SPECIALS_TEST)) } - @Test - fun valueStripKeepsTabs() { - assertEquals(TABULATION_TEST, HeaderUtil.stripHeaderValue(TABULATION_TEST)) - } - @Test fun nameStripDeletesTabs() { assertEquals(TABULATION_STRIP_EXPECTED, HeaderUtil.stripHeaderName(TABULATION_TEST)) } - @Test - fun valueStripRemovesExtraSymbols() { - assertEquals(BANNED_TEST_EXPECTED, HeaderUtil.stripHeaderValue(VALUE_BANNED_SYMBOLS_TEST)) - } - @Test fun nameStripRemovesExtraSymbols() { assertEquals(BANNED_TEST_EXPECTED, HeaderUtil.stripHeaderName(NAME_BANNED_SYMBOLS_TEST)) diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/network/NetworkingModuleTest.java b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/network/NetworkingModuleTest.java index 57105e3e32687d..46894323847521 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/network/NetworkingModuleTest.java +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/network/NetworkingModuleTest.java @@ -482,7 +482,9 @@ public Object answer(InvocationOnMock invocation) throws Throwable { JavaOnlyArray.from( Arrays.asList( JavaOnlyArray.of("content-type", "image/jpg"), - JavaOnlyArray.of("content-disposition", "filename=photo.jpg")))); + JavaOnlyArray.of( + "content-disposition", + "filename=\"测试photo.jpg\"; filename*=utf-8''%E6%B5%8B%E8%AF%95photo.jpg")))); formData.pushMap(imageBodyPart); mNetworkingModule.sendRequest( @@ -521,7 +523,8 @@ public Object answer(InvocationOnMock invocation) throws Throwable { assertThat(bodyHeaders.get(0).get("content-disposition")).isEqualTo("user"); assertThat(bodyRequestBody.get(0).contentType()).isNull(); assertThat(bodyRequestBody.get(0).contentLength()).isEqualTo("locale".getBytes().length); - assertThat(bodyHeaders.get(1).get("content-disposition")).isEqualTo("filename=photo.jpg"); + assertThat(bodyHeaders.get(1).get("content-disposition")) + .isEqualTo("filename=\"测试photo.jpg\"; filename*=utf-8''%E6%B5%8B%E8%AF%95photo.jpg"); assertThat(bodyRequestBody.get(1).contentType()).isEqualTo(MediaType.parse("image/jpg")); assertThat(bodyRequestBody.get(1).contentLength()).isEqualTo("imageUri".getBytes().length); }