mirror of
https://github.com/jetty/jetty.project.git
synced 2025-02-28 19:09:10 +00:00
Merge branch 'jetty-7' into jetty-7-graceful-stop
This commit is contained in:
commit
a5c1ced201
@ -467,7 +467,10 @@ public abstract class AbstractHttpConnection extends AbstractConnection
|
||||
if (info==null && !_request.getMethod().equals(HttpMethods.CONNECT))
|
||||
{
|
||||
if (_uri.getScheme()!=null && _uri.getHost()!=null)
|
||||
{
|
||||
info="/";
|
||||
_request.setRequestURI("");
|
||||
}
|
||||
else
|
||||
throw new HttpException(400);
|
||||
}
|
||||
|
@ -29,10 +29,8 @@ import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
@ -479,18 +477,11 @@ public class ServletHandler extends ScopedHandler
|
||||
}
|
||||
else if (th instanceof ServletException)
|
||||
{
|
||||
LOG.debug(th);
|
||||
LOG.warn(th);
|
||||
Throwable cause=((ServletException)th).getRootCause();
|
||||
if (cause!=null)
|
||||
th=cause;
|
||||
}
|
||||
else if (th instanceof RuntimeIOException)
|
||||
{
|
||||
LOG.debug(th);
|
||||
Throwable cause=(IOException)((RuntimeIOException)th).getCause();
|
||||
if (cause!=null)
|
||||
th=cause;
|
||||
}
|
||||
|
||||
// handle or log exception
|
||||
if (th instanceof HttpException)
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
package org.eclipse.jetty.servlets;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@ -55,7 +54,8 @@ 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;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
@ -79,6 +79,7 @@ import org.eclipse.jetty.util.TypeUtil;
|
||||
*/
|
||||
public class MultiPartFilter implements Filter
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(MultiPartFilter.class);
|
||||
public final static String CONTENT_TYPE_SUFFIX=".org.eclipse.jetty.servlet.contentType";
|
||||
private final static String FILES ="org.eclipse.jetty.servlet.MultiPartFilter.files";
|
||||
private File tempdir;
|
||||
@ -146,10 +147,12 @@ public class MultiPartFilter implements Filter
|
||||
{
|
||||
// Get first boundary
|
||||
String line=((ReadLineInputStream)in).readLine();
|
||||
if(line==null || !line.equals(boundary))
|
||||
{
|
||||
|
||||
if (line == null || line.length() == 0)
|
||||
throw new IOException("Missing content for multipart request");
|
||||
|
||||
if (!line.equals(boundary))
|
||||
throw new IOException("Missing initial multi part boundary");
|
||||
}
|
||||
|
||||
// Read each part
|
||||
boolean lastPart=false;
|
||||
@ -195,7 +198,8 @@ public class MultiPartFilter implements Filter
|
||||
throw new IOException("Missing content-disposition");
|
||||
}
|
||||
|
||||
QuotedStringTokenizer tok=new QuotedStringTokenizer(content_disposition,";");
|
||||
LOG.debug("Content-Disposition: {}", content_disposition);
|
||||
QuotedStringTokenizer tok=new QuotedStringTokenizer(content_disposition,";",false,true);
|
||||
String name=null;
|
||||
String filename=null;
|
||||
while(tok.hasMoreTokens())
|
||||
@ -231,6 +235,7 @@ public class MultiPartFilter implements Filter
|
||||
{
|
||||
if (filename!=null && filename.length()>0)
|
||||
{
|
||||
LOG.debug("filename = \"{}\"", filename);
|
||||
file = File.createTempFile("MultiPart", "", tempdir);
|
||||
out = new FileOutputStream(file);
|
||||
if(_fileOutputBuffer>0)
|
||||
@ -407,7 +412,9 @@ public class MultiPartFilter implements Filter
|
||||
/* ------------------------------------------------------------ */
|
||||
private String value(String nameEqualsValue)
|
||||
{
|
||||
return nameEqualsValue.substring(nameEqualsValue.indexOf('=')+1).trim();
|
||||
int idx = nameEqualsValue.indexOf('=');
|
||||
String value = nameEqualsValue.substring(idx+1).trim();
|
||||
return QuotedStringTokenizer.unquoteOnly(value);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
|
@ -18,13 +18,13 @@
|
||||
|
||||
package org.eclipse.jetty.servlets;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
@ -36,7 +36,6 @@ 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.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@ -197,6 +196,42 @@ public class MultipartFilterTest
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
assertTrue(response.getContent().indexOf("brown cow")>=0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadlyEncodedFilename() 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=\"Taken on Aug 22 \\ 2012.jpg\"\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()));
|
||||
|
||||
System.out.printf("Content: [%s]%n", response.getContent());
|
||||
|
||||
assertThat(response.getMethod(), nullValue());
|
||||
assertThat(response.getStatus(), is(HttpServletResponse.SC_OK));
|
||||
|
||||
assertThat(response.getContent(), containsString("Filename [Taken on Aug 22 \\ 2012.jpg]"));
|
||||
assertThat(response.getContent(), containsString("How now brown cow."));
|
||||
}
|
||||
|
||||
/*
|
||||
* Test multipart with parts encoded in base64 (RFC1521 section 5)
|
||||
@ -472,6 +507,76 @@ public class MultipartFilterTest
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNoBody()
|
||||
throws Exception
|
||||
{
|
||||
String boundary="XyXyXy";
|
||||
// generated and parsed test
|
||||
HttpTester request = new HttpTester();
|
||||
HttpTester response = new HttpTester();
|
||||
request.setMethod("POST");
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host","tester");
|
||||
request.setURI("/context/dump");
|
||||
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
|
||||
|
||||
response.parse(tester.getResponses(request.generate()));
|
||||
assertTrue(response.getMethod()==null);
|
||||
assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
|
||||
assertTrue(response.getReason().startsWith("Missing content"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhitespaceBodyWithCRLF()
|
||||
throws Exception
|
||||
{
|
||||
String whitespace = " \n\n\n\r\n\r\n\r\n\r\n";
|
||||
|
||||
String boundary="XyXyXy";
|
||||
// generated and parsed test
|
||||
HttpTester request = new HttpTester();
|
||||
HttpTester response = new HttpTester();
|
||||
request.setMethod("POST");
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host","tester");
|
||||
request.setURI("/context/dump");
|
||||
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
|
||||
request.setContent(whitespace);
|
||||
|
||||
response.parse(tester.getResponses(request.generate()));
|
||||
assertTrue(response.getMethod()==null);
|
||||
assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
|
||||
assertTrue(response.getReason().startsWith("Missing initial"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testWhitespaceBody()
|
||||
throws Exception
|
||||
{
|
||||
String whitespace = " ";
|
||||
|
||||
String boundary="XyXyXy";
|
||||
// generated and parsed test
|
||||
HttpTester request = new HttpTester();
|
||||
HttpTester response = new HttpTester();
|
||||
request.setMethod("POST");
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host","tester");
|
||||
request.setURI("/context/dump");
|
||||
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
|
||||
request.setContent(whitespace);
|
||||
|
||||
response.parse(tester.getResponses(request.generate()));
|
||||
assertTrue(response.getMethod()==null);
|
||||
assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
|
||||
assertTrue(response.getReason().startsWith("Missing initial"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* see the testParameterMap test
|
||||
@ -483,7 +588,9 @@ public class MultipartFilterTest
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||
{
|
||||
assertEquals("How now brown cow.", req.getParameterMap().get("strupContent-Type: application/octet-stream"));
|
||||
String content = (String)req.getParameterMap().get("\"strup\"Content-Type: application/octet-stream");
|
||||
|
||||
assertThat(content, containsString("How now brown cow."));
|
||||
|
||||
super.doPost(req, resp);
|
||||
}
|
||||
@ -543,8 +650,17 @@ public class MultipartFilterTest
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||
{
|
||||
resp.getWriter().println((IO.toString(new FileInputStream((File)req.getAttribute("fileup")))));
|
||||
FileInputStream in = null;
|
||||
try {
|
||||
File file = (File)req.getAttribute("fileup");
|
||||
in = new FileInputStream(file);
|
||||
|
||||
PrintWriter out = resp.getWriter();
|
||||
out.printf("Filename [%s]\r\n", req.getParameter("fileup"));
|
||||
out.println(IO.toString(in));
|
||||
} finally {
|
||||
IO.close(in);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -435,6 +435,10 @@ public class QuotedStringTokenizer
|
||||
if (escape)
|
||||
{
|
||||
escape=false;
|
||||
if (!isValidEscaping(c))
|
||||
{
|
||||
b.append('\\');
|
||||
}
|
||||
b.append(c);
|
||||
}
|
||||
else if (c=='\\')
|
||||
@ -512,6 +516,10 @@ public class QuotedStringTokenizer
|
||||
);
|
||||
break;
|
||||
default:
|
||||
if (!isValidEscaping(c))
|
||||
{
|
||||
b.append('\\');
|
||||
}
|
||||
b.append(c);
|
||||
}
|
||||
}
|
||||
@ -527,6 +535,13 @@ public class QuotedStringTokenizer
|
||||
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private static boolean isValidEscaping(char c)
|
||||
{
|
||||
return ((c == 'n') || (c == 'r') || (c == 't') ||
|
||||
(c == 'f') || (c == 'b') || (c == '\\') ||
|
||||
(c == '/') || (c == '"') || (c == 'u'));
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
|
@ -51,7 +51,11 @@ public class ReadLineInputStream extends BufferedInputStream
|
||||
int b=super.read();
|
||||
if (b==-1)
|
||||
{
|
||||
int m=markpos;
|
||||
markpos=-1;
|
||||
if (pos>m)
|
||||
return new String(buf,m,pos-m,StringUtil.__UTF8);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -224,10 +224,7 @@ public class UrlEncoded extends MultiMap implements Cloneable
|
||||
key = null;
|
||||
value=null;
|
||||
if (maxKeys>0 && map.size()>maxKeys)
|
||||
{
|
||||
LOG.warn("maxFormKeys limit exceeded keys>{}",maxKeys);
|
||||
return;
|
||||
}
|
||||
throw new IllegalStateException("Form too many keys");
|
||||
break;
|
||||
case '=':
|
||||
if (key!=null)
|
||||
@ -396,10 +393,7 @@ public class UrlEncoded extends MultiMap implements Cloneable
|
||||
key = null;
|
||||
value=null;
|
||||
if (maxKeys>0 && map.size()>maxKeys)
|
||||
{
|
||||
LOG.warn("maxFormKeys limit exceeded keys>{}",maxKeys);
|
||||
return;
|
||||
}
|
||||
throw new IllegalStateException("Form too many keys");
|
||||
break;
|
||||
|
||||
case '=':
|
||||
@ -483,10 +477,7 @@ public class UrlEncoded extends MultiMap implements Cloneable
|
||||
key = null;
|
||||
value=null;
|
||||
if (maxKeys>0 && map.size()>maxKeys)
|
||||
{
|
||||
LOG.warn("maxFormKeys limit exceeded keys>{}",maxKeys);
|
||||
return;
|
||||
}
|
||||
throw new IllegalStateException("Form too many keys");
|
||||
break;
|
||||
|
||||
case '=':
|
||||
@ -611,6 +602,8 @@ public class UrlEncoded extends MultiMap implements Cloneable
|
||||
}
|
||||
key = null;
|
||||
value=null;
|
||||
if (maxKeys>0 && map.size()>maxKeys)
|
||||
throw new IllegalStateException("Form too many keys");
|
||||
break;
|
||||
case '=':
|
||||
if (key!=null)
|
||||
|
@ -18,9 +18,7 @@
|
||||
|
||||
package org.eclipse.jetty.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@ -190,4 +188,21 @@ public class QuotedStringTokenizerTest
|
||||
assertEquals("ba\\uXXXXaaa", QuotedStringTokenizer.unquoteOnly("\"ba\\\\uXXXXaaa\""));
|
||||
}
|
||||
|
||||
/**
|
||||
* When encountering a Content-Disposition line during a multi-part mime file
|
||||
* upload, the filename="..." field can contain '\' characters that do not
|
||||
* belong to a proper escaping sequence, this tests QuotedStringTokenizer to
|
||||
* ensure that it preserves those slashes for where they cannot be escaped.
|
||||
*/
|
||||
@Test
|
||||
public void testNextTokenOnContentDisposition()
|
||||
{
|
||||
String content_disposition = "form-data; name=\"fileup\"; filename=\"Taken on Aug 22 \\ 2012.jpg\"";
|
||||
|
||||
QuotedStringTokenizer tok=new QuotedStringTokenizer(content_disposition,";",false,true);
|
||||
|
||||
assertEquals("form-data", tok.nextToken().trim());
|
||||
assertEquals("name=\"fileup\"", tok.nextToken().trim());
|
||||
assertEquals("filename=\"Taken on Aug 22 \\ 2012.jpg\"", tok.nextToken().trim());
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,9 @@ import javax.servlet.ServletResponse;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.HandlerList;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
@ -185,6 +187,36 @@ public class WebAppContextTest
|
||||
assertFalse(context.isProtectedTarget("/something-else/web-inf"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNullPath() throws Exception
|
||||
{
|
||||
Server server = new Server(0);
|
||||
HandlerList handlers = new HandlerList();
|
||||
ContextHandlerCollection contexts = new ContextHandlerCollection();
|
||||
WebAppContext context = new WebAppContext();
|
||||
context.setBaseResource(Resource.newResource("./src/test/webapp"));
|
||||
context.setContextPath("/");
|
||||
server.setHandler(handlers);
|
||||
handlers.addHandler(contexts);
|
||||
contexts.addHandler(context);
|
||||
|
||||
LocalConnector connector = new LocalConnector();
|
||||
server.addConnector(connector);
|
||||
|
||||
server.start();
|
||||
try
|
||||
{
|
||||
String response = connector.getResponses("GET http://localhost:8080 HTTP/1.1\r\nHost: localhost:8080\r\nConnection: close\r\n\r\n");
|
||||
Assert.assertTrue(response.indexOf("200 OK")>=0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ServletA extends GenericServlet
|
||||
{
|
||||
@Override
|
||||
|
@ -97,6 +97,12 @@ public class ServletTester
|
||||
_server.start();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void join() throws Exception
|
||||
{
|
||||
_server.join();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void stop() throws Exception
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user