395215 Multipart mime with just LF and no CRLF

This commit is contained in:
Jan Bartel 2012-11-29 16:48:21 +11:00
parent 769260f251
commit 2723c415bf
3 changed files with 305 additions and 12 deletions

View File

@ -53,6 +53,7 @@ import org.eclipse.jetty.util.B64Code;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.QuotedStringTokenizer;
import org.eclipse.jetty.util.ReadLineInputStream;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
@ -118,7 +119,7 @@ public class MultiPartFilter implements Filter
return;
}
InputStream in = new BufferedInputStream(request.getInputStream());
InputStream in = new ReadLineInputStream(request.getInputStream());
String content_type=srequest.getContentType();
// TODO - handle encodings
@ -144,8 +145,7 @@ public class MultiPartFilter implements Filter
try
{
// Get first boundary
byte[] bytes=TypeUtil.readLine(in);
String line=bytes==null?null:new String(bytes,"UTF-8");
String line=((ReadLineInputStream)in).readLine();
if(line==null || !line.equals(boundary))
{
throw new IOException("Missing initial multi part boundary");
@ -164,14 +164,15 @@ public class MultiPartFilter implements Filter
while(true)
{
// read a line
bytes=TypeUtil.readLine(in);
if (bytes==null)
line=((ReadLineInputStream)in).readLine();
//No more input
if (line==null)
break outer;
// If blank line, end of part headers
if(bytes.length==0)
if("".equals(line))
break;
line=new String(bytes,"UTF-8");
// place part header key and value in map
int c=line.indexOf(':',0);
@ -301,7 +302,14 @@ public class MultiPartFilter implements Filter
if(c==13||c==10)
{
if(c==13)
state=in.read();
{
in.mark(1);
int tmp=in.read();
if (tmp!=10)
in.reset();
else
state=tmp;
}
break;
}
// look for boundary
@ -359,7 +367,7 @@ public class MultiPartFilter implements Filter
if (file==null)
{
bytes = ((ByteArrayOutputStream)out).toByteArray();
byte[] bytes = ((ByteArrayOutputStream)out).toByteArray();
params.add(name,bytes);
if (type_content != null)
params.add(name+CONTENT_TYPE_SUFFIX, type_content);

View File

@ -53,11 +53,11 @@ public class MultipartFilterTest
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
assertNotNull(req.getParameter("fileName"));
assertEquals("abc", req.getParameter("fileName"));
assertEquals(getServletContext().getAttribute("fileName"), req.getParameter("fileName"));
assertNotNull(req.getParameter("desc"));
assertEquals("123", req.getParameter("desc"));
assertEquals(getServletContext().getAttribute("desc"), req.getParameter("desc"));
assertNotNull(req.getParameter("title"));
assertEquals("ttt", req.getParameter("title"));
assertEquals(getServletContext().getAttribute("title"), req.getParameter("title"));
super.doPost(req, resp);
}
}
@ -277,6 +277,9 @@ public class MultipartFilterTest
HttpTester request = new HttpTester();
HttpTester response = new HttpTester();
tester.addServlet(BoundaryServlet.class,"/testb");
tester.setAttribute("fileName", "abc");
tester.setAttribute("desc", "123");
tester.setAttribute("title", "ttt");
request.setMethod("POST");
request.setVersion("HTTP/1.0");
request.setHeader("Host","tester");
@ -315,6 +318,160 @@ public class MultipartFilterTest
assertTrue(response.getMethod()==null);
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
}
@Test
public void testLFOnlyRequest() throws Exception
{
String boundary="XyXyXy";
// generated and parsed test
HttpTester request = new HttpTester();
HttpTester response = new HttpTester();
tester.addServlet(BoundaryServlet.class,"/testb");
tester.setAttribute("fileName", "abc");
tester.setAttribute("desc", "123");
tester.setAttribute("title", "ttt");
request.setMethod("POST");
request.setVersion("HTTP/1.0");
request.setHeader("Host","tester");
request.setURI("/context/testb");
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
String content = "--XyXyXy\n"+
"Content-Disposition: form-data; name=\"fileName\"\n"+
"Content-Type: text/plain; charset=US-ASCII\n"+
"Content-Transfer-Encoding: 8bit\n"+
"\n"+
"abc\n"+
"--XyXyXy\n"+
"Content-Disposition: form-data; name=\"desc\"\n"+
"Content-Type: text/plain; charset=US-ASCII\n"+
"Content-Transfer-Encoding: 8bit\n"+
"\n"+
"123\n"+
"--XyXyXy\n"+
"Content-Disposition: form-data; name=\"title\"\n"+
"Content-Type: text/plain; charset=US-ASCII\n"+
"Content-Transfer-Encoding: 8bit\n"+
"\n"+
"ttt\n"+
"--XyXyXy\n"+
"Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\n"+
"Content-Type: application/octet-stream\n"+
"Content-Transfer-Encoding: binary\n"+
"\n"+
"000\n"+
"--XyXyXy--\n";
request.setContent(content);
response.parse(tester.getResponses(request.generate()));
assertTrue(response.getMethod()==null);
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
}
@Test
public void testCROnlyRequest() throws Exception
{
String boundary="XyXyXy";
// generated and parsed test
HttpTester request = new HttpTester();
HttpTester response = new HttpTester();
tester.addServlet(BoundaryServlet.class,"/testb");
tester.setAttribute("fileName", "abc");
tester.setAttribute("desc", "123");
tester.setAttribute("title", "ttt");
request.setMethod("POST");
request.setVersion("HTTP/1.0");
request.setHeader("Host","tester");
request.setURI("/context/testb");
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
String content = "--XyXyXy\r"+
"Content-Disposition: form-data; name=\"fileName\"\r"+
"Content-Type: text/plain; charset=US-ASCII\r"+
"Content-Transfer-Encoding: 8bit\r"+
"\r"+
"abc\r"+
"--XyXyXy\r"+
"Content-Disposition: form-data; name=\"desc\"\r"+
"Content-Type: text/plain; charset=US-ASCII\r"+
"Content-Transfer-Encoding: 8bit\r"+
"\r"+
"123\r"+
"--XyXyXy\r"+
"Content-Disposition: form-data; name=\"title\"\r"+
"Content-Type: text/plain; charset=US-ASCII\r"+
"Content-Transfer-Encoding: 8bit\r"+
"\r"+
"ttt\r"+
"--XyXyXy\r"+
"Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r"+
"Content-Type: application/octet-stream\r"+
"Content-Transfer-Encoding: binary\r"+
"\r"+
"000\r"+
"--XyXyXy--\r";
request.setContent(content);
response.parse(tester.getResponses(request.generate()));
assertTrue(response.getMethod()==null);
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
}
@Test
public void testCROnlyWithEmbeddedLFRequest() throws Exception
{
String boundary="XyXyXy";
// generated and parsed test
HttpTester request = new HttpTester();
HttpTester response = new HttpTester();
tester.addServlet(BoundaryServlet.class,"/testb");
tester.setAttribute("fileName", "\nabc\n");
tester.setAttribute("desc", "\n123\n");
tester.setAttribute("title", "\nttt\n");
request.setMethod("POST");
request.setVersion("HTTP/1.0");
request.setHeader("Host","tester");
request.setURI("/context/testb");
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
String content = "--XyXyXy\r"+
"Content-Disposition: form-data; name=\"fileName\"\r"+
"Content-Type: text/plain; charset=US-ASCII\r"+
"Content-Transfer-Encoding: 8bit\r"+
"\r"+
"\nabc\n"+
"\r"+
"--XyXyXy\r"+
"Content-Disposition: form-data; name=\"desc\"\r"+
"Content-Type: text/plain; charset=US-ASCII\r"+
"Content-Transfer-Encoding: 8bit\r"+
"\r"+
"\n123\n"+
"\r"+
"--XyXyXy\r"+
"Content-Disposition: form-data; name=\"title\"\r"+
"Content-Type: text/plain; charset=US-ASCII\r"+
"Content-Transfer-Encoding: 8bit\r"+
"\r"+
"\nttt\n"+
"\r"+
"--XyXyXy\r"+
"Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r"+
"Content-Type: application/octet-stream\r"+
"Content-Transfer-Encoding: binary\r"+
"\r"+
"000\r"+
"--XyXyXy--\r";
request.setContent(content);
response.parse(tester.getResponses(request.generate()));
assertTrue(response.getMethod()==null);
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
}
/*
* see the testParameterMap test

View File

@ -0,0 +1,128 @@
//
// ========================================================================
// Copyright (c) 1995-2012 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.util;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* ReadLineInputStream
*
* Read from an input stream, accepting CR/LF, LF or just CR.
*/
public class ReadLineInputStream extends BufferedInputStream
{
boolean _seenCRLF;
boolean _skipLF;
public ReadLineInputStream(InputStream in)
{
super(in);
}
public ReadLineInputStream(InputStream in, int size)
{
super(in,size);
}
public String readLine() throws IOException
{
mark(buf.length);
while (true)
{
int b=super.read();
if (b==-1)
{
markpos=-1;
return null;
}
if (b=='\r')
{
int p=pos;
// if we have seen CRLF before, hungrily consume LF
if (_seenCRLF && pos<count)
{
if (buf[pos]=='\n')
pos+=1;
}
else
_skipLF=true;
int m=markpos;
markpos=-1;
return new String(buf,m,p-m-1,StringUtil.__UTF8_CHARSET);
}
if (b=='\n')
{
if (_skipLF)
{
_skipLF=false;
_seenCRLF=true;
markpos++;
continue;
}
int m=markpos;
markpos=-1;
return new String(buf,m,pos-m-1,StringUtil.__UTF8_CHARSET);
}
}
}
@Override
public synchronized int read() throws IOException
{
int b = super.read();
if (_skipLF)
{
_skipLF=false;
if (_seenCRLF && b=='\n')
b=super.read();
}
return b;
}
@Override
public synchronized int read(byte[] buf, int off, int len) throws IOException
{
if (_skipLF && len>0)
{
_skipLF=false;
if (_seenCRLF)
{
int b = super.read();
if (b==-1)
return -1;
if (b!='\n')
{
buf[off]=(byte)(0xff&b);
return 1+super.read(buf,off+1,len-1);
}
}
}
return super.read(buf,off,len);
}
}