View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.http.entity.mime;
29  
30  import java.io.ByteArrayOutputStream;
31  import java.io.File;
32  import java.io.FileInputStream;
33  import java.io.FileWriter;
34  import java.io.Writer;
35  import java.nio.charset.Charset;
36  import java.util.Arrays;
37  
38  import org.apache.http.Consts;
39  import org.apache.http.entity.ContentType;
40  import org.apache.http.entity.mime.content.FileBody;
41  import org.apache.http.entity.mime.content.InputStreamBody;
42  import org.apache.http.entity.mime.content.StringBody;
43  import org.junit.After;
44  import org.junit.Assert;
45  import org.junit.Test;
46  
47  public class TestMultipartForm {
48  
49      private File tmpfile;
50  
51      @After
52      public void cleanup() {
53          if (tmpfile != null) {
54              tmpfile.delete();
55          }
56      }
57  
58      @Test
59      public void testMultipartFormStringParts() throws Exception {
60          final FormBodyPart p1 = FormBodyPartBuilder.create(
61                  "field1",
62                  new StringBody("this stuff", ContentType.DEFAULT_TEXT)).build();
63          final FormBodyPart p2 = FormBodyPartBuilder.create(
64                  "field2",
65                  new StringBody("that stuff", ContentType.create(
66                          ContentType.TEXT_PLAIN.getMimeType(), Consts.UTF_8))).build();
67          final FormBodyPart p3 = FormBodyPartBuilder.create(
68                  "field3",
69                  new StringBody("all kind of stuff", ContentType.DEFAULT_TEXT)).build();
70          final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
71                  Arrays.asList(p1, p2, p3));
72  
73          final ByteArrayOutputStream out = new ByteArrayOutputStream();
74          multipart.writeTo(out);
75          out.close();
76  
77          final String expected =
78              "--foo\r\n" +
79              "Content-Disposition: form-data; name=\"field1\"\r\n" +
80              "Content-Type: text/plain; charset=ISO-8859-1\r\n" +
81              "Content-Transfer-Encoding: 8bit\r\n" +
82              "\r\n" +
83              "this stuff\r\n" +
84              "--foo\r\n" +
85              "Content-Disposition: form-data; name=\"field2\"\r\n" +
86              "Content-Type: text/plain; charset=UTF-8\r\n" +
87              "Content-Transfer-Encoding: 8bit\r\n" +
88              "\r\n" +
89              "that stuff\r\n" +
90              "--foo\r\n" +
91              "Content-Disposition: form-data; name=\"field3\"\r\n" +
92              "Content-Type: text/plain; charset=ISO-8859-1\r\n" +
93              "Content-Transfer-Encoding: 8bit\r\n" +
94              "\r\n" +
95              "all kind of stuff\r\n" +
96              "--foo--\r\n";
97          final String s = out.toString("US-ASCII");
98          Assert.assertEquals(expected, s);
99          Assert.assertEquals(s.length(), multipart.getTotalLength());
100     }
101 
102     @Test
103     public void testMultipartFormCustomContentType() throws Exception {
104         final FormBodyPart p1 = FormBodyPartBuilder.create(
105                 "field1",
106                 new StringBody("this stuff", ContentType.DEFAULT_TEXT)).build();
107         final FormBodyPart p2 = FormBodyPartBuilder.create(
108                 "field2",
109                 new StringBody("that stuff", ContentType.parse("stuff/plain; param=value"))).build();
110         final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
111                 Arrays.asList(p1, p2));
112 
113         final ByteArrayOutputStream out = new ByteArrayOutputStream();
114         multipart.writeTo(out);
115         out.close();
116 
117         final String expected =
118                 "--foo\r\n" +
119                         "Content-Disposition: form-data; name=\"field1\"\r\n" +
120                         "Content-Type: text/plain; charset=ISO-8859-1\r\n" +
121                         "Content-Transfer-Encoding: 8bit\r\n" +
122                         "\r\n" +
123                         "this stuff\r\n" +
124                         "--foo\r\n" +
125                         "Content-Disposition: form-data; name=\"field2\"\r\n" +
126                         "Content-Type: stuff/plain; param=value\r\n" +
127                         "Content-Transfer-Encoding: 8bit\r\n" +
128                         "\r\n" +
129                         "that stuff\r\n" +
130                         "--foo--\r\n";
131         final String s = out.toString("US-ASCII");
132         Assert.assertEquals(expected, s);
133         Assert.assertEquals(s.length(), multipart.getTotalLength());
134     }
135 
136     @Test
137     public void testMultipartFormBinaryParts() throws Exception {
138         tmpfile = File.createTempFile("tmp", ".bin");
139         final Writer writer = new FileWriter(tmpfile);
140         try {
141             writer.append("some random whatever");
142         } finally {
143             writer.close();
144         }
145 
146         final FormBodyPart p1 = FormBodyPartBuilder.create(
147                 "field1",
148                 new FileBody(tmpfile)).build();
149         @SuppressWarnings("resource")
150         final FormBodyPart p2 = FormBodyPartBuilder.create(
151                 "field2",
152                 new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build();
153         final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
154                 Arrays.asList(p1, p2));
155 
156         final ByteArrayOutputStream out = new ByteArrayOutputStream();
157         multipart.writeTo(out);
158         out.close();
159 
160         final String expected =
161             "--foo\r\n" +
162             "Content-Disposition: form-data; name=\"field1\"; " +
163                 "filename=\"" + tmpfile.getName() + "\"\r\n" +
164             "Content-Type: application/octet-stream\r\n" +
165             "Content-Transfer-Encoding: binary\r\n" +
166             "\r\n" +
167             "some random whatever\r\n" +
168             "--foo\r\n" +
169             "Content-Disposition: form-data; name=\"field2\"; " +
170                 "filename=\"file.tmp\"\r\n" +
171             "Content-Type: application/octet-stream\r\n" +
172             "Content-Transfer-Encoding: binary\r\n" +
173             "\r\n" +
174             "some random whatever\r\n" +
175             "--foo--\r\n";
176         final String s = out.toString("US-ASCII");
177         Assert.assertEquals(expected, s);
178         Assert.assertEquals(-1, multipart.getTotalLength());
179     }
180 
181     @Test
182     public void testMultipartFormStrict() throws Exception {
183         tmpfile = File.createTempFile("tmp", ".bin");
184         final Writer writer = new FileWriter(tmpfile);
185         try {
186             writer.append("some random whatever");
187         } finally {
188             writer.close();
189         }
190 
191         final FormBodyPart p1 = FormBodyPartBuilder.create(
192                 "field1",
193                 new FileBody(tmpfile)).build();
194         final FormBodyPart p2 = FormBodyPartBuilder.create(
195                 "field2",
196                 new FileBody(tmpfile, ContentType.create("text/plain", "ANSI_X3.4-1968"), "test-file")).build();
197         @SuppressWarnings("resource")
198         final FormBodyPart p3 = FormBodyPartBuilder.create(
199                 "field3",
200                 new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build();
201         final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
202                 Arrays.asList(p1, p2, p3));
203 
204         final ByteArrayOutputStream out = new ByteArrayOutputStream();
205         multipart.writeTo(out);
206         out.close();
207 
208         final String expected =
209             "--foo\r\n" +
210             "Content-Disposition: form-data; name=\"field1\"; " +
211                 "filename=\"" + tmpfile.getName() + "\"\r\n" +
212             "Content-Type: application/octet-stream\r\n" +
213             "Content-Transfer-Encoding: binary\r\n" +
214             "\r\n" +
215             "some random whatever\r\n" +
216             "--foo\r\n" +
217             "Content-Disposition: form-data; name=\"field2\"; " +
218                 "filename=\"test-file\"\r\n" +
219             "Content-Type: text/plain; charset=US-ASCII\r\n" +
220             "Content-Transfer-Encoding: binary\r\n" +
221             "\r\n" +
222             "some random whatever\r\n" +
223             "--foo\r\n" +
224             "Content-Disposition: form-data; name=\"field3\"; " +
225                 "filename=\"file.tmp\"\r\n" +
226             "Content-Type: application/octet-stream\r\n" +
227             "Content-Transfer-Encoding: binary\r\n" +
228             "\r\n" +
229             "some random whatever\r\n" +
230             "--foo--\r\n";
231         final String s = out.toString("US-ASCII");
232         Assert.assertEquals(expected, s);
233         Assert.assertEquals(-1, multipart.getTotalLength());
234     }
235 
236     @Test
237     public void testMultipartFormRFC6532() throws Exception {
238         tmpfile = File.createTempFile("tmp", ".bin");
239         final Writer writer = new FileWriter(tmpfile);
240         try {
241             writer.append("some random whatever");
242         } finally {
243             writer.close();
244         }
245 
246         final FormBodyPart p1 = FormBodyPartBuilder.create(
247                 "field1\u0414",
248                 new FileBody(tmpfile)).build();
249         final FormBodyPart p2 = FormBodyPartBuilder.create(
250                 "field2",
251                 new FileBody(tmpfile, ContentType.create("text/plain", "ANSI_X3.4-1968"), "test-file")).build();
252         @SuppressWarnings("resource")
253         final FormBodyPart p3 = FormBodyPartBuilder.create(
254                 "field3",
255                 new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build();
256         final HttpRFC6532Multipart multipart = new HttpRFC6532Multipart(null, "foo",
257                 Arrays.asList(p1, p2, p3));
258 
259         final ByteArrayOutputStream out = new ByteArrayOutputStream();
260         multipart.writeTo(out);
261         out.close();
262 
263         final String expected =
264             "--foo\r\n" +
265             "Content-Disposition: form-data; name=\"field1\u0414\"; " +
266                 "filename=\"" + tmpfile.getName() + "\"\r\n" +
267             "Content-Type: application/octet-stream\r\n" +
268             "Content-Transfer-Encoding: binary\r\n" +
269             "\r\n" +
270             "some random whatever\r\n" +
271             "--foo\r\n" +
272             "Content-Disposition: form-data; name=\"field2\"; " +
273                 "filename=\"test-file\"\r\n" +
274             "Content-Type: text/plain; charset=US-ASCII\r\n" +
275             "Content-Transfer-Encoding: binary\r\n" +
276             "\r\n" +
277             "some random whatever\r\n" +
278             "--foo\r\n" +
279             "Content-Disposition: form-data; name=\"field3\"; " +
280                 "filename=\"file.tmp\"\r\n" +
281             "Content-Type: application/octet-stream\r\n" +
282             "Content-Transfer-Encoding: binary\r\n" +
283             "\r\n" +
284             "some random whatever\r\n" +
285             "--foo--\r\n";
286         final String s = out.toString("UTF-8");
287         Assert.assertEquals(expected, s);
288         Assert.assertEquals(-1, multipart.getTotalLength());
289     }
290 
291     private static final int SWISS_GERMAN_HELLO [] = {
292         0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4
293     };
294 
295     private static final int RUSSIAN_HELLO [] = {
296         0x412, 0x441, 0x435, 0x43C, 0x5F, 0x43F, 0x440, 0x438,
297         0x432, 0x435, 0x442
298     };
299 
300     private static String constructString(final int [] unicodeChars) {
301         final StringBuilder buffer = new StringBuilder();
302         if (unicodeChars != null) {
303             for (final int unicodeChar : unicodeChars) {
304                 buffer.append((char)unicodeChar);
305             }
306         }
307         return buffer.toString();
308     }
309 
310     @Test
311     public void testMultipartFormBrowserCompatibleNonASCIIHeaders() throws Exception {
312         final String s1 = constructString(SWISS_GERMAN_HELLO);
313         final String s2 = constructString(RUSSIAN_HELLO);
314 
315         tmpfile = File.createTempFile("tmp", ".bin");
316         final Writer writer = new FileWriter(tmpfile);
317         try {
318             writer.append("some random whatever");
319         } finally {
320             writer.close();
321         }
322 
323         @SuppressWarnings("resource")
324         final FormBodyPart p1 = FormBodyPartBuilder.create(
325                 "field1",
326                 new InputStreamBody(new FileInputStream(tmpfile), s1 + ".tmp")).build();
327         @SuppressWarnings("resource")
328         final FormBodyPart p2 = FormBodyPartBuilder.create(
329                 "field2",
330                 new InputStreamBody(new FileInputStream(tmpfile), s2 + ".tmp")).build();
331         final HttpBrowserCompatibleMultipart multipart = new HttpBrowserCompatibleMultipart(
332                 Consts.UTF_8, "foo",
333                 Arrays.asList(p1, p2));
334 
335         final ByteArrayOutputStream out = new ByteArrayOutputStream();
336         multipart.writeTo(out);
337         out.close();
338 
339         final String expected =
340             "--foo\r\n" +
341             "Content-Disposition: form-data; name=\"field1\"; " +
342                 "filename=\"" + s1 + ".tmp\"\r\n" +
343             "Content-Type: application/octet-stream\r\n" +
344             "\r\n" +
345             "some random whatever\r\n" +
346             "--foo\r\n" +
347             "Content-Disposition: form-data; name=\"field2\"; " +
348                 "filename=\"" + s2 + ".tmp\"\r\n" +
349             "Content-Type: application/octet-stream\r\n" +
350             "\r\n" +
351             "some random whatever\r\n" +
352             "--foo--\r\n";
353         final String s = out.toString("UTF-8");
354         Assert.assertEquals(expected, s);
355         Assert.assertEquals(-1, multipart.getTotalLength());
356     }
357 
358     @Test
359     public void testMultipartFormStringPartsMultiCharsets() throws Exception {
360         final String s1 = constructString(SWISS_GERMAN_HELLO);
361         final String s2 = constructString(RUSSIAN_HELLO);
362 
363         final FormBodyPart p1 = FormBodyPartBuilder.create(
364                 "field1",
365                 new StringBody(s1, ContentType.create("text/plain", Charset.forName("ISO-8859-1")))).build();
366         final FormBodyPart p2 = FormBodyPartBuilder.create(
367                 "field2",
368                 new StringBody(s2, ContentType.create("text/plain", Charset.forName("KOI8-R")))).build();
369         final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
370                 Arrays.asList(p1, p2));
371 
372         final ByteArrayOutputStream out1 = new ByteArrayOutputStream();
373         multipart.writeTo(out1);
374         out1.close();
375 
376         final ByteArrayOutputStream out2 = new ByteArrayOutputStream();
377 
378         out2.write((
379             "--foo\r\n" +
380             "Content-Disposition: form-data; name=\"field1\"\r\n" +
381             "Content-Type: text/plain; charset=ISO-8859-1\r\n" +
382             "Content-Transfer-Encoding: 8bit\r\n" +
383             "\r\n").getBytes(Consts.ASCII));
384         out2.write(s1.getBytes(Consts.ISO_8859_1));
385         out2.write(("\r\n" +
386             "--foo\r\n" +
387             "Content-Disposition: form-data; name=\"field2\"\r\n" +
388             "Content-Type: text/plain; charset=KOI8-R\r\n" +
389             "Content-Transfer-Encoding: 8bit\r\n" +
390             "\r\n").getBytes(Consts.ASCII));
391         out2.write(s2.getBytes(Charset.forName("KOI8-R")));
392         out2.write(("\r\n" +
393             "--foo--\r\n").getBytes(Consts.ASCII));
394         out2.close();
395 
396         final byte[] actual = out1.toByteArray();
397         final byte[] expected = out2.toByteArray();
398 
399         Assert.assertEquals(expected.length, actual.length);
400         for (int i = 0; i < actual.length; i++) {
401             Assert.assertEquals(expected[i], actual[i]);
402         }
403         Assert.assertEquals(expected.length, multipart.getTotalLength());
404     }
405 
406 }