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 2017/09/30 21:24:17 UTC

[1/3] httpcomponents-client git commit: Upgraded HttpCore dependency to version 4.4.7

Repository: httpcomponents-client
Updated Branches:
  refs/heads/4.6.x dd7d65f90 -> 278e47d27


Upgraded HttpCore dependency to version 4.4.7


Project: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/commit/258d7539
Tree: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/tree/258d7539
Diff: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/diff/258d7539

Branch: refs/heads/4.6.x
Commit: 258d7539a7644b1c14df4e6ce043dccf842bcbba
Parents: dd7d65f
Author: Oleg Kalnichevski <ol...@apache.org>
Authored: Thu Sep 14 13:27:00 2017 +0200
Committer: Oleg Kalnichevski <ol...@apache.org>
Committed: Fri Sep 29 23:29:54 2017 +0200

----------------------------------------------------------------------
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/258d7539/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 545e23a..edc9930 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,7 +67,7 @@
   <properties>
     <maven.compiler.source>1.6</maven.compiler.source>
     <maven.compiler.target>1.6</maven.compiler.target>
-    <httpcore.version>4.4.6</httpcore.version>
+    <httpcore.version>4.4.7</httpcore.version>
     <commons-logging.version>1.2</commons-logging.version>
     <commons-codec.version>1.10</commons-codec.version>
     <ehcache.version>2.6.11</ehcache.version>


[3/3] httpcomponents-client git commit: HTTPCLIENT-293 Implemented the percent encoding of the filename parameter of the Content-Disposition header based on RFC7578 sections 2 and 4.2. In the new MultipartForm implementation I included a PercentCodec tha

Posted by ol...@apache.org.
HTTPCLIENT-293 Implemented the percent encoding of the filename parameter of the Content-Disposition header based on RFC7578 sections 2 and 4.2. In the new MultipartForm implementation I included a PercentCodec that performs encoding/decoding to/from the percent encoding as described in RFC7578 and RFC3986.

Closes #85


Project: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/commit/278e47d2
Tree: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/tree/278e47d2
Diff: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/diff/278e47d2

Branch: refs/heads/4.6.x
Commit: 278e47d277568045ab8ff3c42677c791f0227d03
Parents: 582b280
Author: Ioannis Sermetziadis <se...@gmail.com>
Authored: Fri Sep 29 00:12:13 2017 +0300
Committer: Oleg Kalnichevski <ol...@apache.org>
Committed: Sat Sep 30 23:22:20 2017 +0200

----------------------------------------------------------------------
 .../http/entity/mime/AbstractMultipartForm.java |  18 +-
 .../http/entity/mime/HttpMultipartMode.java     |   4 +-
 .../http/entity/mime/HttpRFC7578Multipart.java  | 192 +++++++++++++++++++
 .../apache/http/entity/mime/MinimalField.java   |   7 +
 .../entity/mime/MultipartEntityBuilder.java     |   8 +-
 .../entity/mime/TestMultipartEntityBuilder.java |  29 ++-
 6 files changed, 246 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/278e47d2/httpmime/src/main/java/org/apache/http/entity/mime/AbstractMultipartForm.java
