JETTY-1252 Handle more multipart transfer encodings
git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@2278 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
parent
f0fdcb065d
commit
3af3ddd0ff
|
@ -38,6 +38,7 @@ jetty-7.2-SNAPSHOT
|
|||
+ JETTY-1245 Do not use direct buffers with NIO SSL
|
||||
+ JETTY-1249 Apply max idle time to all connectors
|
||||
+ JETTY-1250 Parallel start of HandlerCollection
|
||||
+ JETTY-1252 Handle more multipart transfer encodings
|
||||
+ JETTY-1256 annotation jars from Orbit
|
||||
+ JETTY-1261 errant listener usage in StandardDescriptorProcessor
|
||||
+ JETTY-1263 JDBCSessionIdManager table creation fails on Oracle
|
||||
|
|
|
@ -81,6 +81,11 @@
|
|||
<artifactId>servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>test-jetty-servlet</artifactId>
|
||||
|
|
|
@ -17,7 +17,9 @@ import java.io.BufferedOutputStream;
|
|||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
|
@ -38,6 +40,7 @@ import javax.servlet.ServletResponse;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64InputStream;
|
||||
import org.eclipse.jetty.util.LazyList;
|
||||
import org.eclipse.jetty.util.MultiMap;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
|
@ -95,7 +98,7 @@ public class MultiPartFilter implements Filter
|
|||
return;
|
||||
}
|
||||
|
||||
BufferedInputStream in = new BufferedInputStream(request.getInputStream());
|
||||
InputStream in = new BufferedInputStream(request.getInputStream());
|
||||
String content_type=srequest.getContentType();
|
||||
|
||||
// TODO - handle encodings
|
||||
|
@ -127,6 +130,7 @@ public class MultiPartFilter implements Filter
|
|||
// Read each part
|
||||
boolean lastPart=false;
|
||||
String content_disposition=null;
|
||||
String content_transfer_encoding=null;
|
||||
|
||||
outer:while(!lastPart)
|
||||
{
|
||||
|
@ -150,6 +154,8 @@ public class MultiPartFilter implements Filter
|
|||
String value=line.substring(c+1,line.length()).trim();
|
||||
if(key.equals("content-disposition"))
|
||||
content_disposition=value;
|
||||
else if(key.equals("content-transfer-encoding"))
|
||||
content_transfer_encoding=value;
|
||||
}
|
||||
}
|
||||
// Extract content-disposition
|
||||
|
@ -212,12 +218,37 @@ public class MultiPartFilter implements Filter
|
|||
request.setAttribute(FILES,files);
|
||||
}
|
||||
files.add(file);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
out=new ByteArrayOutputStream();
|
||||
|
||||
|
||||
if ("base64".equalsIgnoreCase(content_transfer_encoding))
|
||||
in = new Base64InputStream(in);
|
||||
else if ("quoted-printable".equalsIgnoreCase(content_transfer_encoding))
|
||||
{
|
||||
in = new FilterInputStream(in)
|
||||
{
|
||||
@Override
|
||||
public int read() throws IOException
|
||||
{
|
||||
int c = in.read();
|
||||
if (c >= 0 && c == '=')
|
||||
{
|
||||
int hi = in.read();
|
||||
int lo = in.read();
|
||||
if (hi < 0 || lo < 0)
|
||||
{
|
||||
throw new IOException("Unexpected end to quoted-printable byte");
|
||||
}
|
||||
char[] chars = new char[] { (char)hi, (char)lo };
|
||||
c = Integer.parseInt(new String(chars),16);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int state=-2;
|
||||
int c;
|
||||
boolean cr=false;
|
||||
|
|
|
@ -128,6 +128,77 @@ public class MultipartFilterTest
|
|||
assertTrue(response.getContent().indexOf("brown cow")>=0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test multipart with parts encoded in base64 (RFC1521 section 5)
|
||||
*/
|
||||
@Test
|
||||
public void testPostWithContentTransferEncodingBase64() throws Exception {
|
||||
// generated and parsed test
|
||||
HttpTester request = new HttpTester();
|
||||
HttpTester response = new HttpTester();
|
||||
|
||||
// test GET
|
||||
request.setMethod("POST");
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host","tester");
|
||||
request.setURI("/context/dump");
|
||||
|
||||
String boundary="XyXyXy";
|
||||
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
|
||||
|
||||
// part content is "How now brown cow." run through a base64 encoder
|
||||
String content = "--" + boundary + "\r\n"+
|
||||
"Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r\n"+
|
||||
"Content-Transfer-Encoding: base64\r\n"+
|
||||
"Content-Type: application/octet-stream\r\n\r\n"+
|
||||
"SG93IG5vdyBicm93biBjb3cuCg=="+
|
||||
"\r\n--" + boundary + "--\r\n\r\n";
|
||||
|
||||
request.setContent(content);
|
||||
|
||||
response.parse(tester.getResponses(request.generate()));
|
||||
assertTrue(response.getMethod()==null);
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
assertTrue(response.getContent().indexOf("brown cow")>=0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test multipart with parts encoded in quoted-printable (RFC1521 section 5)
|
||||
*/
|
||||
@Test
|
||||
public void testPostWithContentTransferEncodingQuotedPrintable() throws Exception {
|
||||
// generated and parsed test
|
||||
HttpTester request = new HttpTester();
|
||||
HttpTester response = new HttpTester();
|
||||
|
||||
// test GET
|
||||
request.setMethod("POST");
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host","tester");
|
||||
request.setURI("/context/dump");
|
||||
|
||||
String boundary="XyXyXy";
|
||||
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
|
||||
|
||||
/*
|
||||
* Part content is "How now brown cow." run through Apache Commons Codec
|
||||
* quoted printable encoding.
|
||||
*/
|
||||
String content = "--" + boundary + "\r\n"+
|
||||
"Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r\n"+
|
||||
"Content-Transfer-Encoding: quoted-printable\r\n"+
|
||||
"Content-Type: application/octet-stream\r\n\r\n"+
|
||||
"=48=6F=77=20=6E=6F=77=20=62=72=6F=77=6E=20=63=6F=77=2E"+
|
||||
"\r\n--" + boundary + "--\r\n\r\n";
|
||||
|
||||
request.setContent(content);
|
||||
|
||||
response.parse(tester.getResponses(request.generate()));
|
||||
assertTrue(response.getMethod()==null);
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
assertTrue(response.getContent().indexOf("brown cow")>=0);
|
||||
}
|
||||
|
||||
public static class DumpServlet extends HttpServlet
|
||||
{
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
|
||||
package org.eclipse.jetty.servlets;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlet.FilterMapping;
|
||||
import org.eclipse.jetty.testing.HttpTester;
|
||||
import org.eclipse.jetty.testing.ServletTester;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class MultipartFilterTest
|
||||
{
|
||||
private File _dir;
|
||||
private ServletTester tester;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
_dir = File.createTempFile("testmultupart",null);
|
||||
assertTrue(_dir.delete());
|
||||
assertTrue(_dir.mkdir());
|
||||
_dir.deleteOnExit();
|
||||
assertTrue(_dir.isDirectory());
|
||||
|
||||
tester=new ServletTester();
|
||||
tester.setContextPath("/context");
|
||||
tester.setResourceBase(_dir.getCanonicalPath());
|
||||
tester.addServlet(DumpServlet.class, "/");
|
||||
tester.addFilter(MultiPartFilter.class,"/*",FilterMapping.DEFAULT);
|
||||
tester.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception
|
||||
{
|
||||
tester.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadPost() throws Exception
|
||||
{
|
||||
// generated and parsed test
|
||||
HttpTester request = new HttpTester();
|
||||
HttpTester response = new HttpTester();
|
||||
|
||||
// test GET
|
||||
request.setMethod("POST");
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host","tester");
|
||||
request.setURI("/context/dump");
|
||||
|
||||
String boundary="XyXyXy";
|
||||
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
|
||||
|
||||
|
||||
String content = "--" + boundary + "\r\n"+
|
||||
"Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r\n"+
|
||||
"Content-Type: application/octet-stream\r\n\r\n"+
|
||||
"How now brown cow."+
|
||||
"\r\n--" + boundary + "-\r\n\r\n";
|
||||
|
||||
request.setContent(content);
|
||||
|
||||
|
||||
response.parse(tester.getResponses(request.generate()));
|
||||
assertTrue(response.getMethod()==null);
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPost() throws Exception
|
||||
{
|
||||
// generated and parsed test
|
||||
HttpTester request = new HttpTester();
|
||||
HttpTester response = new HttpTester();
|
||||
|
||||
// test GET
|
||||
request.setMethod("POST");
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host","tester");
|
||||
request.setURI("/context/dump");
|
||||
|
||||
String boundary="XyXyXy";
|
||||
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
|
||||
|
||||
|
||||
String content = "--" + boundary + "\r\n"+
|
||||
"Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r\n"+
|
||||
"Content-Type: application/octet-stream\r\n\r\n"+
|
||||
"How now brown cow."+
|
||||
"\r\n--" + boundary + "--\r\n\r\n";
|
||||
|
||||
request.setContent(content);
|
||||
|
||||
response.parse(tester.getResponses(request.generate()));
|
||||
assertTrue(response.getMethod()==null);
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
assertTrue(response.getContent().indexOf("brown cow")>=0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test multipart with parts encoded in base64 (RFC1521 section 5)
|
||||
*/
|
||||
@Test
|
||||
public void testPostWithContentTransferEncodingBase64() throws Exception {
|
||||
// generated and parsed test
|
||||
HttpTester request = new HttpTester();
|
||||
HttpTester response = new HttpTester();
|
||||
|
||||
// test GET
|
||||
request.setMethod("POST");
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host","tester");
|
||||
request.setURI("/context/dump");
|
||||
|
||||
String boundary="XyXyXy";
|
||||
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
|
||||
|
||||
// part content is "How now brown cow." run through a base64 encoder
|
||||
String content = "--" + boundary + "\r\n"+
|
||||
"Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r\n"+
|
||||
"Content-Transfer-Encoding: base64\r\n"+
|
||||
"Content-Type: application/octet-stream\r\n\r\n"+
|
||||
"SG93IG5vdyBicm93biBjb3cuCg=="+
|
||||
"\r\n--" + boundary + "--\r\n\r\n";
|
||||
|
||||
request.setContent(content);
|
||||
|
||||
response.parse(tester.getResponses(request.generate()));
|
||||
assertTrue(response.getMethod()==null);
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
assertTrue(response.getContent().indexOf("brown cow")>=0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test multipart with parts encoded in quoted-printable (RFC1521 section 5)
|
||||
*/
|
||||
@Test
|
||||
public void testPostWithContentTransferEncodingQuotedPrintable() throws Exception {
|
||||
// generated and parsed test
|
||||
HttpTester request = new HttpTester();
|
||||
HttpTester response = new HttpTester();
|
||||
|
||||
// test GET
|
||||
request.setMethod("POST");
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host","tester");
|
||||
request.setURI("/context/dump");
|
||||
|
||||
String boundary="XyXyXy";
|
||||
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
|
||||
|
||||
/*
|
||||
* Part content is "How now brown cow." run through Apache Commons Codec
|
||||
* quoted printable encoding.
|
||||
*/
|
||||
String content = "--" + boundary + "\r\n"+
|
||||
"Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r\n"+
|
||||
"Content-Transfer-Encoding: quoted-printable\r\n"+
|
||||
"Content-Type: application/octet-stream\r\n\r\n"+
|
||||
"=48=6F=77=20=6E=6F=77=20=62=72=6F=77=6E=20=63=6F=77=2E"+
|
||||
"\r\n--" + boundary + "--\r\n\r\n";
|
||||
|
||||
request.setContent(content);
|
||||
|
||||
response.parse(tester.getResponses(request.generate()));
|
||||
assertTrue(response.getMethod()==null);
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
assertTrue(response.getContent().indexOf("brown cow")>=0);
|
||||
}
|
||||
|
||||
public static class DumpServlet extends HttpServlet
|
||||
{
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||
*/
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||
{
|
||||
resp.getWriter().println((IO.toString(new FileInputStream((File)req.getAttribute("fileup")))));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue