Merge branch 'master' into websocket-ssl

This commit is contained in:
Joakim Erdfelt 2013-06-11 08:58:32 -07:00
commit 4cd1bd8ce4
18 changed files with 226 additions and 80 deletions

View File

@ -25,10 +25,12 @@ import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.deploy.test.XmlConfiguredJetty;
import org.eclipse.jetty.toolchain.test.OS;
import org.eclipse.jetty.toolchain.test.TestTracker;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
@ -43,6 +45,9 @@ public class ScanningAppProviderRuntimeUpdatesTest
{
private static final Logger LOG = Log.getLogger(ScanningAppProviderRuntimeUpdatesTest.class);
@Rule
public TestTracker tracker = new TestTracker();
@Rule
public TestingDir testdir = new TestingDir();
private static XmlConfiguredJetty jetty;
@ -52,6 +57,9 @@ public class ScanningAppProviderRuntimeUpdatesTest
@Before
public void setupEnvironment() throws Exception
{
testdir.ensureEmpty();
Resource.setDefaultUseCaches(false);
jetty = new XmlConfiguredJetty(testdir);
jetty.addConfiguration("jetty.xml");
jetty.addConfiguration("jetty-http.xml");
@ -91,7 +99,7 @@ public class ScanningAppProviderRuntimeUpdatesTest
public void waitForDirectoryScan()
{
int scan=_scans.get()+2*_providers;
int scan=_scans.get()+(2*_providers);
do
{
try

View File

@ -18,6 +18,8 @@
package org.eclipse.jetty.deploy.test;
import static org.hamcrest.Matchers.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@ -207,6 +209,7 @@ public class XmlConfiguredJetty
if (context.getContextPath().equals(expectedPath))
{
found = true;
Assert.assertThat("Context[" + context.getContextPath() + "].state", context.getState(), is("STARTED"));
break;
}
}

View File

@ -272,8 +272,13 @@ public abstract class AbstractWebAppProvider extends AbstractLifeCycle implement
if (tmp != null)
{
File defaultWebXml = getFile (tmp, bundleInstallLocation);
if (defaultWebXml != null && defaultWebXml.exists())
_webApp.setDefaultsDescriptor(defaultWebXml.getAbsolutePath());
if (defaultWebXml != null)
{
if (defaultWebXml.exists())
_webApp.setDefaultsDescriptor(defaultWebXml.getAbsolutePath());
else
LOG.warn(defaultWebXml.getAbsolutePath()+" does not exist");
}
}
//Handle Require-TldBundle
@ -377,6 +382,7 @@ public abstract class AbstractWebAppProvider extends AbstractLifeCycle implement
HashMap properties = new HashMap();
properties.put("Server", getDeploymentManager().getServer());
properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, rootResource.toString());
properties.put(OSGiServerConstants.JETTY_HOME, getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME));
xmlConfiguration.getProperties().putAll(properties);
xmlConfiguration.configure(_webApp);
}
@ -390,11 +396,21 @@ public abstract class AbstractWebAppProvider extends AbstractLifeCycle implement
{
if (file == null)
return null;
if (file.startsWith("/") || file.startsWith("file:/"))
if (file.startsWith("/") || file.startsWith("file:/")) //absolute location
return new File(file);
else
return new File(bundleInstall, file);
{
//relative location
//try inside the bundle first
File f = new File (bundleInstall, file);
if (f.exists()) return f;
String jettyHome = (String)getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME);
if (jettyHome != null)
return new File(jettyHome, file);
}
return null;
}
}

View File