----------------------------------------------------------------------
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/AbstractMultipartForm.java b/httpmime/src/main/java/org/apache/http/entity/mime/AbstractMultipartForm.java
index 32249f6..3c31922 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/AbstractMultipartForm.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/AbstractMultipartForm.java
@@ -48,7 +48,7 @@ import org.apache.http.util.ByteArrayBuffer;
  */
 abstract class AbstractMultipartForm {
 
-    private static ByteArrayBuffer encode(
+    static ByteArrayBuffer encode(
             final Charset charset, final String string) {
         final ByteBuffer encoded = charset.encode(CharBuffer.wrap(string));
         final ByteArrayBuffer bab = new ByteArrayBuffer(encoded.remaining());
@@ -56,24 +56,24 @@ abstract class AbstractMultipartForm {
         return bab;
     }
 
-    private static void writeBytes(
+    static void writeBytes(
             final ByteArrayBuffer b, final OutputStream out) throws IOException {
         out.write(b.buffer(), 0, b.length());
     }
 
-    private static void writeBytes(
+    static void writeBytes(
             final String s, final Charset charset, final OutputStream out) throws IOException {
         final ByteArrayBuffer b = encode(charset, s);
         writeBytes(b, out);
     }
 
-    private static void writeBytes(
+    static void writeBytes(
             final String s, final OutputStream out) throws IOException {
         final ByteArrayBuffer b = encode(MIME.DEFAULT_CHARSET, s);
         writeBytes(b, out);
     }
 
-    protected static void writeField(
+    static void writeField(
             final MinimalField field, final OutputStream out) throws IOException {
         writeBytes(field.getName(), out);
         writeBytes(FIELD_SEP, out);
@@ -81,7 +81,7 @@ abstract class AbstractMultipartForm {
         writeBytes(CR_LF, out);
     }
 
-    protected static void writeField(
+    static void writeField(
             final MinimalField field, final Charset charset, final OutputStream out) throws IOException {
         writeBytes(field.getName(), charset, out);
         writeBytes(FIELD_SEP, out);
@@ -89,9 +89,9 @@ abstract class AbstractMultipartForm {
         writeBytes(CR_LF, out);
     }
 
-    private static final ByteArrayBuffer FIELD_SEP = encode(MIME.DEFAULT_CHARSET, ": ");
-    private static final ByteArrayBuffer CR_LF = encode(MIME.DEFAULT_CHARSET, "\r\n");
-    private static final ByteArrayBuffer TWO_DASHES = encode(MIME.DEFAULT_CHARSET, "--");
+    static final ByteArrayBuffer FIELD_SEP = encode(MIME.DEFAULT_CHARSET, ": ");
+    static final ByteArrayBuffer CR_LF = encode(MIME.DEFAULT_CHARSET, "\r\n");
+    static final ByteArrayBuffer TWO_DASHES = encode(MIME.DEFAULT_CHARSET, "--");
 
     final Charset charset;
     final String boundary;

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/278e47d2/httpmime/src/main/java/org/apache/http/entity/mime/HttpMultipartMode.java
----------------------------------------------------------------------
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/HttpMultipartMode.java b/httpmime/src/main/java/org/apache/http/entity/mime/HttpMultipartMode.java
index 18923d8..2a75f6f 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/HttpMultipartMode.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/HttpMultipartMode.java
@@ -38,6 +38,8 @@ public enum HttpMultipartMode {
     /** browser-compatible mode, i.e. only write Content-Disposition; use content charset */
     BROWSER_COMPATIBLE,
     /** RFC 6532 compliant */
-    RFC6532
+    RFC6532,
+    /** RFC 7578 compliant */
+    RFC7578
 
 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/278e47d2/httpmime/src/main/java/org/apache/http/entity/mime/HttpRFC7578Multipart.java
----------------------------------------------------------------------
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/HttpRFC7578Multipart.java b/httpmime/src/main/java/org/apache/http/entity/mime/HttpRFC7578Multipart.java
new file mode 100644
index 0000000..8d37bcb
--- /dev/null
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/HttpRFC7578Multipart.java
@@ -0,0 +1,192 @@
+/*
+ * ====================================================================
+ * 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.http.entity.mime;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.util.BitSet;
+import java.util.List;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.http.Consts;
+import org.apache.http.NameValuePair;
+import org.apache.http.util.ByteArrayBuffer;
+
+public class HttpRFC7578Multipart extends AbstractMultipartForm {
+
+    private static final PercentCodec PERCENT_CODEC = new PercentCodec();
+
+    private final List<FormBodyPart> parts;
+
+    public HttpRFC7578Multipart(
+        final Charset charset,
+        final String boundary,
+        final List<FormBodyPart> parts) {
+        super(charset, boundary);
+        this.parts = parts;
+    }
+
+    @Override
+    public List<FormBodyPart> getBodyParts() {
+        return parts;
+    }
+
+    @Override
+    protected void formatMultipartHeader(final FormBodyPart part, final OutputStream out) throws IOException {
+        for (final MinimalField field: part.getHeader()) {
+            if (MIME.CONTENT_DISPOSITION.equalsIgnoreCase(field.getName())) {
+                writeBytes(field.getName(), charset, out);
+                writeBytes(FIELD_SEP, out);
+                writeBytes(field.getValue(), out);
+                final List<NameValuePair> parameters = field.getParameters();
+                for (int i = 0; i < parameters.size(); i++) {
+                    final NameValuePair parameter = parameters.get(i);
+                    final String name = parameter.getName();
+                    final String value = parameter.getValue();
+                    writeBytes("; ", out);
+                    writeBytes(name, out);
+                    writeBytes("=\"", out);
+                    if (value != null) {
+                        if (name.equalsIgnoreCase(MIME.FIELD_PARAM_FILENAME)) {
+                            out.write(PERCENT_CODEC.encode(value.getBytes(charset)));
+                        } else {
+                            writeBytes(value, out);
+                        }
+                    }
+                    writeBytes("\"", out);
+                }
+                writeBytes(CR_LF, out);
+            } else {
+                writeField(field, charset, out);
+            }
+        }
+    }
+
+    static class PercentCodec {
+
+        private static final byte ESCAPE_CHAR = '%';
+
+        private static final BitSet ALWAYSENCODECHARS = new BitSet();
+
+        static {
+            ALWAYSENCODECHARS.set(' ');
+            ALWAYSENCODECHARS.set('%');
+        }
+
+        /**
+         * Percent-Encoding implementation based on RFC 3986
+         */
+        public byte[] encode(final byte[] bytes) {
+            if (bytes == null) {
+                return null;
+            }
+
+            final CharsetEncoder characterSetEncoder = Consts.ASCII.newEncoder();
+            final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+            for (final byte c : bytes) {
+                int b = c;
+                if (b < 0) {
+                    b = 256 + b;
+                }
+                if (characterSetEncoder.canEncode((char) b) && !ALWAYSENCODECHARS.get(c)) {
+                    buffer.write(b);
+                } else {
+                    buffer.write(ESCAPE_CHAR);
+                    final char hex1 = Utils.hexDigit(b >> 4);
+                    final char hex2 = Utils.hexDigit(b);
+                    buffer.write(hex1);
+                    buffer.write(hex2);
+                }
+            }
+            return buffer.toByteArray();
+        }
+
+        public byte[] decode(final byte[] bytes) throws DecoderException {
+            if (bytes == null) {
+                return null;
+            }
+            final ByteArrayBuffer buffer = new ByteArrayBuffer(bytes.length);
+            for (int i = 0; i < bytes.length; i++) {
+                final int b = bytes[i];
+                if (b == ESCAPE_CHAR) {
+                    try {
+                        final int u = Utils.digit16(bytes[++i]);
+                        final int l = Utils.digit16(bytes[++i]);
+                        buffer.append((char) ((u << 4) + l));
+                    } catch (final ArrayIndexOutOfBoundsException e) {
+                        throw new DecoderException("Invalid URL encoding: ", e);
+                    }
+                } else {
+                    buffer.append(b);
+                }
+            }
+            return buffer.toByteArray();
+        }
+    }
+
+    static class Utils {
+
+        /**
+         * Radix used in encoding and decoding.
+         */
+        private static final int RADIX = 16;
+
+        /**
+         * Returns the numeric value of the character <code>b</code> in radix 16.
+         *
+         * @param b
+         *            The byte to be converted.
+         * @return The numeric value represented by the character in radix 16.
+         *
+         * @throws DecoderException
+         *             Thrown when the byte is not valid per {@link Character#digit(char,int)}
+         */
+        static int digit16(final byte b) throws DecoderException {
+            final int i = Character.digit((char) b, RADIX);
+            if (i == -1) {
+                throw new DecoderException("Invalid URL encoding: not a valid digit (radix " + RADIX + "): " + b);
+            }
+            return i;
+        }
+
+        /**
+         * Returns the upper case hex digit of the lower 4 bits of the int.
+         *
+         * @param b the input int
+         * @return the upper case hex digit of the lower 4 bits of the int.
+         */
+        static char hexDigit(final int b) {
+            return Character.toUpperCase(Character.forDigit(b & 0xF, RADIX));
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/278e47d2/httpmime/src/main/java/org/apache/http/entity/mime/MinimalField.java
----------------------------------------------------------------------
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/MinimalField.java b/httpmime/src/main/java/org/apache/http/entity/mime/MinimalField.java
index 676eba5..05ebc02 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/MinimalField.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/MinimalField.java
@@ -69,6 +69,13 @@ public class MinimalField {
         return this.name;
     }
 
+    /**
+     * @since 4.6
+     */
+    public String getValue() {
+        return this.value;
+    }
+
     public String getBody() {
         final StringBuilder sb = new StringBuilder();
         sb.append(this.value);

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/278e47d2/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java
----------------------------------------------------------------------
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java b/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java
index d9978a8..ba7e353 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java
@@ -231,8 +231,14 @@ public class MultipartEntityBuilder {
             case RFC6532:
                 form = new HttpRFC6532Multipart(charsetCopy, boundaryCopy, bodyPartsCopy);
                 break;
+            case RFC7578:
+                if (charsetCopy == null) {
+                    charsetCopy = Consts.UTF_8;
+                }
+                form = new HttpRFC7578Multipart(charsetCopy, boundaryCopy, bodyPartsCopy);
+                break;
             default:
-                form = new HttpStrictMultipart(Consts.ASCII, boundaryCopy, bodyPartsCopy);
+                form = new HttpStrictMultipart(charsetCopy, boundaryCopy, bodyPartsCopy);
         }
         return new MultipartFormEntity(form, contentTypeCopy, form.getTotalLength());
     }

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/278e47d2/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java
----------------------------------------------------------------------
diff --git a/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java b/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java
index d63123b..33f28f2 100644
--- a/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java
+++ b/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java
@@ -144,7 +144,7 @@ public class TestMultipartEntityBuilder {
 
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
-        entity.getMultipart().writeTo(out);
+        entity.writeTo(out);
         out.close();
         Assert.assertEquals("--xxxxxxxxxxxxxxxxxxxxxxxx\r\n" +
                 "Content-Disposition: multipart/form-data; name=\"test\"; filename=\"hello world\"\r\n" +
@@ -154,5 +154,32 @@ public class TestMultipartEntityBuilder {
                 "hello world\r\n" +
                 "--xxxxxxxxxxxxxxxxxxxxxxxx--\r\n", out.toString(Consts.ASCII.name()));
     }
+    @Test
+    public void testMultipartWriteToRFC7578Mode() throws Exception {
+        final List<NameValuePair> parameters = new ArrayList<NameValuePair>();
+        parameters.add(new BasicNameValuePair(MIME.FIELD_PARAM_NAME, "test"));
+        parameters.add(new BasicNameValuePair(MIME.FIELD_PARAM_FILENAME, "hello \u03BA\u03CC\u03C3\u03BC\u03B5!%"));
+
+        final MultipartFormEntity entity = MultipartEntityBuilder.create()
+                .setMode(HttpMultipartMode.RFC7578)
+                .setBoundary("xxxxxxxxxxxxxxxxxxxxxxxx")
+                .addPart(new FormBodyPartBuilder()
+                        .setName("test")
+                        .setBody(new StringBody("hello world", ContentType.TEXT_PLAIN))
+                        .addField("Content-Disposition", "multipart/form-data", parameters)
+                        .build())
+                .buildEntity();
+
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        entity.writeTo(out);
+        out.close();
+        Assert.assertEquals("--xxxxxxxxxxxxxxxxxxxxxxxx\r\n" +
+                "Content-Disposition: multipart/form-data; name=\"test\"; filename=\"hello%20%CE%BA%CF%8C%CF%83%CE%BC%CE%B5!%25\"\r\n" +
+                "Content-Type: text/plain; charset=ISO-8859-1\r\n" +
+                "Content-Transfer-Encoding: 8bit\r\n" +
+                "\r\n" +
+                "hello world\r\n" +
+                "--xxxxxxxxxxxxxxxxxxxxxxxx--\r\n", out.toString(Consts.ASCII.name()));
+    }
 
 }


[2/3] httpcomponents-client git commit: HTTPCLIENT-293 Refactored code in order to support multipart header field parameters in the data model and postpone the formatting and encoding of the parameters until the moment written into a stream, which is ess

Posted by ol...@apache.org.
HTTPCLIENT-293 Refactored code in order to support multipart header field parameters in the data model and postpone the formatting and encoding of the parameters until the moment written into a stream, which is essential in order to avoid multiple encodings of the same value.


Project: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/commit/582b2806
Tree: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/tree/582b2806
Diff: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/diff/582b2806

Branch: refs/heads/4.6.x
Commit: 582b28060335c443f971b7fe02bbfc9f3d44bf44
Parents: 258d753
Author: Ioannis Sermetziadis <se...@gmail.com>
Authored: Thu Sep 28 23:56:49 2017 +0300
Committer: Oleg Kalnichevski <ol...@apache.org>
Committed: Sat Sep 30 12:39:07 2017 +0200

----------------------------------------------------------------------
 .../http/entity/mime/FormBodyPartBuilder.java   | 39 ++++++++-----------
 .../java/org/apache/http/entity/mime/MIME.java  |  7 +++-
 .../apache/http/entity/mime/MinimalField.java   | 40 +++++++++++++++++++-
 .../entity/mime/MultipartEntityBuilder.java     |  3 +-
 .../entity/mime/TestFormBodyPartBuilder.java    | 26 -------------
 .../entity/mime/TestMultipartEntityBuilder.java | 32 ++++++++++++++++
 6 files changed, 93 insertions(+), 54 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/582b2806/httpmime/src/main/java/org/apache/http/entity/mime/FormBodyPartBuilder.java
----------------------------------------------------------------------
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/FormBodyPartBuilder.java b/httpmime/src/main/java/org/apache/http/entity/mime/FormBodyPartBuilder.java
index 31c203a..cb3e584 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/FormBodyPartBuilder.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/FormBodyPartBuilder.java
@@ -27,11 +27,14 @@
 
 package org.apache.http.entity.mime;
 
+import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.http.NameValuePair;
 import org.apache.http.entity.ContentType;
 import org.apache.http.entity.mime.content.AbstractContentBody;
 import org.apache.http.entity.mime.content.ContentBody;
+import org.apache.http.message.BasicNameValuePair;
 import org.apache.http.util.Args;
 import org.apache.http.util.Asserts;
 
@@ -74,6 +77,15 @@ public class FormBodyPartBuilder {
         return this;
     }
 
+    /**
+     * @since 4.6
+     */
+    public FormBodyPartBuilder addField(final String name, final String value, final List<NameValuePair> parameters) {
+        Args.notNull(name, "Field name");
+        this.header.addField(new MinimalField(name, value, parameters));
+        return this;
+    }
+
     public FormBodyPartBuilder addField(final String name, final String value) {
         Args.notNull(name, "Field name");
         this.header.addField(new MinimalField(name, value));
@@ -101,16 +113,12 @@ public class FormBodyPartBuilder {
             headerCopy.addField(field);
         }
         if (headerCopy.getField(MIME.CONTENT_DISPOSITION) == null) {
-            final StringBuilder buffer = new StringBuilder();
-            buffer.append("form-data; name=\"");
-            buffer.append(encodeForHeader(this.name));
-            buffer.append("\"");
+            final List<NameValuePair> fieldParameters = new ArrayList<NameValuePair>();
+            fieldParameters.add(new BasicNameValuePair(MIME.FIELD_PARAM_NAME, this.name));
             if (this.body.getFilename() != null) {
-                buffer.append("; filename=\"");
-                buffer.append(encodeForHeader(this.body.getFilename()));
-                buffer.append("\"");
+                fieldParameters.add(new BasicNameValuePair(MIME.FIELD_PARAM_FILENAME, this.body.getFilename()));
             }
-            headerCopy.addField(new MinimalField(MIME.CONTENT_DISPOSITION, buffer.toString()));
+            headerCopy.addField(new MinimalField(MIME.CONTENT_DISPOSITION, "form-data", fieldParameters));
         }
         if (headerCopy.getField(MIME.CONTENT_TYPE) == null) {
             final ContentType contentType;
@@ -138,19 +146,4 @@ public class FormBodyPartBuilder {
         return new FormBodyPart(this.name, this.body, headerCopy);
     }
 
-    private static String encodeForHeader(final String headerName) {
-        if (headerName == null) {
-            return null;
-        }
-        final StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < headerName.length(); i++) {
-            final char x = headerName.charAt(i);
-            if (x == '"' || x == '\\' || x == '\r') {
-                sb.append("\\");
-            }
-            sb.append(x);
-        }
-        return sb.toString();
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/582b2806/httpmime/src/main/java/org/apache/http/entity/mime/MIME.java
----------------------------------------------------------------------
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/MIME.java b/httpmime/src/main/java/org/apache/http/entity/mime/MIME.java
index 945bd15..e4df389 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/MIME.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/MIME.java
@@ -27,10 +27,10 @@
 
 package org.apache.http.entity.mime;
 
-import org.apache.http.Consts;
-
 import java.nio.charset.Charset;
 
+import org.apache.http.Consts;
+
 /**
  *
  * @since 4.0
@@ -41,6 +41,9 @@ public final class MIME {
     public static final String CONTENT_TRANSFER_ENC  = "Content-Transfer-Encoding";
     public static final String CONTENT_DISPOSITION   = "Content-Disposition";
 
+    public static final String FIELD_PARAM_NAME      = "name";
+    public static final String FIELD_PARAM_FILENAME  = "filename";
+
     public static final String ENC_8BIT              = "8bit";
     public static final String ENC_BINARY            = "binary";
 

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/582b2806/httpmime/src/main/java/org/apache/http/entity/mime/MinimalField.java
----------------------------------------------------------------------
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/MinimalField.java b/httpmime/src/main/java/org/apache/http/entity/mime/MinimalField.java
index c6da877..676eba5 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/MinimalField.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/MinimalField.java
@@ -27,6 +27,12 @@
 
 package org.apache.http.entity.mime;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.http.NameValuePair;
+
 /**
  * Minimal MIME field.
  *
@@ -36,11 +42,27 @@ public class MinimalField {
 
     private final String name;
     private final String value;
+    private final List<NameValuePair> parameters;
 
     public MinimalField(final String name, final String value) {
         super();
         this.name = name;
         this.value = value;
+        this.parameters = Collections.emptyList();
+    }
+
+    /**
+     * @since 4.6
+     */
+    public MinimalField(final String name, final String value, final List<NameValuePair> parameters) {
+        this.name = name;
+        this.value = value;
+        this.parameters = parameters != null ?
+                Collections.unmodifiableList(new ArrayList<NameValuePair>(parameters)) : Collections.<NameValuePair>emptyList();
+    }
+
+    public MinimalField(final MinimalField from) {
+        this(from.name, from.value, from.parameters);
     }
 
     public String getName() {
@@ -48,7 +70,21 @@ public class MinimalField {
     }
 
     public String getBody() {
-        return this.value;
+        final StringBuilder sb = new StringBuilder();
+        sb.append(this.value);
+        for (int i = 0; i < this.parameters.size(); i++) {
+            final NameValuePair parameter = this.parameters.get(i);
+            sb.append("; ");
+            sb.append(parameter.getName());
+            sb.append("=\"");
+            sb.append(parameter.getValue());
+            sb.append("\"");
+        }
+        return sb.toString();
+    }
+
+    public List<NameValuePair> getParameters() {
+        return this.parameters;
     }
 
     @Override
@@ -56,7 +92,7 @@ public class MinimalField {
         final StringBuilder buffer = new StringBuilder();
         buffer.append(this.name);
         buffer.append(": ");
-        buffer.append(this.value);
+        buffer.append(this.getBody());
         return buffer.toString();
     }
 

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/582b2806/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java
----------------------------------------------------------------------
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java b/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java
index 59e4ccc..d9978a8 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java
@@ -35,6 +35,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Random;
 
+import org.apache.http.Consts;
 import org.apache.http.HttpEntity;
 import org.apache.http.NameValuePair;
 import org.apache.http.entity.ContentType;
@@ -231,7 +232,7 @@ public class MultipartEntityBuilder {
                 form = new HttpRFC6532Multipart(charsetCopy, boundaryCopy, bodyPartsCopy);
                 break;
             default:
-                form = new HttpStrictMultipart(charsetCopy, boundaryCopy, bodyPartsCopy);
+                form = new HttpStrictMultipart(Consts.ASCII, boundaryCopy, bodyPartsCopy);
         }
         return new MultipartFormEntity(form, contentTypeCopy, form.getTotalLength());
     }

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/582b2806/httpmime/src/test/java/org/apache/http/entity/mime/TestFormBodyPartBuilder.java
----------------------------------------------------------------------
diff --git a/httpmime/src/test/java/org/apache/http/entity/mime/TestFormBodyPartBuilder.java b/httpmime/src/test/java/org/apache/http/entity/mime/TestFormBodyPartBuilder.java
index 56dd4f9..81b7e1f 100644
--- a/httpmime/src/test/java/org/apache/http/entity/mime/TestFormBodyPartBuilder.java
+++ b/httpmime/src/test/java/org/apache/http/entity/mime/TestFormBodyPartBuilder.java
@@ -27,14 +27,11 @@
 
 package org.apache.http.entity.mime;
 
-import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.util.Arrays;
 import java.util.List;
-
 import org.apache.http.entity.ContentType;
 import org.apache.http.entity.mime.content.FileBody;
-import org.apache.http.entity.mime.content.InputStreamBody;
 import org.apache.http.entity.mime.content.StringBody;
 import org.junit.Assert;
 import org.junit.Test;
@@ -61,29 +58,6 @@ public class TestFormBodyPartBuilder {
     }
 
     @Test
-    public void testCharacterStuffing() throws Exception {
-        final FormBodyPartBuilder builder = FormBodyPartBuilder.create();
-        final InputStreamBody fileBody = new InputStreamBody(new ByteArrayInputStream(
-                "hello world".getBytes("UTF-8")), "stuff_with \"quotes\" and \\slashes\\.bin");
-        final FormBodyPart bodyPart2 = builder
-                .setName("yada_with \"quotes\" and \\slashes\\")
-                .setBody(fileBody)
-                .build();
-
-        Assert.assertNotNull(bodyPart2);
-        Assert.assertEquals("yada_with \"quotes\" and \\slashes\\", bodyPart2.getName());
-        Assert.assertEquals(fileBody, bodyPart2.getBody());
-        final Header header2 = bodyPart2.getHeader();
-        Assert.assertNotNull(header2);
-        assertFields(Arrays.asList(
-                        new MinimalField("Content-Disposition", "form-data; name=\"yada_with \\\"quotes\\\" " +
-                                "and \\\\slashes\\\\\"; filename=\"stuff_with \\\"quotes\\\" and \\\\slashes\\\\.bin\""),
-                        new MinimalField("Content-Type", "application/octet-stream"),
-                        new MinimalField("Content-Transfer-Encoding", "binary")),
-                header2.getFields());
-    }
-
-    @Test
     public void testBuildBodyPartMultipleBuilds() throws Exception {
         final StringBody stringBody = new StringBody("stuff", ContentType.TEXT_PLAIN);
         final FormBodyPartBuilder builder = FormBodyPartBuilder.create();

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/582b2806/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java
----------------------------------------------------------------------
diff --git a/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java b/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java
index 68a1c17..d63123b 100644
--- a/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java
+++ b/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java
@@ -28,12 +28,16 @@
 package org.apache.http.entity.mime;
 
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.http.Consts;
 import org.apache.http.Header;
+import org.apache.http.NameValuePair;
 import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.content.StringBody;
 import org.apache.http.message.BasicNameValuePair;
 import org.junit.Assert;
 import org.junit.Test;
@@ -123,4 +127,32 @@ public class TestMultipartEntityBuilder {
                 contentType.getValue());
     }
 
+    @Test
+    public void testMultipartWriteTo() throws Exception {
+        final List<NameValuePair> parameters = new ArrayList<NameValuePair>();
+        parameters.add(new BasicNameValuePair(MIME.FIELD_PARAM_NAME, "test"));
+        parameters.add(new BasicNameValuePair(MIME.FIELD_PARAM_FILENAME, "hello world"));
+        final MultipartFormEntity entity = MultipartEntityBuilder.create()
+                .setStrictMode()
+                .setBoundary("xxxxxxxxxxxxxxxxxxxxxxxx")
+                .addPart(new FormBodyPartBuilder()
+                        .setName("test")
+                        .setBody(new StringBody("hello world", ContentType.TEXT_PLAIN))
+                        .addField("Content-Disposition", "multipart/form-data", parameters)
+                        .build())
+                .buildEntity();
+
+
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        entity.getMultipart().writeTo(out);
+        out.close();
+        Assert.assertEquals("--xxxxxxxxxxxxxxxxxxxxxxxx\r\n" +
+                "Content-Disposition: multipart/form-data; name=\"test\"; filename=\"hello world\"\r\n" +
+                "Content-Type: text/plain; charset=ISO-8859-1\r\n" +
+                "Content-Transfer-Encoding: 8bit\r\n" +
+                "\r\n" +
+                "hello world\r\n" +
+                "--xxxxxxxxxxxxxxxxxxxxxxxx--\r\n", out.toString(Consts.ASCII.name()));
+    }
+
 }