1 /*
2  * Copyright (C) 2014 Square, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.squareup.okhttp;
17 
18 import java.io.IOException;
19 import okio.Buffer;
20 import okio.BufferedSink;
21 import org.junit.Test;
22 
23 import static com.squareup.okhttp.internal.Util.UTF_8;
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.fail;
26 
27 public final class MultipartBuilderTest {
28   @Test(expected = IllegalStateException.class)
onePartRequired()29   public void onePartRequired() throws Exception {
30     new MultipartBuilder().build();
31   }
32 
singlePart()33   @Test public void singlePart() throws Exception {
34     String expected = ""
35         + "--123\r\n"
36         + "Content-Length: 13\r\n"
37         + "\r\n"
38         + "Hello, World!\r\n"
39         + "--123--\r\n";
40 
41     RequestBody requestBody = new MultipartBuilder("123")
42         .addPart(RequestBody.create(null, "Hello, World!"))
43         .build();
44 
45     assertEquals("multipart/mixed; boundary=123", requestBody.contentType().toString());
46 
47     Buffer buffer = new Buffer();
48     requestBody.writeTo(buffer);
49     assertEquals(53, requestBody.contentLength());
50     assertEquals(buffer.size(), requestBody.contentLength());
51     assertEquals(expected, buffer.readUtf8());
52   }
53 
threeParts()54   @Test public void threeParts() throws Exception {
55     String expected = ""
56         + "--123\r\n"
57         + "Content-Length: 5\r\n"
58         + "\r\n"
59         + "Quick\r\n"
60         + "--123\r\n"
61         + "Content-Length: 5\r\n"
62         + "\r\n"
63         + "Brown\r\n"
64         + "--123\r\n"
65         + "Content-Length: 3\r\n"
66         + "\r\n"
67         + "Fox\r\n"
68         + "--123--\r\n";
69 
70     RequestBody requestBody = new MultipartBuilder("123")
71         .addPart(RequestBody.create(null, "Quick"))
72         .addPart(RequestBody.create(null, "Brown"))
73         .addPart(RequestBody.create(null, "Fox"))
74         .build();
75 
76     assertEquals("multipart/mixed; boundary=123", requestBody.contentType().toString());
77 
78     Buffer buffer = new Buffer();
79     requestBody.writeTo(buffer);
80     assertEquals(112, requestBody.contentLength());
81     assertEquals(buffer.size(), requestBody.contentLength());
82     assertEquals(expected, buffer.readUtf8());
83   }
84 
fieldAndTwoFiles()85   @Test public void fieldAndTwoFiles() throws Exception {
86     String expected = ""
87         + "--AaB03x\r\n"
88         + "Content-Disposition: form-data; name=\"submit-name\"\r\n"
89         + "Content-Length: 5\r\n"
90         + "\r\n"
91         + "Larry\r\n"
92         + "--AaB03x\r\n"
93         + "Content-Disposition: form-data; name=\"files\"\r\n"
94         + "Content-Type: multipart/mixed; boundary=BbC04y\r\n"
95         + "Content-Length: 337\r\n"
96         + "\r\n"
97         + "--BbC04y\r\n"
98         + "Content-Disposition: file; filename=\"file1.txt\"\r\n"
99         + "Content-Type: text/plain; charset=utf-8\r\n"
100         + "Content-Length: 29\r\n"
101         + "\r\n"
102         + "... contents of file1.txt ...\r\n"
103         + "--BbC04y\r\n"
104         + "Content-Disposition: file; filename=\"file2.gif\"\r\n"
105         + "Content-Transfer-Encoding: binary\r\n"
106         + "Content-Type: image/gif\r\n"
107         + "Content-Length: 29\r\n"
108         + "\r\n"
109         + "... contents of file2.gif ...\r\n"
110         + "--BbC04y--\r\n"
111         + "\r\n"
112         + "--AaB03x--\r\n";
113 
114     RequestBody requestBody = new MultipartBuilder("AaB03x")
115         .type(MultipartBuilder.FORM)
116         .addFormDataPart("submit-name", "Larry")
117         .addFormDataPart("files", null,
118             new MultipartBuilder("BbC04y")
119                 .addPart(
120                     Headers.of("Content-Disposition", "file; filename=\"file1.txt\""),
121                     RequestBody.create(
122                         MediaType.parse("text/plain"), "... contents of file1.txt ..."))
123                 .addPart(
124                     Headers.of(
125                         "Content-Disposition", "file; filename=\"file2.gif\"",
126                         "Content-Transfer-Encoding", "binary"),
127                     RequestBody.create(
128                         MediaType.parse("image/gif"),
129                         "... contents of file2.gif ...".getBytes(UTF_8)))
130                 .build())
131         .build();
132 
133     assertEquals("multipart/form-data; boundary=AaB03x", requestBody.contentType().toString());
134 
135     Buffer buffer = new Buffer();
136     requestBody.writeTo(buffer);
137     assertEquals(568, requestBody.contentLength());
138     assertEquals(buffer.size(), requestBody.contentLength());
139     assertEquals(expected, buffer.readUtf8());
140   }
141 
stringEscapingIsWeird()142   @Test public void stringEscapingIsWeird() throws Exception {
143     String expected = ""
144         + "--AaB03x\r\n"
145         + "Content-Disposition: form-data; name=\"field with spaces\"; filename=\"filename with spaces.txt\"\r\n"
146         + "Content-Type: text/plain; charset=utf-8\r\n"
147         + "Content-Length: 4\r\n"
148         + "\r\n"
149         + "okay\r\n"
150         + "--AaB03x\r\n"
151         + "Content-Disposition: form-data; name=\"field with %22\"\r\n"
152         + "Content-Length: 1\r\n"
153         + "\r\n"
154         + "\"\r\n"
155         + "--AaB03x\r\n"
156         + "Content-Disposition: form-data; name=\"field with %22\"\r\n"
157         + "Content-Length: 3\r\n"
158         + "\r\n"
159         + "%22\r\n"
160         + "--AaB03x\r\n"
161         + "Content-Disposition: form-data; name=\"field with \u0391\"\r\n"
162         + "Content-Length: 5\r\n"
163         + "\r\n"
164         + "Alpha\r\n"
165         + "--AaB03x--\r\n";
166 
167     RequestBody requestBody = new MultipartBuilder("AaB03x")
168         .type(MultipartBuilder.FORM)
169         .addFormDataPart("field with spaces", "filename with spaces.txt",
170             RequestBody.create(MediaType.parse("text/plain; charset=utf-8"), "okay"))
171         .addFormDataPart("field with \"", "\"")
172         .addFormDataPart("field with %22", "%22")
173         .addFormDataPart("field with \u0391", "Alpha")
174         .build();
175 
176     Buffer buffer = new Buffer();
177     requestBody.writeTo(buffer);
178     assertEquals(expected, buffer.readUtf8());
179   }
180 
streamingPartHasNoLength()181   @Test public void streamingPartHasNoLength() throws Exception {
182     class StreamingBody extends RequestBody {
183       private final String body;
184 
185       StreamingBody(String body) {
186         this.body = body;
187       }
188 
189       @Override public MediaType contentType() {
190         return null;
191       }
192 
193       @Override public void writeTo(BufferedSink sink) throws IOException {
194         sink.writeUtf8(body);
195       }
196     }
197 
198     String expected = ""
199         + "--123\r\n"
200         + "Content-Length: 5\r\n"
201         + "\r\n"
202         + "Quick\r\n"
203         + "--123\r\n"
204         + "\r\n"
205         + "Brown\r\n"
206         + "--123\r\n"
207         + "Content-Length: 3\r\n"
208         + "\r\n"
209         + "Fox\r\n"
210         + "--123--\r\n";
211 
212     RequestBody requestBody = new MultipartBuilder("123")
213         .addPart(RequestBody.create(null, "Quick"))
214         .addPart(new StreamingBody("Brown"))
215         .addPart(RequestBody.create(null, "Fox"))
216         .build();
217 
218     assertEquals("multipart/mixed; boundary=123", requestBody.contentType().toString());
219 
220     Buffer buffer = new Buffer();
221     requestBody.writeTo(buffer);
222     assertEquals(expected, buffer.readUtf8());
223     assertEquals(-1, requestBody.contentLength());
224   }
225 
contentTypeHeaderIsForbidden()226   @Test public void contentTypeHeaderIsForbidden() throws Exception {
227     try {
228       new MultipartBuilder().addPart(
229           Headers.of("Content-Type", "text/plain"),
230           RequestBody.create(null, "Hello, World!"));
231       fail();
232     } catch (IllegalArgumentException expected) {
233     }
234   }
235 
contentLengthHeaderIsForbidden()236   @Test public void contentLengthHeaderIsForbidden() throws Exception {
237     try {
238       new MultipartBuilder().addPart(
239           Headers.of("Content-Length", "13"),
240           RequestBody.create(null, "Hello, World!"));
241       fail();
242     } catch (IllegalArgumentException expected) {
243     }
244   }
245 }
246