@ -435,12 +435,21 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
LOG.ignore(e);
path = _uri.getDecodedPath(StringUtil.__ISO_8859_1);
}
String info = URIUtil.canonicalPath(path);
if (info == null)
{
info = "/";
_request.setRequestURI("");
if( path==null && _uri.getScheme()!=null &&_uri.getHost()!=null)
{
info = "/";
_request.setRequestURI("");
}
else
{
badMessage(400,null);
return true;
}
}
_request.setPathInfo(info);
_version = version == null ? HttpVersion.HTTP_0_9 : version;
@ -663,7 +672,6 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
* @param content the content buffer to write
* @param complete whether the content is complete for the response
* @param callback Callback when complete or failed
* @throws IOException if the write fails
*/
protected void write(ByteBuffer content, boolean complete, Callback callback)
{
@ -689,6 +697,14 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
return getEndPoint() instanceof ChannelEndPoint;
}
/**
* If a write or similar to this channel fails this method should be called. The standard implementation
* of {@link #failed()} is a noop. But the different implementations of HttpChannel might want to take actions.
*/
public void failed()
{
}
private class CommitCallback implements Callback
{
private final Callback _callback;

View File

@ -311,7 +311,6 @@ public class HttpChannelState
boolean dispatch;
synchronized (this)
{
switch(_state)
{
case ASYNCSTARTED:
@ -326,11 +325,7 @@ public class HttpChannelState
_event.setDispatchTarget(context,path);
_dispatched=true;
break;
case REDISPATCH:
_event.setDispatchTarget(context,path);
return;
default:
throw new IllegalStateException(this.getStatusString());
}

View File

@ -578,6 +578,12 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
_generator.setPersistent(false);
super.handleException(x);
}
@Override
public void failed()
{
getEndPoint().shutdownOutput();
}
}
private class CommitCallback extends IteratingCallback

View File

@ -99,7 +99,7 @@ public class HttpOutput extends ServletOutputStream
}
catch(IOException e)
{
_channel.getEndPoint().shutdownOutput();
_channel.failed();
LOG.ignore(e);
}
releaseBuffer();
@ -120,7 +120,7 @@ public class HttpOutput extends ServletOutputStream
}
catch(IOException e)
{
_channel.getEndPoint().shutdownOutput();
_channel.failed();
LOG.ignore(e);
}
}

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.server;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@ -72,6 +73,7 @@ import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.server.session.AbstractSession;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.MultiPartInputStreamParser;
@ -349,6 +351,7 @@ public class Request implements HttpServletRequest
}
}
}
}
if (_parameters == null)
@ -358,6 +361,28 @@ public class Request implements HttpServletRequest
// Merge parameters (needed if parameters extracted after a forward).
_parameters.addAllValues(_baseParameters);
}
if (content_type != null && content_type.length()>0 && content_type.startsWith("multipart/form-data") && getAttribute(__MULTIPART_CONFIG_ELEMENT)!=null)
{
try
{
getParts();
}
catch (IOException e)
{
if (LOG.isDebugEnabled())
LOG.warn(e);
else
LOG.warn(e.toString());
}
catch (ServletException e)
{
if (LOG.isDebugEnabled())
LOG.warn(e);
else
LOG.warn(e.toString());
}
}
}
finally
{
@ -2036,38 +2061,8 @@ public class Request implements HttpServletRequest
@Override
public Part getPart(String name) throws IOException, ServletException
{
if (getContentType() == null || !getContentType().startsWith("multipart/form-data"))
throw new ServletException("Content-Type != multipart/form-data");
getParts();
if (_multiPartInputStream == null)
{
MultipartConfigElement config = (MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT);
if (config == null)
throw new IllegalStateException("No multipart config for servlet");
_multiPartInputStream = new MultiPartInputStreamParser(getInputStream(),
getContentType(),config,
(_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
setAttribute(__MULTIPART_INPUT_STREAM, _multiPartInputStream);
setAttribute(__MULTIPART_CONTEXT, _context);
Collection<Part> parts = _multiPartInputStream.getParts(); //causes parsing
for (Part p:parts)
{
MultiPartInputStreamParser.MultiPart mp = (MultiPartInputStreamParser.MultiPart)p;
if (mp.getContentDispositionFilename() == null && mp.getFile() == null)
{
//Servlet Spec 3.0 pg 23, parts without filenames must be put into init params
String charset = null;
if (mp.getContentType() != null)
charset = MimeTypes.getCharsetFromContentType(mp.getContentType());
String content=new String(mp.getBytes(),charset==null?StringUtil.__UTF8:charset);
getParameter(""); //cause params to be evaluated
getParameters().add(mp.getName(), content);
}
}
}
return _multiPartInputStream.getPart(name);
}
@ -2078,6 +2073,9 @@ public class Request implements HttpServletRequest
if (getContentType() == null || !getContentType().startsWith("multipart/form-data"))
throw new ServletException("Content-Type != multipart/form-data");
if (_multiPartInputStream == null)
_multiPartInputStream = (MultiPartInputStreamParser)getAttribute(__MULTIPART_INPUT_STREAM);
if (_multiPartInputStream == null)
{
MultipartConfigElement config = (MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT);
@ -2095,19 +2093,32 @@ public class Request implements HttpServletRequest
for (Part p:parts)
{
MultiPartInputStreamParser.MultiPart mp = (MultiPartInputStreamParser.MultiPart)p;
if (mp.getContentDispositionFilename() == null && mp.getFile() == null)
if (mp.getContentDispositionFilename() == null)
{
//Servlet Spec 3.0 pg 23, parts without filenames must be put into init params
String charset = null;
if (mp.getContentType() != null)
charset = MimeTypes.getCharsetFromContentType(mp.getContentType());
String content=new String(mp.getBytes(),charset==null?StringUtil.__UTF8:charset);
getParameter(""); //cause params to be evaluated
getParameters().add(mp.getName(), content);
ByteArrayOutputStream os = null;
InputStream is = mp.getInputStream(); //get the bytes regardless of being in memory or in temp file
try
{
os = new ByteArrayOutputStream();
IO.copy(is, os);
String content=new String(os.toByteArray(),charset==null?StringUtil.__UTF8:charset);
getParameter(""); //cause params to be evaluated
getParameters().add(mp.getName(), content);
}
finally
{
IO.close(os);
IO.close(is);
}
}
}
}
return _multiPartInputStream.getParts();
}

View File

@ -1825,9 +1825,12 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
// uriInContext = uriInContext.substring(0,q);
String pathInContext = URIUtil.canonicalPath(URIUtil.decodePath(uriInContext));
String uri = URIUtil.addPaths(getContextPath(),uriInContext);
ContextHandler context = ContextHandler.this;
return new Dispatcher(context,uri,pathInContext,query);
if (pathInContext!=null)
{
String uri = URIUtil.addPaths(getContextPath(),uriInContext);
ContextHandler context = ContextHandler.this;
return new Dispatcher(context,uri,pathInContext,query);
}
}
catch (Exception e)
{

View File

@ -138,6 +138,66 @@ public class HttpConnectionTest
checkContains(response,offset,"pathInfo=/");
}
@Test
public void testBadNoPath() throws Exception
{
String response=connector.getResponses("GET http://localhost:80/../cheat HTTP/1.1\n"+
"Host: localhost:80\n"+
"\n");
int offset=0;
offset = checkContains(response,offset,"HTTP/1.1 400");
}
@Test
public void testOKPathDotDotPath() throws Exception
{
String response=connector.getResponses("GET /ooops/../path HTTP/1.0\nHost: localhost:80\n\n");
checkContains(response,0,"HTTP/1.1 200 OK");
checkContains(response,0,"pathInfo=/path");
}
@Test
public void testBadPathDotDotPath() throws Exception
{
String response=connector.getResponses("GET /ooops/../../path HTTP/1.0\nHost: localhost:80\n\n");
checkContains(response,0,"HTTP/1.1 400 Bad Request");
}
@Test
public void testOKPathEncodedDotDotPath() throws Exception
{
String response=connector.getResponses("GET /ooops/%2e%2e/path HTTP/1.0\nHost: localhost:80\n\n");
checkContains(response,0,"HTTP/1.1 200 OK");
checkContains(response,0,"pathInfo=/path");
}
@Test
public void testBadPathEncodedDotDotPath() throws Exception
{
String response=connector.getResponses("GET /ooops/%2e%2e/%2e%2e/path HTTP/1.0\nHost: localhost:80\n\n");
checkContains(response,0,"HTTP/1.1 400 Bad Request");
}
@Test
public void testBadDotDotPath() throws Exception
{
String response=connector.getResponses("GET ../path HTTP/1.0\nHost: localhost:80\n\n");
checkContains(response,0,"HTTP/1.1 400 Bad Request");
}
@Test
public void testBadSlashDotDotPath() throws Exception
{
String response=connector.getResponses("GET /../path HTTP/1.0\nHost: localhost:80\n\n");
checkContains(response,0,"HTTP/1.1 400 Bad Request");
}
@Test
public void testEncodedBadDotDotPath() throws Exception
{
String response=connector.getResponses("GET %2e%2e/path HTTP/1.0\nHost: localhost:80\n\n");
checkContains(response,0,"HTTP/1.1 400 Bad Request");
}
@Test
public void testEmpty() throws Exception

View File

@ -1094,10 +1094,12 @@ public class RequestTest
MultipartConfigElement mpce = new MultipartConfigElement(tmpDir.getAbsolutePath(),-1, -1, 2);
request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce);
String field1 = request.getParameter("field1");
assertNotNull(field1);
Part foo = request.getPart("stuff");
assertNotNull(foo);
assertTrue(foo.getSize() > 0);
response.setStatus(200);
}
catch (IllegalStateException e)

View File

@ -18,8 +18,10 @@
package org.eclipse.jetty.servlet;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertThat;
import java.io.IOException;
import java.util.Arrays;
@ -55,6 +57,7 @@ import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.UrlEncoded;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@ -217,6 +220,19 @@ public class DispatcherTest
assertEquals(expected, responses);
}
@Test
public void testServletForwardDotDot() throws Exception
{
_contextHandler.addServlet(DispatchServletServlet.class, "/dispatch/*");
_contextHandler.addServlet(RogerThatServlet.class, "/roger/that");
String requests="GET /context/dispatch/test?forward=%2e%2e/roger/that HTTP/1.0\n" + "Host: localhost\n\n";
String responses = _connector.getResponses(requests);
assertThat(responses,startsWith("HTTP/1.1 404 "));
}
@Test
public void testServletInclude() throws Exception
{
@ -409,7 +425,10 @@ public class DispatcherTest
else if(request.getParameter("forward")!=null)
{
dispatcher = getServletContext().getRequestDispatcher(request.getParameter("forward"));
dispatcher.forward(new ServletRequestWrapper(request), new ServletResponseWrapper(response));
if (dispatcher!=null)
dispatcher.forward(new ServletRequestWrapper(request), new ServletResponseWrapper(response));
else
response.sendError(404);
}
}
@ -581,8 +600,19 @@ public class DispatcherTest
assertTrue(requestAttributeNames.containsAll(expectedAttributeNames));
assertEquals(null, request.getPathInfo());
assertEquals(null, request.getPathTranslated());
assertTrue(request.getQueryString().startsWith("do=end&else=%D0%B2%D1%8B%D0%B1%D1%80%D0%B0%D0%BD%D0%BE%3D%D0%A2%D0%B5%D0%BC%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D1%83%D1%80%D0%B0&test=1&foreign="));
assertEquals(null, request.getPathTranslated());
UrlEncoded query = new UrlEncoded();
query.decode(request.getQueryString());
assertThat(query.getString("do"), is("end"));
// Russian for "selected=Temperature"
UrlEncoded q2=new UrlEncoded();
q2.decode(query.getString("else"));
String russian = q2.encode();
assertThat(russian, is("%D0%B2%D1%8B%D0%B1%D1%80%D0%B0%D0%BD%D0%BE=%D0%A2%D0%B5%D0%BC%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D1%83%D1%80%D0%B0"));
assertThat(query.getString("test"), is("1"));
assertThat(query.containsKey("foreign"), is(true));
String[] vals = request.getParameterValues("foreign");
assertTrue(vals!=null);

