diff --git a/javagen/src/main/resources/MultipartFormDataHelper.java b/javagen/src/main/resources/MultipartFormDataHelper.java index 60bcf5cfef..24b8dfbb2a 100644 --- a/javagen/src/main/resources/MultipartFormDataHelper.java +++ b/javagen/src/main/resources/MultipartFormDataHelper.java @@ -3,7 +3,6 @@ import com.azure.core.util.BinaryData; import com.azure.core.util.CoreUtils; -import java.text.Normalizer; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.SequenceInputStream; @@ -11,7 +10,6 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.UUID; -import java.util.regex.Pattern; // DO NOT modify this helper class @@ -85,8 +83,8 @@ public BinaryData getRequestBody() { public MultipartFormDataHelper serializeTextField(String fieldName, String value) { if (value != null) { String serialized = partSeparator - + CRLF + "Content-Disposition: form-data; name=\"" - + fieldName + "\"" + CRLF + CRLF + + CRLF + "Content-Disposition: form-data; name=\"" + escapeName(fieldName) + "\"" + + CRLF + CRLF + value + CRLF; byte[] data = serialized.getBytes(encoderCharset); @@ -105,7 +103,8 @@ public MultipartFormDataHelper serializeTextField(String fieldName, String value */ public MultipartFormDataHelper serializeJsonField(String fieldName, Object jsonObject) { if (jsonObject != null) { - String serialized = partSeparator + CRLF + "Content-Disposition: form-data; name=\"" + fieldName + "\"" + String serialized = partSeparator + CRLF + + "Content-Disposition: form-data; name=\"" + escapeName(fieldName) + "\"" + CRLF + "Content-Type: application/json" + CRLF + CRLF + BinaryData.fromObject(jsonObject) + CRLF; byte[] data = serialized.getBytes(encoderCharset); @@ -132,11 +131,6 @@ public MultipartFormDataHelper serializeFileField( if (CoreUtils.isNullOrEmpty(contentType)) { contentType = APPLICATION_OCTET_STREAM; } - if (CoreUtils.isNullOrEmpty(filename)) { - filename = fieldName; - } - filename = normalizeAscii(filename); - writeFileField(fieldName, file, contentType, filename); } return this; @@ -164,11 +158,6 @@ public MultipartFormDataHelper serializeFileFields( contentType = APPLICATION_OCTET_STREAM; } String filename = filenames.get(i); - if (CoreUtils.isNullOrEmpty(filename)) { - filename = fieldName + String.valueOf(i + 1); - } - filename = normalizeAscii(filename); - writeFileField(fieldName, file, contentType, filename); } } @@ -194,10 +183,14 @@ public MultipartFormDataHelper end() { } private void writeFileField(String fieldName, BinaryData file, String contentType, String filename) { + String contentDispositionFilename = ""; + if (!CoreUtils.isNullOrEmpty(filename)) { + contentDispositionFilename = "; filename=\"" + escapeName(filename) + "\""; + } + // Multipart preamble String fileFieldPreamble = partSeparator - + CRLF + "Content-Disposition: form-data; name=\"" + fieldName - + "\"; filename=\"" + filename + "\"" + + CRLF + "Content-Disposition: form-data; name=\"" + escapeName(fieldName) + "\"" + contentDispositionFilename + CRLF + "Content-Type: " + contentType + CRLF + CRLF; byte[] data = fileFieldPreamble.getBytes(encoderCharset); appendBytes(data); @@ -216,9 +209,7 @@ private void appendBytes(byte[] bytes) { requestDataStream = new SequenceInputStream(requestDataStream, new ByteArrayInputStream(bytes)); } - private static final Pattern REDACT_FILENAME = Pattern.compile("[^\\x20-\\x7E]|\""); - - private static String normalizeAscii(String text) { - return REDACT_FILENAME.matcher(Normalizer.normalize(text, Normalizer.Form.NFD)).replaceAll(""); + private static String escapeName(String name) { + return name.replace("\n", "%0A").replace("\r", "%0D").replace("\"", "%22"); } } diff --git a/typespec-extension/changelog.md b/typespec-extension/changelog.md index 6f21653ed4..4ace627f89 100644 --- a/typespec-extension/changelog.md +++ b/typespec-extension/changelog.md @@ -1,6 +1,12 @@ # Release History -## 0.13.3 (2024-02-07) +## 0.13.5 (2024-02-09) + +Compatible with compiler 0.53. + +- Behavior changed on "multipart/form-data" request. If `filename` is not provided, implementation will no longer provide a default filename to `Content-Disposition` line. + +## 0.13.4 (2024-02-07) Compatible with compiler 0.53. diff --git a/typespec-extension/package-lock.json b/typespec-extension/package-lock.json index 1c3b4a9e19..8c56ad5c69 100644 --- a/typespec-extension/package-lock.json +++ b/typespec-extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "@azure-tools/typespec-java", - "version": "0.13.4", + "version": "0.13.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@azure-tools/typespec-java", - "version": "0.13.4", + "version": "0.13.5", "license": "MIT", "dependencies": { "@autorest/codemodel": "~4.20.0", diff --git a/typespec-extension/package.json b/typespec-extension/package.json index a38c2d6530..696186f11a 100644 --- a/typespec-extension/package.json +++ b/typespec-extension/package.json @@ -1,6 +1,6 @@ { "name": "@azure-tools/typespec-java", - "version": "0.13.4", + "version": "0.13.5", "description": "TypeSpec library for emitting Java client from the TypeSpec REST protocol binding", "keywords": [ "TypeSpec" diff --git a/typespec-tests/package.json b/typespec-tests/package.json index b6eaaa0170..0bb766bae1 100644 --- a/typespec-tests/package.json +++ b/typespec-tests/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@azure-tools/cadl-ranch-specs": "0.29.0", - "@azure-tools/typespec-java": "file:/../typespec-extension/azure-tools-typespec-java-0.13.4.tgz" + "@azure-tools/typespec-java": "file:/../typespec-extension/azure-tools-typespec-java-0.13.5.tgz" }, "devDependencies": { "@typespec/prettier-plugin-typespec": "~0.53.0", diff --git a/typespec-tests/src/main/java/com/cadl/multipart/implementation/MultipartFormDataHelper.java b/typespec-tests/src/main/java/com/cadl/multipart/implementation/MultipartFormDataHelper.java index dadebf3ef9..76a5c67f57 100644 --- a/typespec-tests/src/main/java/com/cadl/multipart/implementation/MultipartFormDataHelper.java +++ b/typespec-tests/src/main/java/com/cadl/multipart/implementation/MultipartFormDataHelper.java @@ -13,10 +13,8 @@ import java.io.SequenceInputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.text.Normalizer; import java.util.List; import java.util.UUID; -import java.util.regex.Pattern; // DO NOT modify this helper class @@ -89,8 +87,8 @@ public BinaryData getRequestBody() { */ public MultipartFormDataHelper serializeTextField(String fieldName, String value) { if (value != null) { - String serialized = partSeparator + CRLF + "Content-Disposition: form-data; name=\"" + fieldName + "\"" - + CRLF + CRLF + value + CRLF; + String serialized = partSeparator + CRLF + "Content-Disposition: form-data; name=\"" + escapeName(fieldName) + + "\"" + CRLF + CRLF + value + CRLF; byte[] data = serialized.getBytes(encoderCharset); appendBytes(data); } @@ -107,8 +105,9 @@ public MultipartFormDataHelper serializeTextField(String fieldName, String value */ public MultipartFormDataHelper serializeJsonField(String fieldName, Object jsonObject) { if (jsonObject != null) { - String serialized = partSeparator + CRLF + "Content-Disposition: form-data; name=\"" + fieldName + "\"" - + CRLF + "Content-Type: application/json" + CRLF + CRLF + BinaryData.fromObject(jsonObject) + CRLF; + String serialized + = partSeparator + CRLF + "Content-Disposition: form-data; name=\"" + escapeName(fieldName) + "\"" + CRLF + + "Content-Type: application/json" + CRLF + CRLF + BinaryData.fromObject(jsonObject) + CRLF; byte[] data = serialized.getBytes(encoderCharset); appendBytes(data); } @@ -130,11 +129,6 @@ public MultipartFormDataHelper serializeFileField(String fieldName, BinaryData f if (CoreUtils.isNullOrEmpty(contentType)) { contentType = APPLICATION_OCTET_STREAM; } - if (CoreUtils.isNullOrEmpty(filename)) { - filename = fieldName; - } - filename = normalizeAscii(filename); - writeFileField(fieldName, file, contentType, filename); } return this; @@ -159,11 +153,6 @@ public MultipartFormDataHelper serializeFileFields(String fieldName, List