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.LazyList;
|
||||||
import org.eclipse.jetty.util.MultiMap;
|
import org.eclipse.jetty.util.MultiMap;
|
||||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||||
|
import org.eclipse.jetty.util.ReadLineInputStream;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.TypeUtil;
|
import org.eclipse.jetty.util.TypeUtil;
|
||||||
|
|
||||||
|
@ -118,7 +119,7 @@ public class MultiPartFilter implements Filter
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
InputStream in = new BufferedInputStream(request.getInputStream());
|
InputStream in = new ReadLineInputStream(request.getInputStream());
|
||||||
String content_type=srequest.getContentType();
|
String content_type=srequest.getContentType();
|
||||||
|
|
||||||
// TODO - handle encodings
|
// TODO - handle encodings
|
||||||
|
@ -144,8 +145,7 @@ public class MultiPartFilter implements Filter
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Get first boundary
|
// Get first boundary
|
||||||
byte[] bytes=TypeUtil.readLine(in);
|
String line=((ReadLineInputStream)in).readLine();
|
||||||
String line=bytes==null?null:new String(bytes,"UTF-8");
|
|
||||||
if(line==null || !line.equals(boundary))
|
if(line==null || !line.equals(boundary))
|
||||||
{
|
{
|
||||||
throw new IOException("Missing initial multi part boundary");
|
throw new IOException("Missing initial multi part boundary");
|
||||||
|
@ -164,14 +164,15 @@ public class MultiPartFilter implements Filter
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
// read a line
|
// read a line
|
||||||
bytes=TypeUtil.readLine(in);
|
line=((ReadLineInputStream)in).readLine();
|
||||||
if (bytes==null)
|
|
||||||
|
//No more input
|
||||||
|
if (line==null)
|
||||||
break outer;
|
break outer;
|
||||||
|
|
||||||
// If blank line, end of part headers
|
// If blank line, end of part headers
|
||||||
if(bytes.length==0)
|
if("".equals(line))
|
||||||
break;
|
break;
|
||||||
line=new String(bytes,"UTF-8");
|
|
||||||
|
|
||||||
// place part header key and value in map
|
// place part header key and value in map
|
||||||
int c=line.indexOf(':',0);
|
int c=line.indexOf(':',0);
|
||||||
|
@ -301,7 +302,14 @@ public class MultiPartFilter implements Filter
|
||||||
if(c==13||c==10)
|
if(c==13||c==10)
|
||||||
{
|
{
|
||||||
if(c==13)
|
if(c==13)
|
||||||
state=in.read();
|
{
|
||||||
|
in.mark(1);
|
||||||
|
int tmp=in.read();
|
||||||
|
if (tmp!=10)
|
||||||
|
in.reset();
|
||||||
|
else
|
||||||
|
state=tmp;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// look for boundary
|
// look for boundary
|
||||||
|
@ -359,7 +367,7 @@ public class MultiPartFilter implements Filter
|
||||||
|
|
||||||
if (file==null)
|
if (file==null)
|
||||||
{
|
{
|
||||||
bytes = ((ByteArrayOutputStream)out).toByteArray();
|
byte[] bytes = ((ByteArrayOutputStream)out).toByteArray();
|
||||||
params.add(name,bytes);
|
params.add(name,bytes);
|
||||||
if (type_content != null)
|
if (type_content != null)
|
||||||
params.add(name+CONTENT_TYPE_SUFFIX, type_content);
|
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
|
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||||
{
|
{
|
||||||
assertNotNull(req.getParameter("fileName"));
|
assertNotNull(req.getParameter("fileName"));
|
||||||
assertEquals("abc", req.getParameter("fileName"));
|
assertEquals(getServletContext().getAttribute("fileName"), req.getParameter("fileName"));
|
||||||
assertNotNull(req.getParameter("desc"));
|
assertNotNull(req.getParameter("desc"));
|
||||||
assertEquals("123", req.getParameter("desc"));
|
assertEquals(getServletContext().getAttribute("desc"), req.getParameter("desc"));
|
||||||
assertNotNull(req.getParameter("title"));
|
assertNotNull(req.getParameter("title"));
|
||||||
assertEquals("ttt", req.getParameter("title"));
|
assertEquals(getServletContext().getAttribute("title"), req.getParameter("title"));
|
||||||
super.doPost(req, resp);
|
super.doPost(req, resp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -277,6 +277,9 @@ public class MultipartFilterTest
|
||||||
HttpTester request = new HttpTester();
|
HttpTester request = new HttpTester();
|
||||||
HttpTester response = new HttpTester();
|
HttpTester response = new HttpTester();
|
||||||
tester.addServlet(BoundaryServlet.class,"/testb");
|
tester.addServlet(BoundaryServlet.class,"/testb");
|
||||||
|
tester.setAttribute("fileName", "abc");
|
||||||
|
tester.setAttribute("desc", "123");
|
||||||
|
tester.setAttribute("title", "ttt");
|
||||||
request.setMethod("POST");
|
request.setMethod("POST");
|
||||||
request.setVersion("HTTP/1.0");
|
request.setVersion("HTTP/1.0");
|
||||||
request.setHeader("Host","tester");
|
request.setHeader("Host","tester");
|
||||||
|
@ -315,6 +318,160 @@ public class MultipartFilterTest
|
||||||
assertTrue(response.getMethod()==null);
|
assertTrue(response.getMethod()==null);
|
||||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
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
|
* 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