You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2022/11/13 11:30:43 UTC
[httpcomponents-client] 02/03: Regression: Multipart body builder and multipart formatters fail to escape special characters such as backslash and quote mark
This is an automated email from the ASF dual-hosted git repository.
olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git
commit 32228cd4ff6cccc3d4d7889036de879198a8e5b5
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Mon Nov 7 14:39:32 2022 +0100
Regression: Multipart body builder and multipart formatters fail to escape special characters such as backslash and quote mark
---
.../http/entity/mime/AbstractMultipartFormat.java | 47 ++++++++++++++++---
.../hc/client5/http/entity/mime/MimeField.java | 9 +++-
.../hc/client5/http/entity/mime/TestMimeField.java | 51 +++++++++++++++++++++
.../http/entity/mime/TestMultipartFormat.java | 53 ++++++++++++++++++++++
4 files changed, 152 insertions(+), 8 deletions(-)
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/AbstractMultipartFormat.java b/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/AbstractMultipartFormat.java
index 66b373b9a..3f3c2c8e0 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/AbstractMultipartFormat.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/AbstractMultipartFormat.java
@@ -47,7 +47,7 @@ import org.apache.hc.core5.util.ByteArrayBuffer;
abstract class AbstractMultipartFormat {
static ByteArrayBuffer encode(
- final Charset charset, final String string) {
+ final Charset charset, final CharSequence string) {
final ByteBuffer encoded = charset.encode(CharBuffer.wrap(string));
final ByteArrayBuffer bab = new ByteArrayBuffer(encoded.remaining());
bab.append(encoded.array(), encoded.arrayOffset() + encoded.position(), encoded.remaining());
@@ -60,30 +60,63 @@ abstract class AbstractMultipartFormat {
}
static void writeBytes(
- final String s, final Charset charset, final OutputStream out) throws IOException {
+ final CharSequence s, final Charset charset, final OutputStream out) throws IOException {
final ByteArrayBuffer b = encode(charset, s);
writeBytes(b, out);
}
static void writeBytes(
- final String s, final OutputStream out) throws IOException {
+ final CharSequence s, final OutputStream out) throws IOException {
final ByteArrayBuffer b = encode(StandardCharsets.ISO_8859_1, s);
writeBytes(b, out);
}
+ static boolean isLineBreak(final char ch) {
+ return ch == '\r' || ch == '\n' || ch == '\f' || ch == 11;
+ }
+
+ static CharSequence stripLineBreaks(final CharSequence s) {
+ if (s == null) {
+ return null;
+ }
+ boolean requiresRewrite = false;
+ int n = 0;
+ for (; n < s.length(); n++) {
+ final char ch = s.charAt(n);
+ if (isLineBreak(ch)) {
+ requiresRewrite = true;
+ break;
+ }
+ }
+ if (!requiresRewrite) {
+ return s;
+ }
+ final StringBuilder buf = new StringBuilder();
+ buf.append(s, 0, n);
+ for (; n < s.length(); n++) {
+ final char ch = s.charAt(n);
+ if (isLineBreak(ch)) {
+ buf.append(' ');
+ } else {
+ buf.append(ch);
+ }
+ }
+ return buf.toString();
+ }
+
static void writeField(
final MimeField field, final OutputStream out) throws IOException {
- writeBytes(field.getName(), out);
+ writeBytes(stripLineBreaks(field.getName()), out);
writeBytes(FIELD_SEP, out);
- writeBytes(field.getBody(), out);
+ writeBytes(stripLineBreaks(field.getBody()), out);
writeBytes(CR_LF, out);
}
static void writeField(
final MimeField field, final Charset charset, final OutputStream out) throws IOException {
- writeBytes(field.getName(), charset, out);
+ writeBytes(stripLineBreaks(field.getName()), charset, out);
writeBytes(FIELD_SEP, out);
- writeBytes(field.getBody(), charset, out);
+ writeBytes(stripLineBreaks(field.getBody()), charset, out);
writeBytes(CR_LF, out);
}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/MimeField.java b/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/MimeField.java
index 87e9e9620..4fbbee4bc 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/MimeField.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/MimeField.java
@@ -84,7 +84,14 @@ public class MimeField {
sb.append("; ");
sb.append(parameter.getName());
sb.append("=\"");
- sb.append(parameter.getValue());
+ final String v = parameter.getValue();
+ for (int n = 0; n < v.length(); n++) {
+ final char ch = v.charAt(n);
+ if (ch == '"' || ch == '\\' ) {
+ sb.append("\\");
+ }
+ sb.append(ch);
+ }
sb.append("\"");
}
return sb.toString();
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/TestMimeField.java b/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/TestMimeField.java
new file mode 100644
index 000000000..2aa1e24b0
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/TestMimeField.java
@@ -0,0 +1,51 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.client5.http.entity.mime;
+
+import java.util.Arrays;
+
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestMimeField {
+
+ @Test
+ public void testBasics() {
+ final MimeField f1 = new MimeField("some-field", "some-value",
+ Arrays.<NameValuePair>asList(
+ new BasicNameValuePair("p1", "this"),
+ new BasicNameValuePair("p2", "that"),
+ new BasicNameValuePair("p3", "\"this \\and\\ that\"")));
+ Assertions.assertEquals("some-field", f1.getName());
+ Assertions.assertEquals("some-value", f1.getValue());
+ Assertions.assertEquals("some-value; p1=\"this\"; p2=\"that\"; p3=\"\\\"this \\\\and\\\\ that\\\"\"", f1.getBody());
+ }
+
+}
\ No newline at end of file
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/TestMultipartFormat.java b/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/TestMultipartFormat.java
new file mode 100644
index 000000000..eee183c12
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/TestMultipartFormat.java
@@ -0,0 +1,53 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.client5.http.entity.mime;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestMultipartFormat {
+
+ @Test
+ public void testLineBreak() {
+ Assertions.assertTrue(AbstractMultipartFormat.isLineBreak('\r'));
+ Assertions.assertTrue(AbstractMultipartFormat.isLineBreak('\n'));
+ Assertions.assertTrue(AbstractMultipartFormat.isLineBreak('\f'));
+ Assertions.assertTrue(AbstractMultipartFormat.isLineBreak((char) 11));
+ Assertions.assertFalse(AbstractMultipartFormat.isLineBreak(' '));
+ Assertions.assertFalse(AbstractMultipartFormat.isLineBreak('x'));
+ }
+
+ @Test
+ public void testLineBreakRewrite() {
+ final String s = "blah blah blah";
+ Assertions.assertSame(s, AbstractMultipartFormat.stripLineBreaks(s));
+ Assertions.assertEquals("blah blah blah ", AbstractMultipartFormat.stripLineBreaks("blah\rblah\nblah\f"));
+ Assertions.assertEquals(" f", AbstractMultipartFormat.stripLineBreaks("\r\n\r\nf"));
+ }
+
+}
\ No newline at end of file