395215 Multipart mime with just LF and no CRLF
This commit is contained in:
parent
769260f251
commit
2723c415bf
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue