1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.fileupload;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.ByteArrayOutputStream;
21  import java.io.FilterInputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStreamWriter;
25  import java.util.Iterator;
26  import java.util.List;
27  import javax.servlet.http.HttpServletRequest;
28  
29  import org.apache.commons.fileupload.FileUploadBase.IOFileUploadException;
30  import org.apache.commons.fileupload.disk.DiskFileItemFactory;
31  import org.apache.commons.fileupload.servlet.ServletFileUpload;
32  import org.apache.commons.fileupload.servlet.ServletRequestContext;
33  
34  import junit.framework.TestCase;
35  
36  
37  /**
38   * Unit test for items with varying sizes.
39   */
40  public class StreamingTest extends TestCase
41  {
42      /**
43       * Tests a file upload with varying file sizes.
44       */
45      public void testFileUpload()
46              throws IOException, FileUploadException
47      {
48      	byte[] request = newRequest();
49          List fileItems = parseUpload(request);
50          Iterator fileIter = fileItems.iterator();
51          int add = 16;
52          int num = 0;
53          for (int i = 0;  i < 16384;  i += add) {
54              if (++add == 32) {
55                  add = 16;
56              }
57              FileItem item = (FileItem) fileIter.next();
58              assertEquals("field" + (num++), item.getFieldName());
59              byte[] bytes = item.get();
60              assertEquals(i, bytes.length);
61              for (int j = 0;  j < i;  j++) {
62                  assertEquals((byte) j, bytes[j]);
63              }
64          }
65          assertTrue(!fileIter.hasNext());
66      }
67  
68  
69      /**
70       * Tests, whether an invalid request throws a proper
71       * exception.
72       */
73      public void testFileUploadException()
74      		throws IOException, FileUploadException {
75      	byte[] request = newRequest();
76      	byte[] invalidRequest = new byte[request.length-11];
77      	System.arraycopy(request, 0, invalidRequest, 0, request.length-11);
78      	try {
79      		parseUpload(invalidRequest);
80  	        fail("Expected EndOfStreamException");
81      	} catch (IOFileUploadException e) {
82      		assertTrue(e.getCause() instanceof MultipartStream.MalformedStreamException);
83      	}
84      }
85  
86      /**
87       * Tests, whether an IOException is properly delegated.
88       */
89      public void testIOException()
90      		throws IOException {
91      	byte[] request = newRequest();
92      	InputStream stream = new FilterInputStream(new ByteArrayInputStream(request)){
93      		private int num;
94      		public int read() throws IOException {
95      			if (++num > 123) {
96      				throw new IOException("123");
97      			}
98      			return super.read();
99      		}
100 			public int read(byte[] pB, int pOff, int pLen)
101 					throws IOException {
102 				for (int i = 0;  i < pLen;  i++) {
103 					int res = read();
104 					if (res == -1) {
105 						return i == 0 ? -1 : i;
106 					}
107 					pB[pOff+i] = (byte) res;
108 				}
109 				return pLen;
110 			}
111     	};
112     	try {
113     		parseUpload(stream, request.length);
114     		fail("Expected IOException");
115     	} catch (FileUploadException e) {
116     		assertTrue(e.getCause() instanceof IOException);
117     		assertEquals("123", e.getCause().getMessage());
118         }     
119     }         
120 
121     /**
122      * Test for FILEUPLOAD-135
123      */
124     public void testFILEUPLOAD135()
125             throws IOException, FileUploadException
126     {
127         byte[] request = newShortRequest();
128         final ByteArrayInputStream bais = new ByteArrayInputStream(request);
129         List fileItems = parseUpload(new InputStream() {
130             public int read()
131             throws IOException
132             {
133                 return bais.read();
134             }
135             public int read(byte b[], int off, int len) throws IOException 
136             {
137                 return bais.read(b, off, Math.min(len, 3));
138             }
139 
140         }, request.length);
141         Iterator fileIter = fileItems.iterator();
142         assertTrue(fileIter.hasNext());
143         FileItem item = (FileItem) fileIter.next();
144         assertEquals("field", item.getFieldName());
145         byte[] bytes = item.get();
146         assertEquals(3, bytes.length);
147         assertEquals((byte)'1', bytes[0]);
148         assertEquals((byte)'2', bytes[1]);
149         assertEquals((byte)'3', bytes[2]);
150         assertTrue(!fileIter.hasNext());
151     }
152 
153     private List parseUpload(byte[] bytes) throws FileUploadException {
154     	return parseUpload(new ByteArrayInputStream(bytes), bytes.length);
155     }
156 
157     private List parseUpload(InputStream pStream, int pLength)
158     		throws FileUploadException {
159         String contentType = "multipart/form-data; boundary=---1234";
160 
161         FileUploadBase upload = new ServletFileUpload();
162         upload.setFileItemFactory(new DiskFileItemFactory());
163         HttpServletRequest request = new MockHttpServletRequest(pStream,
164         		pLength, contentType);
165 
166         List fileItems = upload.parseRequest(new ServletRequestContext(request));
167         return fileItems;
168     }
169 
170     private String getHeader(String pField) {
171         return "-----1234\r\n"
172             + "Content-Disposition: form-data; name=\"" + pField + "\"\r\n"
173             + "\r\n";
174 
175     }
176 
177     private String getFooter() {
178         return "-----1234--\r\n";
179     }
180 
181     private byte[] newShortRequest() throws IOException {
182         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
183         final OutputStreamWriter osw = new OutputStreamWriter(baos, "US-ASCII");
184         osw.write(getHeader("field"));
185         osw.write("123");
186         osw.write("\r\n");
187         osw.write(getFooter());
188         osw.close();
189         return baos.toByteArray();
190     }
191 
192     private byte[] newRequest() throws IOException {
193         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
194         final OutputStreamWriter osw = new OutputStreamWriter(baos, "US-ASCII");
195         int add = 16;
196         int num = 0;
197         for (int i = 0;  i < 16384;  i += add) {
198             if (++add == 32) {
199                 add = 16;
200             }
201             osw.write(getHeader("field" + (num++)));
202             osw.flush();
203             for (int j = 0;  j < i;  j++) {
204                 baos.write((byte) j);
205             }
206             osw.write("\r\n");
207         }
208         osw.write(getFooter());
209         osw.close();
210         return baos.toByteArray();
211     }
212 }