Bug 475291 - [equinox jetty] doesn't support the servlet 3.0 multipart API

Signed-off-by: Raymond Auge <raymond.auge@liferay.com>
diff --git a/bundles/org.eclipse.equinox.http.jetty9/src/org/eclipse/equinox/http/jetty/internal/HttpServerManager.java b/bundles/org.eclipse.equinox.http.jetty9/src/org/eclipse/equinox/http/jetty/internal/HttpServerManager.java
index 358d275..2d8ef58 100644
--- a/bundles/org.eclipse.equinox.http.jetty9/src/org/eclipse/equinox/http/jetty/internal/HttpServerManager.java
+++ b/bundles/org.eclipse.equinox.http.jetty9/src/org/eclipse/equinox/http/jetty/internal/HttpServerManager.java
@@ -129,6 +129,7 @@
 		ServletContextHandler httpContext = createHttpContext(dictionary);
 		if (null != customizer)
 			httpContext = (ServletContextHandler) customizer.customizeContext(httpContext, dictionary);
+		setupMultiPartConfig(dictionary, holder);
 
 		httpContext.addServlet(holder, "/*"); //$NON-NLS-1$
 		server.setHandler(httpContext);
@@ -422,4 +423,10 @@
 		}
 		return directory.delete();
 	}
+
+	private void setupMultiPartConfig(@SuppressWarnings("rawtypes") Dictionary dictionary, ServletHolder holder) {
+		MultipartConfigElement multipartConfigElement = new MultipartConfigElement(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$
+		holder.getRegistration().setMultipartConfig(multipartConfigElement);
+	}
+
 }
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java
index e95db54..acfa623 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java
@@ -62,6 +62,7 @@
 import javax.servlet.http.HttpSessionEvent;
 import javax.servlet.http.HttpSessionIdListener;
 import javax.servlet.http.HttpSessionListener;
+import javax.servlet.http.Part;
 
 import junit.framework.TestCase;
 
@@ -1859,6 +1860,100 @@
 		Assert.assertEquals("p=3&p=4&p=1&p=2|p=1&p=2|3|[3, 4, 1, 2]", result);
 	}
 