View File

@ -140,7 +140,7 @@ public class UpgradeConnection extends AbstractConnection
public void onOpen()
{
super.onOpen();
// TODO: handle timeout
// TODO: handle timeout?
getExecutor().execute(new SendUpgradeRequest());
}

View File

@ -22,6 +22,7 @@ import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Executor;
import javax.net.ssl.SSLEngine;
import org.eclipse.jetty.io.ByteBufferPool;
@ -87,7 +88,6 @@ public class WebSocketClientSelectorManager extends SelectorManager
Connection connection = newUpgradeConnection(channel,sslEndPoint,connectPromise);
sslEndPoint.setIdleTimeout(connectPromise.getClient().getMaxIdleTimeout());
sslEndPoint.setConnection(connection);
connectionOpened(connection);
return sslConnection;
}
else

View File

@ -77,7 +77,6 @@ public abstract class ContinuationBase
protected void doSuspendResume() throws Exception
{
String response=process("suspend=200&resume=0",null);
System.err.println(response);
assertContains("RESUMED",response);
assertNotContains("history: onTimeout",response);
assertContains("history: onComplete",response);
@ -503,7 +502,7 @@ public abstract class ContinuationBase
public void onTimeout(Continuation continuation)
{
((HttpServletResponse)continuation.getServletResponse()).addHeader("history","onTimeout");
continuation.resume();
// continuation.resume();
}
};

View File

@ -18,9 +18,7 @@
package org.eclipse.jetty.server.session;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;
import java.io.File;
import java.io.FileWriter;
@ -32,35 +30,32 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.StdErrLog;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.Rule;
import org.junit.Test;
/**
* ReloadedSessionMissingClassTest
*
*
*
*/
public class ReloadedSessionMissingClassTest
{
@Rule
public TestingDir testdir = new TestingDir();
@Test
public void testSessionReloadWithMissingClass() throws Exception
{
((StdErrLog)Log.getLogger(org.eclipse.jetty.server.session.JDBCSessionManager.class)).setHideStacks(true);
Resource.setDefaultUseCaches(false);
String contextPath = "/foo";
File srcDir = new File(System.getProperty("basedir"), "src");
File targetDir = new File(System.getProperty("basedir"), "target");
File testDir = new File (srcDir, "test");
File resourcesDir = new File (testDir, "resources");
File unpackedWarDir = new File (targetDir, "foo");
if (unpackedWarDir.exists())
IO.delete(unpackedWarDir);
unpackedWarDir.mkdir();
File unpackedWarDir = testdir.getDir();
testdir.ensureEmpty();
File webInfDir = new File (unpackedWarDir, "WEB-INF");
webInfDir.mkdir();
@ -81,8 +76,8 @@ public class ReloadedSessionMissingClassTest
w.write(xml);
w.close();
File foobarJar = new File (resourcesDir, "foobar.jar");
File foobarNOfooJar = new File (resourcesDir, "foobarNOfoo.jar");
File foobarJar = MavenTestingUtils.getTestResourceFile("foobar.jar");
File foobarNOfooJar = MavenTestingUtils.getTestResourceFile("foobarNOfoo.jar");
URL[] foobarUrls = new URL[]{foobarJar.toURI().toURL()};
URL[] barUrls = new URL[]{foobarNOfooJar.toURI().toURL()};

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.server.session;
import java.sql.DriverManager;
import org.eclipse.jetty.util.resource.Resource;
import java.sql.SQLException;
import org.junit.After;
@ -34,6 +35,7 @@ public class WebAppObjectInSessionTest extends AbstractWebAppObjectInSessionTest
public AbstractTestServer createServer(int port)
{
Resource.setDefaultUseCaches(false);
return new JdbcTestServer(port);
}