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