+	private static String getSubmittedFileName(Part part) {
+		for (String cd : part.getHeader("content-disposition").split(";")) {
+			if (cd.trim().startsWith("filename")) {
+				String fileName = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
+				return fileName.substring(fileName.lastIndexOf('/') + 1).substring(fileName.lastIndexOf('\\') + 1); // MSIE fix.
+			}
+		}
+		return null;
+	}
+
+	/*
+	 * 3.1 file uploads
+	 */
+	public void test_Servlet16() throws Exception {
+		Servlet servlet = new HttpServlet() {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+				throws IOException, ServletException {
+
+				Part part = req.getPart("file");
+				Assert.assertNotNull(part);
+
+				String submittedFileName = part.getSubmittedFileName();
+				String contentType = part.getContentType();
+				long size = part.getSize();
+
+				PrintWriter writer = resp.getWriter();
+
+				writer.write(submittedFileName);
+				writer.write("|");
+				writer.write(contentType);
+				writer.write("|" + size);
+			}
+		};
+
+		Dictionary<String, Object> props = new Hashtable<String, Object>();
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "S16");
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/Servlet16/*");
+		registrations.add(getBundleContext().registerService(Servlet.class, servlet, props));
+
+		Map<String, List<Object>> map = new HashMap<String, List<Object>>();
+
+		map.put("file", Arrays.<Object>asList(getClass().getResource("resource1.txt")));
+
+		Map<String, List<String>> result = requestAdvisor.upload("Servlet16/do", map);
+
+		Assert.assertEquals("200", result.get("responseCode").get(0));
+		Assert.assertEquals("resource1.txt|text/plain|1", result.get("responseBody").get(0));
+	}
+
+	/*
+	 * 3.0 file uploads
+	 */
+	public void test_Servlet17() throws Exception {
+		Servlet servlet = new HttpServlet() {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+				throws IOException, ServletException {
+
+				Part part = req.getPart("file");
+				Assert.assertNotNull(part);
+
+				String submittedFileName = getSubmittedFileName(part);
+				String contentType = part.getContentType();
+				long size = part.getSize();
+
+				PrintWriter writer = resp.getWriter();
+
+				writer.write(submittedFileName);
+				writer.write("|");
+				writer.write(contentType);
+				writer.write("|" + size);
+			}
+		};
+
+		Dictionary<String, Object> props = new Hashtable<String, Object>();
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "S16");
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/Servlet16/*");
+		registrations.add(getBundleContext().registerService(Servlet.class, servlet, props));
+
+		Map<String, List<Object>> map = new HashMap<String, List<Object>>();
+
+		map.put("file", Arrays.<Object>asList(getClass().getResource("blue.png")));
+
+		Map<String, List<String>> result = requestAdvisor.upload("Servlet16/do", map);
+
+		Assert.assertEquals("200", result.get("responseCode").get(0));
+		Assert.assertEquals("blue.png|image/png|292", result.get("responseBody").get(0));
+	}
+
 	public void test_ServletContext1() throws Exception {
 		String expected = "/org/eclipse/equinox/http/servlet/tests/tb1/resource1.txt";
 		String actual;
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/blue.png b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/blue.png
new file mode 100644
index 0000000..f1ad5b2
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/blue.png
Binary files differ
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/ServletRequestAdvisor.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/ServletRequestAdvisor.java
index ec7c80d..9fdc89c 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/ServletRequestAdvisor.java
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/ServletRequestAdvisor.java
@@ -13,9 +13,13 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
 
 import java.net.HttpURLConnection;
 import java.net.URL;
+import java.net.URLConnection;
 
 import java.util.Arrays;
 import java.util.Collections;
@@ -132,4 +136,119 @@
 			stream.close();
 		}
 	}
+
+	public Map<String, List<String>> upload(String value, Map<String, List<Object>> headers) throws IOException {
+		String spec = createUrlSpec(value);
+		log("Requesting " + spec); //$NON-NLS-1$
+		URL url = new URL(spec);
+		HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+
+		connection.setInstanceFollowRedirects(false);
+		connection.setConnectTimeout(150 * 1000);
+		connection.setReadTimeout(150 * 1000);
+
+		if (headers != null) {
+			for(Map.Entry<String, List<Object>> entry : headers.entrySet()) {
+				for(Object entryValue : entry.getValue()) {
+					if (entryValue instanceof String) {
+						connection.setRequestProperty(entry.getKey(), (String)entryValue);
+					}
+					else if (entryValue instanceof URL) {
+						uploadFileConnection(connection, entry.getKey(), (URL)entryValue);
+					}
+					else {
+						throw new IllegalArgumentException("only supports strings and files");
+					}
+				}
+			}
+		}
+
+		int responseCode = connection.getResponseCode();
+
+		Map<String, List<String>> map = new HashMap<String, List<String>>(connection.getHeaderFields());
+		map.put("responseCode", Collections.singletonList(String.valueOf(responseCode)));
+
+		InputStream stream;
+
+		if (responseCode >= 400) {
+			stream = connection.getErrorStream();
+		}
+		else {
+			stream = connection.getInputStream();
+		}
+
+		try {
+			map.put("responseBody", Arrays.asList(drain(stream)));
+			return map;
+		} finally {
+			stream.close();
+		}
+	}
+
+	private void uploadFileConnection(HttpURLConnection connection, String param, URL file)
+		throws IOException {
+
+		String fileName = file.getPath();
+		fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
+		connection.setDoOutput(true);
+
+		String boundary = Long.toHexString(System.currentTimeMillis());
+		String CRLF = "\r\n";
+		connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
+
+		InputStream input = null;
+		OutputStream output = null;
+		PrintWriter writer = null;
+
+		try {
+			output = connection.getOutputStream();
+			writer = new PrintWriter(new OutputStreamWriter(output, "UTF-8"), true);
+
+			writer.append("--" + boundary);
+			writer.append(CRLF);
+			writer.append("Content-Disposition: form-data; name=\"file\"; filename=\"");
+			writer.append(fileName);
+			writer.append("\"");
+			writer.append(CRLF);
+			writer.append("Content-Type: ");
+			String contentType = URLConnection.guessContentTypeFromName(fileName);
+			writer.append(contentType);
+			writer.append(CRLF);
+			if (!contentType.startsWith("text/")) {
+				writer.append("Content-Transfer-Encoding: binary");
+				writer.append(CRLF);
+			}
+			writer.append(CRLF);
+			writer.flush();
+
+			byte[] buf = new byte[64];
+			input = file.openStream();
+			int c = 0;
+			while ((c = input.read(buf, 0, buf.length)) > 0) {
+				output.write(buf, 0, c);
+				output.flush();
+			}
+
+			output.flush(); // Important before continuing with writer!
+			writer.append(CRLF); // CRLF is important! It indicates end of boundary.
+			writer.flush();
+
+			// End of multipart/form-data.
+			writer.append("--" + boundary + "--");
+			writer.append(CRLF);
+			writer.flush();
+		}
+		finally {
+			if (input != null) {
+				input.close();
+			}
+			if (output != null) {
+				output.close();
+			}
+			if (writer != null) {
+				writer.close();
+			}
+		}
+	}
+
 }