Merge branch 'jetty-8' into release-8

This commit is contained in:
Jesse McConnell 2013-03-12 08:37:45 -05:00
commit fdda21c167
83 changed files with 3293 additions and 1028 deletions

View File

@ -51,6 +51,51 @@ jetty-8.1.9.v20130131 - 31 January 2013
+ 399132 check parent dir of session store against file to be removed
+ JETTY-1533 handle URL with no path
jetty-7.6.9.v20130131 - 31 January 2013
+ 362226 HttpConnection "wait" call causes thread resource exhaustion
+ 367638 throw exception for excess form keys
+ 381521 Only set Vary header when content could be compressed
+ 382237 support non java JSON classes
+ 391248 fixing localhost checking in statistics servlet
+ 391249 fix for invalid XML node dispatchedTimeMean in statistics servlet
+ 391345 fix missing br tag in statistics servlet
+ 391623 Add option to --stop to wait for target jetty to stop
+ 392417 Prevent Cookie parsing interpreting unicode chars
+ 392492 expect headers only examined for requests>=HTTP/1.1
+ 393075 1xx 204 and 304 ignore all headers suggesting content
+ 393220 remove dead code from ServletHandler and log ServletExceptions in
warn instead of debug
+ 393947 additional tests
+ 393968 fix typo in javadoc
+ 394514 Preserve URI parameters in sendRedirect
+ 394541 remove continuation jar from distro, add as dep to test-jetty-webapp
+ 394719 remove regex from classpath matching
+ 394811 Make JAASLoginService log login failures to DEBUG instead of WARN.
Same for some other exceptions.
+ 394829 Session can not be restored after SessionManager.setIdleSavePeriod
has saved the session
+ 394839 Allow multipart mime with no boundary
+ 395215 Multipart mime with just LF and no CRLF
+ 395380 add ValidUrlRule to jetty-rewrite
+ 395394 allow logging from boot classloader
+ 396459 Log specific message for empty request body for multipart mime
requests
+ 396500 HttpClient Exchange takes forever to complete when less content sent
than Content-Length
+ 396574 add JETTY_HOME as a location for pid to be found
+ 396886 MultiPartFilter strips bad escaping on filename="..."
+ 397110 Accept %uXXXX encodings in URIs
+ 397111 Tolerate empty or excessive whitespace preceeding MultiParts
+ 397112 Requests with byte-range throws NPE if requested file has no mimetype
(eg no file extension)
+ 397130 maxFormContentSize set in jetty.xml is ignored
+ 397190 improve ValidUrlRule to iterate on codepoints
+ 397321 Wrong condition in default start.config for annotations
+ 397535 Support pluggable alias checking to support symbolic links
+ 398337 UTF-16 percent encoding in UTF-16 form content
+ 399132 check parent dir of session store against file to be removed
+ JETTY-1533 handle URL with no path
jetty-8.1.8.v20121106 - 06 November 2012
+ 371170 MongoSessionManager LastAccessTimeTest fails
+ 388675 Non utf8 encoded query strings not decoded to parameter map using

View File

@ -224,10 +224,10 @@ public class AnnotationConfiguration extends AbstractConfiguration
}
//add a listener which will call the servletcontainerinitializers when appropriate
//add a bean which will call the servletcontainerinitializers when appropriate
ServletContainerInitializerListener listener = new ServletContainerInitializerListener();
listener.setWebAppContext(context);
context.addEventListener(listener);
context.addBean(listener, true);
}

View File

@ -25,12 +25,13 @@ import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import org.eclipse.jetty.util.Loader;
@ -54,7 +55,7 @@ public class AnnotationParser
{
private static final Logger LOG = Log.getLogger(AnnotationParser.class);
protected List<String> _parsedClassNames = new ArrayList<String>();
protected Set<String> _parsedClassNames = new HashSet<String>();
protected List<Handler> _handlers = new ArrayList<Handler>();
public static String normalize (String name)

View File

@ -22,12 +22,10 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.plus.annotation.ContainerInitializer;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.WebAppContext;
@ -37,7 +35,7 @@ import org.eclipse.jetty.webapp.WebAppContext;
*
*
*/
public class ServletContainerInitializerListener implements ServletContextListener
public class ServletContainerInitializerListener extends AbstractLifeCycle
{
private static final Logger LOG = Log.getLogger(ServletContainerInitializerListener.class);
protected WebAppContext _context = null;
@ -48,10 +46,12 @@ public class ServletContainerInitializerListener implements ServletContextListen
_context = context;
}
/**
* @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
* Call the doStart method of the ServletContainerInitializers
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
*/
public void contextInitialized(ServletContextEvent sce)
public void doStart()
{
List<ContainerInitializer> initializers = (List<ContainerInitializer>)_context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS);
MultiMap classMap = (MultiMap)_context.getAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP);
@ -131,10 +131,12 @@ public class ServletContainerInitializerListener implements ServletContextListen
}
/**
* @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
* Nothing to do for ServletContainerInitializers on stop
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
*/
public void contextDestroyed(ServletContextEvent sce)
public void doStop()
{
}

View File

@ -0,0 +1,64 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.client;
import java.io.IOException;
import java.net.Socket;
import org.eclipse.jetty.http.HttpSchemes;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
public class ExternalSiteTest
{
@Test
public void testExternalSSLSite() throws Exception
{
HttpClient client = new HttpClient(new SslContextFactory());
client.start();
String host = "api-3t.paypal.com";
int port = 443;
// Verify that we have connectivity
try
{
new Socket(host, port).close();
}
catch (IOException x)
{
Assume.assumeNoException(x);
}
ContentExchange exchange = new ContentExchange(true);
exchange.setScheme(HttpSchemes.HTTPS_BUFFER);
exchange.setAddress(new Address(host, port));
exchange.setRequestURI("/nvp");
client.send(exchange);
int done = exchange.waitForDone();
Assert.assertEquals(HttpExchange.STATUS_COMPLETED, done);
Assert.assertEquals(HttpStatus.OK_200, exchange.getResponseStatus());
Assert.assertNotNull(exchange.getResponseContentBytes());
client.stop();
}
}

View File

@ -18,8 +18,6 @@
package org.eclipse.jetty.client;
import static org.hamcrest.Matchers.*;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.File;
@ -39,7 +37,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocket;
@ -72,6 +69,11 @@ import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.lessThan;
import static org.hamcrest.Matchers.not;
public class SslBytesServerTest extends SslBytesTest
{
private final AtomicInteger sslHandles = new AtomicInteger();
@ -847,7 +849,9 @@ public class SslBytesServerTest extends SslBytesTest
// Close the raw socket, this generates a truncation attack
proxy.flushToServer((TLSRecord)null);
// Expect raw close from server
// Expect alert + raw close from server
record = proxy.readFromServer();
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record);
proxy.flushToClient(record);
@ -1631,9 +1635,9 @@ public class SslBytesServerTest extends SslBytesTest
//System.err.println(((Dumpable)server.getConnectors()[0]).dump());
Assert.assertThat(((Dumpable)server.getConnectors()[0]).dump(),containsString("SCEP@"));
completeClose(client);
TimeUnit.MILLISECONDS.sleep(200);
//System.err.println(((Dumpable)server.getConnectors()[0]).dump());
Assert.assertThat(((Dumpable)server.getConnectors()[0]).dump(),not(containsString("SCEP@")));
@ -1748,13 +1752,13 @@ public class SslBytesServerTest extends SslBytesTest
// Close Alert
record = proxy.readFromServer();
proxy.flushToClient(record);
// Socket close
record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record);
proxy.flushToClient(record);
}
private void completeClose(SSLSocket client) throws Exception
{
client.close();
@ -1770,6 +1774,6 @@ public class SslBytesServerTest extends SslBytesTest
// Close Alert
record = proxy.readFromServer();
proxy.flushToClient(record);
}
}

View File

@ -537,7 +537,7 @@ case "$ACTION" in
;;
check)
check|status)
echo "Checking arguments to Jetty: "
echo "JETTY_HOME = $JETTY_HOME"
echo "JETTY_CONF = $JETTY_CONF"

View File

@ -1017,7 +1017,7 @@ public class HttpGenerator extends AbstractGenerator
{
if (_needCRLF)
{
if (_buffer == null && _header.space() >= 2)
if (_buffer == null && _header != null && _header.space() >= 2)
{
_header.put(HttpTokens.CRLF);
_needCRLF = false;
@ -1031,7 +1031,7 @@ public class HttpGenerator extends AbstractGenerator
if (!_needCRLF && _needEOC)
{
if (_buffer == null && _header.space() >= LAST_CHUNK.length)
if (_buffer == null && _header != null && _header.space() >= LAST_CHUNK.length)
{
if (!_head)
{

View File

@ -225,8 +225,8 @@ public class HttpParser implements Parser
/* ------------------------------------------------------------------------------- */
/**
* Parse until END state.
* This method will parse any remaining content in the current buffer. It does not care about the
* {@link #getState current state} of the parser.
* This method will parse any remaining content in the current buffer as long as there is
* no unconsumed content. It does not care about the {@link #getState current state} of the parser.
* @see #parse
* @see #parseNext
*/
@ -235,7 +235,7 @@ public class HttpParser implements Parser
boolean progress=parseNext()>0;
// continue parsing
while (!isComplete() && _buffer!=null && _buffer.length()>0)
while (!isComplete() && _buffer!=null && _buffer.length()>0 && !_contentView.hasContent())
{
progress |= parseNext()>0;
}

View File

@ -31,6 +31,9 @@ import org.eclipse.jetty.util.StringUtil;
*/
public class ByteArrayBuffer extends AbstractBuffer
{
// Set a maximum size to a write for the writeTo method, to ensure that very large content is not
// written as a single write (which may fall foul to write timeouts if consumed slowly).
final static int MAX_WRITE=Integer.getInteger("org.eclipse.jetty.io.ByteArrayBuffer.MAX_WRITE",128*1024);
final protected byte[] _bytes;
protected ByteArrayBuffer(int size, int access, boolean isVolatile)
@ -356,7 +359,20 @@ public class ByteArrayBuffer extends AbstractBuffer
public void writeTo(OutputStream out)
throws IOException
{
out.write(_bytes,getIndex(),length());
int len=length();
if (MAX_WRITE>0 && len>MAX_WRITE)
{
int off=getIndex();
while(len>0)
{
int c=len>MAX_WRITE?MAX_WRITE:len;
out.write(_bytes,off,c);
off+=c;
len-=c;
}
}
else
out.write(_bytes,getIndex(),len);
if (!isImmutable())
clear();
}

View File

@ -406,7 +406,7 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
// pass on ishut/oshut state
if (_endp.isOpen() && _endp.isInputShutdown() && !_inbound.hasContent())
_engine.closeInbound();
closeInbound();
if (_endp.isOpen() && _engine.isOutboundDone() && !_outbound.hasContent())
_endp.shutdownOutput();
@ -428,6 +428,18 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
return some_progress;
}
private void closeInbound()
{
try
{
_engine.closeInbound();
}
catch (SSLException x)
{
_logger.debug(x);
}
}
private synchronized boolean wrap(final Buffer buffer) throws IOException
{
ByteBuffer bbuf=extractByteBuffer(buffer);

View File

@ -26,6 +26,7 @@ import java.nio.channels.SocketChannel;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import org.eclipse.jetty.io.EndPoint;
@ -188,12 +189,28 @@ public class SelectChannelEndPointSslTest extends SelectChannelEndPointTest
Assert.assertEquals("HelloWorld",reply);
SelectorManager.LOG.info("javax.net.ssl.SSLException: Inbound closed... is expected soon");
if (debug) System.err.println("\nSudden Death");
if (debug) System.err.println("Shutting down output");
client.socket().shutdownOutput();
filled=client.read(sslIn);
Assert.assertEquals(-1,filled);
if (debug) System.err.println("in="+filled);
sslIn.flip();
try
{
// Since the client closed abruptly, the server is sending a close alert with a failure
engine.unwrap(sslIn, appIn);
Assert.fail();
}
catch (SSLException x)
{
// Expected
}
sslIn.clear();
filled = client.read(sslIn);
Assert.assertEquals(-1, filled);
Assert.assertFalse(server.isOpen());
}
@Test

View File

@ -52,9 +52,11 @@ import org.eclipse.jetty.util.log.Logger;
* specific to a webapp).
*
* The context selected is based on classloaders. First
* we try looking in at the classloader that is associated
* with the current webapp context (if there is one). If
* not, we use the thread context classloader.
* we try looking at the thread context classloader if it is set, and walk its
* hierarchy, creating a context if none is found. If the thread context classloader
* is not set, then we use the classloader associated with the current Context.
*
* If there is no current context, or no classloader, we return null.
*
* Created: Fri Jun 27 09:26:40 2003
*
@ -80,9 +82,16 @@ public class ContextFactory implements ObjectFactory
/**
* Find or create a context which pertains to a classloader.
*
* We use either the classloader for the current ContextHandler if
* we are handling a request, OR we use the thread context classloader
* if we are not processing a request.
* If the thread context classloader is set, we try to find an already-created naming context
* for it. If one does not exist, we walk its classloader hierarchy until one is found, or we
* run out of parent classloaders. In the latter case, we will create a new naming context associated
* with the original thread context classloader.
*
* If the thread context classloader is not set, we obtain the classloader from the current
* jetty Context, and look for an already-created naming context.
*
* If there is no current jetty Context, or it has no associated classloader, we
* return null.
* @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object, javax.naming.Name, javax.naming.Context, java.util.Hashtable)
*/
public Object getObjectInstance (Object obj,
@ -99,41 +108,89 @@ public class ContextFactory implements ObjectFactory
return ctx;
}
ClassLoader loader = null;
loader = Thread.currentThread().getContextClassLoader();
if (__log.isDebugEnabled() && loader != null) __log.debug("Using thread context classloader");
if (loader == null && ContextHandler.getCurrentContext() != null)
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
ClassLoader loader = tccl;
//If the thread context classloader is set, then try its hierarchy to find a matching context
if (loader != null)
{
if (__log.isDebugEnabled() && loader != null) __log.debug("Trying thread context classloader");
while (ctx == null && loader != null)
{
ctx = getContextForClassLoader(loader);
if (ctx == null && loader != null)
loader = loader.getParent();
}
if (ctx == null)
{
ctx = newNamingContext(obj, tccl, env, name, nameCtx);
__contextMap.put (tccl, ctx);
if(__log.isDebugEnabled())__log.debug("Made context "+name.get(0)+" for classloader: "+tccl);
}
return ctx;
}
//If trying thread context classloader hierarchy failed, try the
//classloader associated with the current context
if (ContextHandler.getCurrentContext() != null)
{
if (__log.isDebugEnabled() && loader != null) __log.debug("Trying classloader of current org.eclipse.jetty.server.handler.ContextHandler");
loader = ContextHandler.getCurrentContext().getContextHandler().getClassLoader();
if (__log.isDebugEnabled() && loader != null) __log.debug("Using classloader of current org.eclipse.jetty.server.handler.ContextHandler");
ctx = (Context)__contextMap.get(loader);
if (ctx == null && loader != null)
{
ctx = newNamingContext(obj, loader, env, name, nameCtx);
__contextMap.put (loader, ctx);
if(__log.isDebugEnabled())__log.debug("Made context "+name.get(0)+" for classloader: "+loader);
}
return ctx;
}
return null;
}
//Get the context matching the classloader
ctx = (Context)__contextMap.get(loader);
//The map does not contain an entry for this classloader
if (ctx == null)
{
//Didn't find a context to match, make one
Reference ref = (Reference)obj;
StringRefAddr parserAddr = (StringRefAddr)ref.get("parser");
String parserClassName = (parserAddr==null?null:(String)parserAddr.getContent());
NameParser parser = (NameParser)(parserClassName==null?null:loader.loadClass(parserClassName).newInstance());
/**
* Create a new NamingContext.
* @param obj
* @param loader
* @param env
* @param name
* @param parentCtx
* @return
* @throws Exception
*/
public NamingContext newNamingContext(Object obj, ClassLoader loader, Hashtable env, Name name, Context parentCtx)
throws Exception
{
Reference ref = (Reference)obj;
StringRefAddr parserAddr = (StringRefAddr)ref.get("parser");
String parserClassName = (parserAddr==null?null:(String)parserAddr.getContent());
NameParser parser = (NameParser)(parserClassName==null?null:loader.loadClass(parserClassName).newInstance());
ctx = new NamingContext (env,
name.get(0),
(NamingContext)nameCtx,
parser);
if(__log.isDebugEnabled())__log.debug("Made context "+name.get(0)+" for classloader: "+loader);
__contextMap.put (loader, ctx);
}
return ctx;
return new NamingContext (env,
name.get(0),
(NamingContext)parentCtx,
parser);
}
/**
* Find the naming Context for the given classloader
* @param loader
* @return
*/
public Context getContextForClassLoader(ClassLoader loader)
{
if (loader == null)
return null;
return (Context)__contextMap.get(loader);
}
/**

View File

@ -36,13 +36,17 @@ import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import javax.naming.spi.ObjectFactory;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.eclipse.jetty.jndi.ContextFactory;
import org.eclipse.jetty.jndi.NamingContext;
import org.eclipse.jetty.jndi.NamingUtil;
import org.eclipse.jetty.jndi.local.localContextRoot;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
@ -72,70 +76,137 @@ public class TestJNDI
}
}
@Test
public void testIt() throws Exception
public void testThreadContextClassloaderAndCurrentContext()
throws Exception
{
//set up some classloaders
Thread currentThread = Thread.currentThread();
ClassLoader currentLoader = currentThread.getContextClassLoader();
ClassLoader childLoader1 = new URLClassLoader(new URL[0], currentLoader);
ClassLoader childLoader2 = new URLClassLoader(new URL[0], currentLoader);
//create a jetty context, and start it so that its classloader it created
//and it is the current context
ClassLoader currentLoader = Thread.currentThread().getContextClassLoader();
ContextHandler ch = new ContextHandler();
URLClassLoader chLoader = new URLClassLoader(new URL[0], currentLoader);
ch.setClassLoader(chLoader);
//Create another one
ContextHandler ch2 = new ContextHandler();
URLClassLoader ch2Loader = new URLClassLoader(new URL[0], currentLoader);
ch2.setClassLoader(ch2Loader);
try
{
//Uncomment to aid with debug
/*
javaRootURLContext.getRoot().addListener(new NamingContext.Listener()
ch.setContextPath("/ch");
ch.addEventListener(new ServletContextListener()
{
public void unbind(NamingContext ctx, Binding binding)
{
System.err.println("java unbind "+binding+" from "+ctx.getName());
}
private Context comp;
private Object testObj = new Object();
public Binding bind(NamingContext ctx, Binding binding)
public void contextInitialized(ServletContextEvent sce)
{
System.err.println("java bind "+binding+" to "+ctx.getName());
return binding;
try
{
InitialContext initCtx = new InitialContext();
Context java = (Context)initCtx.lookup("java:");
assertNotNull(java);
comp = (Context)initCtx.lookup("java:comp");
assertNotNull(comp);
Context env = ((Context)comp).createSubcontext("env");
assertNotNull(env);
env.bind("ch", testObj);
}
catch (Exception e)
{
throw new IllegalStateException(e);
}
}
public void contextDestroyed(ServletContextEvent sce)
{
try
{
assertNotNull(comp);
assertEquals(testObj,comp.lookup("env/ch"));
comp.destroySubcontext("env");
}
catch (Exception e)
{
throw new IllegalStateException(e);
}
}
});
//Starting the context makes it current and creates a classloader for it
ch.start();
localContextRoot.getRoot().addListener(new NamingContext.Listener()
ch2.setContextPath("/ch2");
ch2.addEventListener(new ServletContextListener()
{
public void unbind(NamingContext ctx, Binding binding)
private Context comp;
private Object testObj = new Object();
public void contextInitialized(ServletContextEvent sce)
{
System.err.println("local unbind "+binding+" from "+ctx.getName());
try
{
InitialContext initCtx = new InitialContext();
comp = (Context)initCtx.lookup("java:comp");
assertNotNull(comp);
//another context's bindings should not be visible
Context env = ((Context)comp).createSubcontext("env");
try
{
env.lookup("ch");
fail("java:comp/env visible from another context!");
}
catch (NameNotFoundException e)
{
//expected
}
}
catch (Exception e)
{
throw new IllegalStateException(e);
}
}
public Binding bind(NamingContext ctx, Binding binding)
public void contextDestroyed(ServletContextEvent sce)
{
System.err.println("local bind "+binding+" to "+ctx.getName());
return binding;
try
{
assertNotNull(comp);
comp.destroySubcontext("env");
}
catch (Exception e)
{
throw new IllegalStateException(e);
}
}
});
*/
//set the current thread's classloader
currentThread.setContextClassLoader(childLoader1);
//make the new context the current one
ch2.start();
}
finally
{
ch.stop();
ch2.stop();
Thread.currentThread().setContextClassLoader(currentLoader);
}
}
@Test
public void testJavaNameParsing() throws Exception
{
Thread currentThread = Thread.currentThread();
ClassLoader currentLoader = currentThread.getContextClassLoader();
ClassLoader childLoader1 = new URLClassLoader(new URL[0], currentLoader);
//set the current thread's classloader
currentThread.setContextClassLoader(childLoader1);
InitialContext initCtxA = new InitialContext();
initCtxA.bind ("blah", "123");
assertEquals ("123", initCtxA.lookup("blah"));
initCtxA.destroySubcontext("blah");
try
{
initCtxA.lookup("blah");
fail("context blah was not destroyed");
}
catch (NameNotFoundException e)
{
//expected
}
try
{
InitialContext initCtx = new InitialContext();
Context sub0 = (Context)initCtx.lookup("java:");
@ -185,10 +256,74 @@ public class TestJNDI
Context fee = ncontext.createSubcontext("fee");
fee.bind ("fi", "88");
assertEquals("88", initCtxA.lookup("java:/fee/fi"));
assertEquals("88", initCtxA.lookup("java:/fee/fi/"));
assertTrue (initCtxA.lookup("java:/fee/") instanceof javax.naming.Context);
assertEquals("88", initCtx.lookup("java:/fee/fi"));
assertEquals("88", initCtx.lookup("java:/fee/fi/"));
assertTrue (initCtx.lookup("java:/fee/") instanceof javax.naming.Context);
}
finally
{
InitialContext ic = new InitialContext();
Context java = (Context)ic.lookup("java:");
java.destroySubcontext("fee");
currentThread.setContextClassLoader(currentLoader);
}
}
@Test
public void testIt() throws Exception
{
//set up some classloaders
Thread currentThread = Thread.currentThread();
ClassLoader currentLoader = currentThread.getContextClassLoader();
ClassLoader childLoader1 = new URLClassLoader(new URL[0], currentLoader);
ClassLoader childLoader2 = new URLClassLoader(new URL[0], currentLoader);
try
{
//Uncomment to aid with debug
/*
javaRootURLContext.getRoot().addListener(new NamingContext.Listener()
{
public void unbind(NamingContext ctx, Binding binding)
{
System.err.println("java unbind "+binding+" from "+ctx.getName());
}
public Binding bind(NamingContext ctx, Binding binding)
{
System.err.println("java bind "+binding+" to "+ctx.getName());
return binding;
}
});
localContextRoot.getRoot().addListener(new NamingContext.Listener()
{
public void unbind(NamingContext ctx, Binding binding)
{
System.err.println("local unbind "+binding+" from "+ctx.getName());
}
public Binding bind(NamingContext ctx, Binding binding)
{
System.err.println("local bind "+binding+" to "+ctx.getName());
return binding;
}
});
*/
//Set up the tccl before doing any jndi operations
currentThread.setContextClassLoader(childLoader1);
InitialContext initCtx = new InitialContext();
//Test we can lookup the root java: naming tree
Context sub0 = (Context)initCtx.lookup("java:");
assertNotNull(sub0);
//Test that we cannot bind java:comp as it should
//already be bound
try
{
Context sub1 = sub0.createSubcontext ("comp");
@ -201,8 +336,10 @@ public class TestJNDI
//check bindings at comp
Context sub1 = (Context)initCtx.lookup("java:comp");
assertNotNull(sub1);
Context sub2 = sub1.createSubcontext ("env");
assertNotNull(sub2);
initCtx.bind ("java:comp/env/rubbish", "abc");
assertEquals ("abc", initCtx.lookup("java:comp/env/rubbish"));
@ -305,7 +442,6 @@ public class TestJNDI
//expected failure to modify immutable context
}
//test what happens when you close an initial context that was used
initCtx.close();
}
finally
@ -320,61 +456,7 @@ public class TestJNDI
comp.destroySubcontext("env");
comp.unbind("crud");
comp.unbind("crud2");
}
}
@Test
public void testParent()
throws Exception
{
//set up some classloaders
Thread currentThread = Thread.currentThread();
ClassLoader parentLoader = currentThread.getContextClassLoader();
ClassLoader childLoader1 = new URLClassLoader(new URL[0], parentLoader);
try
{
//Test creating a comp for the parent loader does not leak to child
InitialContext initCtx = new InitialContext();
Context comp = (Context)initCtx.lookup("java:comp");
assertNotNull(comp);
Context env = (Context)comp.createSubcontext("env");
assertNotNull(env);
env.bind("foo", "aaabbbcccddd");
assertEquals("aaabbbcccddd", (String)initCtx.lookup("java:comp/env/foo"));
//Change to child loader
currentThread.setContextClassLoader(childLoader1);
comp = (Context)initCtx.lookup("java:comp");
Context childEnv = (Context)comp.createSubcontext("env");
assertNotSame(env, childEnv);
childEnv.bind("foo", "eeefffggghhh");
assertEquals("eeefffggghhh", (String)initCtx.lookup("java:comp/env/foo"));
//Change back to parent
currentThread.setContextClassLoader(parentLoader);
assertEquals("aaabbbcccddd", (String)initCtx.lookup("java:comp/env/foo"));
}
finally
{
//make some effort to clean up
InitialContext ic = new InitialContext();
currentThread.setContextClassLoader(parentLoader);
Context comp = (Context)ic.lookup("java:comp");
comp.destroySubcontext("env");
currentThread.setContextClassLoader(childLoader1);
comp = (Context)ic.lookup("java:comp");
comp.destroySubcontext("env");
currentThread.setContextClassLoader(currentLoader);
}
}
}

View File

@ -224,6 +224,15 @@ public class TestLocalJNDI
assertEquals("333", (String)o);
assertEquals("333", ic.lookup(name));
ic.destroySubcontext("a");
try
{
ic.lookup("a");
fail("context a was not destroyed");
}
catch (NameNotFoundException e)
{
//expected
}
name = parser.parse("");
name.add("x");

View File

@ -0,0 +1,126 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
<version>7.6.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-osgi-boot-logback</artifactId>
<name>Jetty :: OSGi :: Boot Logback</name>
<description>Jetty OSGi Boot Logback bundle</description>
<properties>
<bundle-symbolic-name>${project.groupId}.boot.logback</bundle-symbolic-name>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-boot</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>artifact-jar</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
<execution>
<id>test-jar</id>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
<configuration>
<archive>
<manifestFile>target/classes/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-SymbolicName>org.eclipse.jetty.osgi.boot.logback;singleton:=true</Bundle-SymbolicName>
<Bundle-Name>Jetty-OSGi-Logback Integration</Bundle-Name>
<Fragment-Host>org.eclipse.jetty.osgi.boot</Fragment-Host>
<Import-Package>
ch.qos.logback.access.jetty;version="[0.9,1.1)";resolution:=optional,
ch.qos.logback.access.jetty.v7;version="[0.9,1.1)";resolution:=optional,
ch.qos.logback.*;version="[0.9,1.1)",
org.osgi.framework.*,
org.slf4j.*,
*;resolution:=optional
</Import-Package>
<Export-Package>
!org.eclipse.jetty.osgi.boot.logback.internal.*,
org.eclipse.jetty.osgi.boot.logback.*;version="${parsedVersion.osgiVersion}"
</Export-Package>
<_nouses>true</_nouses>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<configuration>
<onlyAnalyze>org.eclipse.jetty.osgi.boot.logback.*</onlyAnalyze>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -133,29 +133,28 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
{
BundleContext context = FrameworkUtil.getBundle(JettyBootstrapActivator.class).getBundleContext();
ContextHandler contextHandler = (ContextHandler) context.getService(sr);
//if this was not a service that another of our deployers may have deployed (in which case they will undeploy it)
String watermark = (String)sr.getProperty(OSGiWebappConstants.WATERMARK);
if (watermark != null && !"".equals(watermark))
//Get a jetty deployer targetted to the named server instance, or the default one if not named
//The individual deployer will decide if it can remove the context or not
String serverName = (String)sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
Map<ServiceReference, ServiceProvider> candidates = getDeployers(serverName);
if (candidates != null)
{
//Get a jetty deployer targetted to the named server instance, or the default one if not named
String serverName = (String)sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
Map<ServiceReference, ServiceProvider> candidates = getDeployers(serverName);
if (candidates != null)
boolean removed = false;
Iterator<Entry<ServiceReference, ServiceProvider>> itor = candidates.entrySet().iterator();
while (!removed && itor.hasNext())
{
boolean removed = false;
Iterator<Entry<ServiceReference, ServiceProvider>> itor = candidates.entrySet().iterator();
while (!removed && itor.hasNext())
Entry<ServiceReference, ServiceProvider> e = itor.next();
try
{
Entry<ServiceReference, ServiceProvider> e = itor.next();
try
{
removed = e.getValue().serviceRemoved(sr, contextHandler);
}
catch (Exception x)
{
LOG.warn("Error undeploying service representing jetty context ", x);
}
removed = e.getValue().serviceRemoved(sr, contextHandler);
}
catch (Exception x)
{
LOG.warn("Error undeploying service representing jetty context ", x);
}
}
}
@ -181,7 +180,7 @@ public class JettyContextHandlerServiceTracker implements ServiceListener
}
String watermark = (String)sr.getProperty(OSGiWebappConstants.WATERMARK);
if (watermark != null && !"".equals(watermark))
return; //another of our deployers is responsible for handling service registrations for this context
return; //one of our deployers just registered the context as an OSGi service, so we can ignore it
//Get a jetty deployer targetted to the named server instance, or the default one if not named
String serverName = (String)sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);

View File

@ -0,0 +1,120 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
<version>7.6.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-osgi-equinoxtools</artifactId>
<name>Jetty :: OSGi :: Example Equinox Tools</name>
<description>Jetty OSGi Example Equinox Tools</description>
<properties>
<bundle-symbolic-name>${project.groupId}.equinoxtools</bundle-symbolic-name>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-continuation</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>process-resources</phase>
<configuration>
<tasks>
<copy todir="target/classes/equinoxconsole">
<fileset dir="equinoxconsole" />
</copy>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>artifact-jar</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
<execution>
<id>test-jar</id>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
<configuration>
<archive>
<manifestFile>target/classes/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-SymbolicName>org.eclipse.jetty.osgi.equinoxtools</Bundle-SymbolicName>
<Bundle-Name>Console</Bundle-Name>
<Bundle-Activator>org.eclipse.jetty.osgi.equinoxtools.WebEquinoxToolsActivator</Bundle-Activator>
<Export-Package>org.eclipse.jetty.osgi.equinoxtools;x-internal:=true;version="${parsedVersion.osgiVersion}",
org.eclipse.jetty.osgi.equinoxtools.console;x-internal:=true;version="${parsedVersion.osgiVersion}"
</Export-Package>
<_nouses>true</_nouses>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<configuration>
<onlyAnalyze>org.eclipse.jetty.osgi.equinoxtools.*</onlyAnalyze>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -108,6 +108,8 @@
compilation time. -->
<_nouses>true</_nouses>
<Import-Package>
javax.servlet;version="2.6.0",
javax.servlet.resources;version="2.6.0",
org.osgi.framework,
org.osgi.service.cm;version="1.2.0",
org.osgi.service.packageadmin,

View File

@ -21,6 +21,9 @@ package com.acme.osgi;
import java.util.Dictionary;
import java.util.Hashtable;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.osgi.framework.Bundle;
@ -43,9 +46,21 @@ public class Activator implements BundleActivator
*
* @param context
*/
public void start(BundleContext context) throws Exception
public void start(final BundleContext context) throws Exception
{
ContextHandler ch = new ContextHandler();
ch.addEventListener(new ServletContextListener ()
{
public void contextInitialized(ServletContextEvent sce)
{
System.err.println("Context is initialized");
}
public void contextDestroyed(ServletContextEvent sce)
{
System.err.println("CONTEXT IS DESTROYED!");
}
});
Dictionary props = new Hashtable();
props.put("contextPath","/acme");
props.put("Jetty-ContextFilePath", "acme.xml");

View File

@ -38,6 +38,7 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.CoreOptions;
import org.ops4j.pax.exam.Inject;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.container.def.PaxRunnerOptions;
@ -69,7 +70,9 @@ public class JettyOSGiBootContextAsService
{
ArrayList<Option> options = new ArrayList<Option>();
options.addAll(TestJettyOSGiBootCore.provisionCoreJetty());
options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*",
"org.w3c.*", "javax.xml.*"));
File base = MavenTestingUtils.getBasedir();
File src = new File (base, "src");
File tst = new File (src, "test");
@ -114,7 +117,7 @@ public class JettyOSGiBootContextAsService
//a bundle that registers a webapp as a service for the jetty osgi core to pick up and deploy
mavenBundle().groupId( "org.eclipse.jetty.osgi" ).artifactId( "test-jetty-osgi-context" ).versionAsInProject().start()
// mavenBundle().groupId( "org.eclipse.equinox.http" ).artifactId( "servlet" ).versionAsInProject().start()
// mavenBundle().groupId( "org.eclipse.equinox.http" ).artifactId( "servlet" ).versionAsInProject().start()
)));
return options.toArray(new Option[options.size()]);
@ -173,8 +176,18 @@ public class JettyOSGiBootContextAsService
ServiceReference[] refs = bundleContext.getServiceReferences(ContextHandler.class.getName(), null);
Assert.assertNotNull(refs);
Assert.assertEquals(1,refs.length);
String[] keys = refs[0].getPropertyKeys();
if (keys != null)
{
for (String k:keys)
System.err.println("service property: "+k+", "+refs[0].getProperty(k));
}
ContextHandler ch = (ContextHandler)bundleContext.getService(refs[0]);
Assert.assertEquals("/acme", ch.getContextPath());
testWebBundle.stop();
//Check you can see CONTEXT DESTROYED on stderr. TODO: think of a better way to communicate this to the test
}

View File

@ -39,6 +39,7 @@ import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.CoreOptions;
import org.ops4j.pax.exam.Inject;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.container.def.PaxRunnerOptions;
@ -72,7 +73,8 @@ public class TestJettyOSGiBootWebAppAsService
{
ArrayList<Option> options = new ArrayList<Option>();
options.addAll(TestJettyOSGiBootCore.provisionCoreJetty());
options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*",
"org.w3c.*", "javax.xml.*"));
File base = MavenTestingUtils.getBasedir();
File src = new File (base, "src");
File tst = new File (src, "test");
@ -128,7 +130,6 @@ public class TestJettyOSGiBootWebAppAsService
* plus your testcase, wrapped into a bundle called pax-exam-probe
*/
@Test
@Ignore
public void listBundles() throws Exception
{
Map<String,Bundle> bundlesIndexedBySymbolicName = new HashMap<String, Bundle>();

View File

@ -41,6 +41,7 @@ import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.CoreOptions;
import org.ops4j.pax.exam.Inject;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.container.def.PaxRunnerOptions;
@ -82,7 +83,8 @@ public class TestJettyOSGiBootWithJsp
ArrayList<Option> options = new ArrayList<Option>();
options.addAll(TestJettyOSGiBootCore.provisionCoreJetty());
options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*",
"org.w3c.*", "javax.xml.*"));
// Enable Logging
if(LOGGING_ENABLED) {
options.addAll(Arrays.asList(options(
@ -132,7 +134,6 @@ public class TestJettyOSGiBootWithJsp
* plus your testcase, wrapped into a bundle called pax-exam-probe
*/
@Test
@Ignore
public void listBundles() throws Exception
{
Map<String,Bundle> bundlesIndexedBySymbolicName = new HashMap<String, Bundle>();

View File

@ -443,6 +443,7 @@ public abstract class AbstractHttpConnection extends AbstractConnection
// within the call to unhandle().
final Server server=_server;
boolean was_continuation=_request._async.isContinuation();
boolean handling=_request._async.handling() && server!=null && server.isRunning();
while (handling)
{
@ -489,7 +490,15 @@ public abstract class AbstractHttpConnection extends AbstractConnection
}
else
{
_request.setDispatcherType(DispatcherType.ASYNC);
if (_request._async.isExpired()&&!was_continuation)
{
_response.setStatus(500,"Async Timeout");
_request.setAttribute(Dispatcher.ERROR_STATUS_CODE,new Integer(500));
_request.setAttribute(Dispatcher.ERROR_MESSAGE, "Async Timeout");
_request.setDispatcherType(DispatcherType.ERROR);
}
else
_request.setDispatcherType(DispatcherType.ASYNC);
server.handleAsync(this);
}
}
@ -530,6 +539,7 @@ public abstract class AbstractHttpConnection extends AbstractConnection
}
finally
{
was_continuation=_request._async.isContinuation();
handling = !_request._async.unhandle() && server.isRunning() && _server!=null;
}
}

View File

@ -43,6 +43,7 @@ import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Timeout;
import org.omg.CosNaming.IstringHelper;
/* ------------------------------------------------------------ */
/** Implementation of Continuation and AsyncContext interfaces
@ -200,6 +201,11 @@ public class AsyncContinuation implements AsyncContext, Continuation
}
}
public boolean isContinuation()
{
return _continuation;
}
/* ------------------------------------------------------------ */
/* (non-Javadoc)
* @see javax.servlet.ServletRequest#isSuspended()
@ -525,8 +531,6 @@ public class AsyncContinuation implements AsyncContext, Continuation
}
}
}
synchronized (this)
{
@ -534,11 +538,12 @@ public class AsyncContinuation implements AsyncContext, Continuation
{
case __ASYNCSTARTED:
case __ASYNCWAIT:
if (_continuation)
dispatch();
else
// TODO maybe error dispatch?
complete();
dispatch();
break;
default:
if (!_continuation)
_expired=false;
}
}
@ -937,7 +942,7 @@ public class AsyncContinuation implements AsyncContext, Continuation
/* ------------------------------------------------------------ */
protected void suspend(final ServletContext context,
protected void startAsync(final ServletContext context,
final ServletRequest request,
final ServletResponse response)
{
@ -952,6 +957,14 @@ public class AsyncContinuation implements AsyncContext, Continuation
}
}
/* ------------------------------------------------------------ */
protected void startAsync()
{
_responseWrapped=false;
_continuation=false;
doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),_connection.getResponse());
}
/* ------------------------------------------------------------ */
/**

View File

@ -1980,7 +1980,7 @@ public class Request implements HttpServletRequest
{
if (!_asyncSupported)
throw new IllegalStateException("!asyncSupported");
_async.suspend();
_async.startAsync();
return _async;
}
@ -1989,7 +1989,7 @@ public class Request implements HttpServletRequest
{
if (!_asyncSupported)
throw new IllegalStateException("!asyncSupported");
_async.suspend(_context,servletRequest,servletResponse);
_async.startAsync(_context,servletRequest,servletResponse);
return _async;
}

View File

@ -157,7 +157,7 @@ public class ShutdownMonitor extends Thread
return;
}
while (true)
while (serverSocket != null)
{
Socket socket = null;
try
@ -190,7 +190,9 @@ public class ShutdownMonitor extends Thread
// Shutdown Monitor
debug("Shutting down monitor");
close(socket);
socket = null;
close(serverSocket);
serverSocket = null;
if (exitVm)
{

View File

@ -946,7 +946,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
if (old_context != _scontext)
{
// check the target.
if (DispatcherType.REQUEST.equals(dispatch) || DispatcherType.ASYNC.equals(dispatch))
if (DispatcherType.REQUEST.equals(dispatch) || DispatcherType.ASYNC.equals(dispatch) || (DispatcherType.ERROR.equals(dispatch) && baseRequest.getAsyncContinuation().isExpired()))
{
if (_compactPath)
target = URIUtil.compactPath(target);
@ -2327,6 +2327,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
if (!_enabled)
throw new UnsupportedOperationException();
ContextHandler.this.addEventListener(t);
ContextHandler.this.restrictEventListener(t);
}
@Override

View File

@ -201,6 +201,12 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
checkValid();
return _lastAccessed;
}
/* ------------------------------------------------------------- */
public void setLastAccessedTime(long time)
{
_lastAccessed = time;
}
/* ------------------------------------------------------------- */
public int getMaxInactiveInterval()
@ -307,16 +313,19 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
_manager.removeSession(this,true);
// Notify listeners and unbind values
boolean do_invalidate=false;
synchronized (this)
{
if (!_invalid)
{
if (_requests<=0)
doInvalidate();
do_invalidate=true;
else
_doInvalidate=true;
}
}
if (do_invalidate)
doInvalidate();
}
/* ------------------------------------------------------------- */

View File

@ -37,6 +37,7 @@ public abstract class AbstractSessionIdManager extends AbstractLifeCycle impleme
protected Random _random;
protected boolean _weakRandom;
protected String _workerName;
protected long _reseed=100000L;
/* ------------------------------------------------------------ */
public AbstractSessionIdManager()
@ -50,6 +51,24 @@ public abstract class AbstractSessionIdManager extends AbstractLifeCycle impleme
}
/* ------------------------------------------------------------ */
/**
* @return the reseed probability
*/
public long getReseed()
{
return _reseed;
}
/* ------------------------------------------------------------ */
/** Set the reseed probability.
* @param reseed If non zero then when a random long modulo the reseed value == 1, the {@link SecureRandom} will be reseeded.
*/
public void setReseed(long reseed)
{
_reseed = reseed;
}
/* ------------------------------------------------------------ */
/**
* Get the workname. If set, the workername is dot appended to the session
@ -125,6 +144,22 @@ public abstract class AbstractSessionIdManager extends AbstractLifeCycle impleme
:_random.nextLong();
if (r0<0)
r0=-r0;
// random chance to reseed
if (_reseed>0 && (r0%_reseed)== 1L)
{
LOG.debug("Reseeding {}",this);
if (_random instanceof SecureRandom)
{
SecureRandom secure = (SecureRandom)_random;
secure.setSeed(secure.generateSeed(8));
}
else
{
_random.setSeed(_random.nextLong()^System.currentTimeMillis()^request.hashCode()^Runtime.getRuntime().freeMemory());
}
}
long r1=_weakRandom
?(hashCode()^Runtime.getRuntime().freeMemory()^_random.nextInt()^(((long)request.hashCode())<<32))
:_random.nextLong();

View File

@ -821,7 +821,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
statement = connection.prepareStatement(_deleteOldExpiredSessions);
statement.setLong(1, upperBound);
int rows = statement.executeUpdate();
if (LOG.isDebugEnabled()) LOG.debug("Deleted "+rows+" rows");
if (LOG.isDebugEnabled()) LOG.debug("Deleted "+rows+" rows of old sessions expired before "+upperBound);
}
}
}

View File

@ -80,141 +80,116 @@ public class JDBCSessionManager extends AbstractSessionManager
protected JDBCSessionIdManager _jdbcSessionIdMgr = null;
protected long _saveIntervalSec = 60; //only persist changes to session access times every 60 secs
/**
* SessionData
* Session
*
* Persistable data about a session.
* Session instance.
*/
public class SessionData
public class Session extends AbstractSession
{
private final String _id;
private String _rowId;
private long _accessed;
private long _lastAccessed;
private long _maxIdleMs=-1;
private static final long serialVersionUID = 5208464051134226143L;
/**
* If dirty, session needs to be (re)persisted
*/
private boolean _dirty=false;
/**
* Time in msec since the epoch that a session cookie was set for this session
*/
private long _cookieSet;
private long _created;
private Map<String,Object> _attributes;
private String _lastNode;
private String _canonicalContext;
private long _lastSaved;
/**
* Time in msec since the epoch that the session will expire
*/
private long _expiryTime;
/**
* Time in msec since the epoch that the session was last persisted
*/
private long _lastSaved;
/**
* Unique identifier of the last node to host the session
*/
private String _lastNode;
/**
* Virtual host for context (used to help distinguish 2 sessions with same id on different contexts)
*/
private String _virtualHost;
public SessionData (String sessionId)
/**
* Unique row in db for session
*/
private String _rowId;
/**
* Mangled context name (used to help distinguish 2 sessions with same id on different contexts)
*/
private String _canonicalContext;
/**
* Session from a request.
*
* @param request
*/
protected Session (HttpServletRequest request)
{
_id=sessionId;
_created=System.currentTimeMillis();
_accessed = _created;
_attributes = new HashMap<String,Object>();
super(JDBCSessionManager.this,request);
int maxInterval=getMaxInactiveInterval();
_expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
_virtualHost = JDBCSessionManager.getVirtualHost(_context);
_canonicalContext = canonicalize(_context.getContextPath());
_lastNode = getSessionIdManager().getWorkerName();
}
public SessionData (String sessionId,Map<String,Object> attributes)
/**
* Session restored from database
* @param sessionId
* @param rowId
* @param created
* @param accessed
*/
protected Session (String sessionId, String rowId, long created, long accessed)
{
_id=sessionId;
_created=System.currentTimeMillis();
_accessed = _created;
_attributes = attributes;
_lastNode = getSessionIdManager().getWorkerName();
super(JDBCSessionManager.this, created, accessed, sessionId);
_rowId = rowId;
}
public synchronized String getId ()
{
return _id;
}
public synchronized long getCreated ()
{
return _created;
}
protected synchronized void setCreated (long ms)
{
_created = ms;
}
public synchronized long getAccessed ()
{
return _accessed;
}
protected synchronized void setAccessed (long ms)
{
_accessed = ms;
}
public synchronized void setMaxIdleMs (long ms)
{
_maxIdleMs = ms;
}
public synchronized long getMaxIdleMs()
{
return _maxIdleMs;
}
public synchronized void setLastAccessed (long ms)
{
_lastAccessed = ms;
}
public synchronized long getLastAccessed()
{
return _lastAccessed;
}
public void setCookieSet (long ms)
{
_cookieSet = ms;
}
public synchronized long getCookieSet ()
{
return _cookieSet;
}
public synchronized void setRowId (String rowId)
{
_rowId=rowId;
}
protected synchronized String getRowId()
{
return _rowId;
}
protected synchronized Map<String,Object> getAttributeMap ()
protected synchronized void setRowId(String rowId)
{
return _attributes;
_rowId = rowId;
}
public synchronized void setVirtualHost (String vhost)
{
_virtualHost=vhost;
}
protected synchronized void setAttributeMap (Map<String,Object> map)
public synchronized String getVirtualHost ()
{
_attributes = map;
return _virtualHost;
}
public synchronized void setLastNode (String node)
{
_lastNode=node;
}
public synchronized String getLastNode ()
{
return _lastNode;
}
public synchronized void setCanonicalContext(String str)
{
_canonicalContext=str;
}
public synchronized String getCanonicalContext ()
{
return _canonicalContext;
}
public synchronized long getLastSaved ()
{
return _lastSaved;
@ -234,91 +209,57 @@ public class JDBCSessionManager extends AbstractSessionManager
{
return _expiryTime;
}
public synchronized void setVirtualHost (String vhost)
public synchronized void setCanonicalContext(String str)
{
_virtualHost=vhost;
_canonicalContext=str;
}
public synchronized String getVirtualHost ()
public synchronized String getCanonicalContext ()
{
return _virtualHost;
return _canonicalContext;
}
public void setCookieSet (long ms)
{
_cookieSet = ms;
}
public synchronized long getCookieSet ()
{
return _cookieSet;
}
public synchronized void setLastNode (String node)
{
_lastNode=node;
}
public synchronized String getLastNode ()
{
return _lastNode;
}
@Override
public String toString ()
{
return "Session rowId="+_rowId+",id="+_id+",lastNode="+_lastNode+
",created="+_created+",accessed="+_accessed+
",lastAccessed="+_lastAccessed+",cookieSet="+_cookieSet+
"lastSaved="+_lastSaved;
}
}
/**
* Session
*
* Session instance in memory of this node.
*/
public class Session extends AbstractSession
{
private static final long serialVersionUID = 5208464051134226143L;
private final SessionData _data;
private boolean _dirty=false;
/**
* Session from a request.
*
* @param request
*/
protected Session (HttpServletRequest request)
{
super(JDBCSessionManager.this,request);
_data = new SessionData(getClusterId(),getAttributeMap());
if (_dftMaxIdleSecs>0)
_data.setMaxIdleMs(_dftMaxIdleSecs*1000L);
_data.setCanonicalContext(canonicalize(_context.getContextPath()));
_data.setVirtualHost(getVirtualHost(_context));
int maxInterval=getMaxInactiveInterval();
_data.setExpiryTime(maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
}
/**
* Session restored in database.
* @param data
*/
protected Session (long accessed, SessionData data)
{
super(JDBCSessionManager.this,data.getCreated(), accessed, data.getId());
_data=data;
if (_dftMaxIdleSecs>0)
_data.setMaxIdleMs(_dftMaxIdleSecs*1000L);
addAttributes(_data.getAttributeMap());
_data.setAttributeMap(getAttributeMap());
}
@Override
public void setAttribute (String name, Object value)
{
super.setAttribute(name, value);
_dirty=true;
}
{
super.setAttribute(name, value);
_dirty=true;
}
@Override
@Override
public void removeAttribute (String name)
{
super.removeAttribute(name);
_dirty=true;
}
{
super.removeAttribute(name);
_dirty=true;
}
@Override
@Override
protected void cookieSet()
{
_data.setCookieSet(_data.getAccessed());
}
{
_cookieSet = getAccessed();
}
/**
* Entry to session.
@ -329,17 +270,19 @@ public class JDBCSessionManager extends AbstractSessionManager
@Override
protected boolean access(long time)
{
if (super.access(time))
synchronized (this)
{
_data.setLastAccessed(_data.getAccessed());
_data.setAccessed(time);
int maxInterval=getMaxInactiveInterval();
_data.setExpiryTime(maxInterval <= 0 ? 0 : (time + maxInterval*1000L));
return true;
if (super.access(time))
{
int maxInterval=getMaxInactiveInterval();
_expiryTime = (maxInterval <= 0 ? 0 : (time + maxInterval*1000L));
return true;
}
return false;
}
return false;
}
/**
* Exit from session
@ -348,29 +291,32 @@ public class JDBCSessionManager extends AbstractSessionManager
@Override
protected void complete()
{
super.complete();
try
synchronized (this)
{
if (_dirty)
super.complete();
try
{
//The session attributes have changed, write to the db, ensuring
//http passivation/activation listeners called
willPassivate();
updateSession(_data);
didActivate();
if (_dirty)
{
//The session attributes have changed, write to the db, ensuring
//http passivation/activation listeners called
willPassivate();
updateSession(this);
didActivate();
}
else if ((getAccessed() - _lastSaved) >= (getSaveInterval() * 1000L))
{
updateSessionAccessTime(this);
}
}
else if ((_data._accessed - _data._lastSaved) >= (getSaveInterval() * 1000L))
catch (Exception e)
{
updateSessionAccessTime(_data);
LOG.warn("Problem persisting changed session data id="+getId(), e);
}
finally
{
_dirty=false;
}
}
catch (Exception e)
{
LOG.warn("Problem persisting changed session data id="+getId(), e);
}
finally
{
_dirty=false;
}
}
@ -381,6 +327,15 @@ public class JDBCSessionManager extends AbstractSessionManager
LOG.debug("Timing out session id="+getClusterId());
super.timeout();
}
@Override
public String toString ()
{
return "Session rowId="+_rowId+",id="+getId()+",lastNode="+_lastNode+
",created="+getCreationTime()+",accessed="+getAccessed()+
",lastAccessed="+getLastAccessedTime()+",cookieSet="+_cookieSet+
",lastSaved="+_lastSaved+",expiry="+_expiryTime;
}
}
@ -389,7 +344,7 @@ public class JDBCSessionManager extends AbstractSessionManager
/**
* ClassLoadingObjectInputStream
*
*
* Used to persist the session attribute map
*/
protected class ClassLoadingObjectInputStream extends ObjectInputStream
{
@ -418,8 +373,6 @@ public class JDBCSessionManager extends AbstractSessionManager
}
/**
* Set the time in seconds which is the interval between
* saving the session access time to the database.
@ -468,7 +421,7 @@ public class JDBCSessionManager extends AbstractSessionManager
/**
* A session has been requested by it's id on this node.
* A session has been requested by its id on this node.
*
* Load the session by id AND context path from the database.
* Multiple contexts may share the same session id (due to dispatching)
@ -486,12 +439,11 @@ public class JDBCSessionManager extends AbstractSessionManager
@Override
public Session getSession(String idInCluster)
{
Session session = (Session)_sessions.get(idInCluster);
Session session = null;
Session memSession = (Session)_sessions.get(idInCluster);
synchronized (this)
{
try
{
//check if we need to reload the session -
//as an optimization, don't reload on every access
//to reduce the load on the database. This introduces a window of
@ -500,83 +452,94 @@ public class JDBCSessionManager extends AbstractSessionManager
//re-migrated to this node. This should be an extremely rare occurrence,
//as load-balancers are generally well-behaved and consistently send
//sessions to the same node, changing only iff that node fails.
SessionData data = null;
//Session data = null;
long now = System.currentTimeMillis();
if (LOG.isDebugEnabled())
{
if (session==null)
if (memSession==null)
LOG.debug("getSession("+idInCluster+"): not in session map,"+
" now="+now+
" lastSaved="+(session==null?0:session._data._lastSaved)+
" lastSaved="+(memSession==null?0:memSession._lastSaved)+
" interval="+(_saveIntervalSec * 1000L));
else
LOG.debug("getSession("+idInCluster+"): in session map, "+
" now="+now+
" lastSaved="+(session==null?0:session._data._lastSaved)+
" lastSaved="+(memSession==null?0:memSession._lastSaved)+
" interval="+(_saveIntervalSec * 1000L)+
" lastNode="+session._data.getLastNode()+
" lastNode="+memSession._lastNode+
" thisNode="+getSessionIdManager().getWorkerName()+
" difference="+(now - session._data._lastSaved));
" difference="+(now - memSession._lastSaved));
}
if (session==null || ((now - session._data._lastSaved) >= (_saveIntervalSec * 1000L)))
try
{
LOG.debug("getSession("+idInCluster+"): no session in session map or stale session. Reloading session data from db.");
data = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
}
else if ((now - session._data._lastSaved) >= (_saveIntervalSec * 1000L))
{
LOG.debug("getSession("+idInCluster+"): stale session. Reloading session data from db.");
data = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
}
else
{
LOG.debug("getSession("+idInCluster+"): session in session map");
data = session._data;
}
if (data != null)
{
if (!data.getLastNode().equals(getSessionIdManager().getWorkerName()) || session==null)
if (memSession==null)
{
//if the session has no expiry, or it is not already expired
if (data._expiryTime <= 0 || data._expiryTime > now)
LOG.debug("getSession("+idInCluster+"): no session in session map. Reloading session data from db.");
session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
}
else if ((now - memSession._lastSaved) >= (_saveIntervalSec * 1000L))
{
LOG.debug("getSession("+idInCluster+"): stale session. Reloading session data from db.");
session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
}
else
{
LOG.debug("getSession("+idInCluster+"): session in session map");
session = memSession;
}
}
catch (Exception e)
{
LOG.warn("Unable to load session "+idInCluster, e);
return null;
}
//If we have a session
if (session != null)
{
//If the session was last used on a different node, or session doesn't exist on this node
if (!session.getLastNode().equals(getSessionIdManager().getWorkerName()) || memSession==null)
{
//if session doesn't expire, or has not already expired, update it and put it in this nodes' memory
if (session._expiryTime <= 0 || session._expiryTime > now)
{
if (LOG.isDebugEnabled()) LOG.debug("getSession("+idInCluster+"): lastNode="+data.getLastNode()+" thisNode="+getSessionIdManager().getWorkerName());
data.setLastNode(getSessionIdManager().getWorkerName());
//session last used on a different node, or we don't have it in memory
session = new Session(now,data);
if (LOG.isDebugEnabled())
LOG.debug("getSession("+idInCluster+"): lastNode="+session.getLastNode()+" thisNode="+getSessionIdManager().getWorkerName());
session.setLastNode(getSessionIdManager().getWorkerName());
_sessions.put(idInCluster, session);
session.didActivate();
//TODO is this the best way to do this? Or do this on the way out using
//the _dirty flag?
updateSessionNode(data);
//update in db: if unable to update, session will be scavenged later
try
{
updateSessionNode(session);
session.didActivate();
}
catch (Exception e)
{
LOG.warn("Unable to update freshly loaded session "+idInCluster, e);
return null;
}
}
else
{
LOG.debug("getSession ({}): Session has expired", idInCluster);
LOG.debug("getSession ({}): Session has expired", idInCluster);
session=null;
}
}
else
LOG.debug("getSession({}): Session not stale {}", idInCluster,session._data);
//session in db shares same id, but is not for this context
LOG.debug("getSession({}): Session not stale {}", idInCluster,session);
}
else
{
//No session in db with matching id and context path.
session=null;
LOG.debug("getSession({}): No session in database matching id={}",idInCluster,idInCluster);
}
return session;
}
catch (Exception e)
{
LOG.warn("Unable to load session from database", e);
return null;
}
}
}
@ -676,7 +639,7 @@ public class JDBCSessionManager extends AbstractSessionManager
try
{
if (session != null)
deleteSession(session._data);
deleteSession(session);
}
catch (Exception e)
{
@ -707,9 +670,12 @@ public class JDBCSessionManager extends AbstractSessionManager
//then session data will be lost.
try
{
session.willPassivate();
storeSession(((JDBCSessionManager.Session)session)._data);
session.didActivate();
synchronized (session)
{
session.willPassivate();
storeSession(((JDBCSessionManager.Session)session));
session.didActivate();
}
}
catch (Exception e)
{
@ -828,17 +794,17 @@ public class JDBCSessionManager extends AbstractSessionManager
* @return the session data that was loaded
* @throws Exception
*/
protected SessionData loadSession (final String id, final String canonicalContextPath, final String vhost)
protected Session loadSession (final String id, final String canonicalContextPath, final String vhost)
throws Exception
{
final AtomicReference<SessionData> _reference = new AtomicReference<SessionData>();
final AtomicReference<Session> _reference = new AtomicReference<Session>();
final AtomicReference<Exception> _exception = new AtomicReference<Exception>();
Runnable load = new Runnable()
{
@SuppressWarnings("unchecked")
public void run()
{
SessionData data = null;
Session session = null;
Connection connection=null;
PreparedStatement statement = null;
try
@ -847,29 +813,26 @@ public class JDBCSessionManager extends AbstractSessionManager
statement = _jdbcSessionIdMgr._dbAdaptor.getLoadStatement(connection, id, canonicalContextPath, vhost);
ResultSet result = statement.executeQuery();
if (result.next())
{
data = new SessionData(id);
data.setRowId(result.getString(_jdbcSessionIdMgr._sessionTableRowId));
data.setCookieSet(result.getLong("cookieTime"));
data.setLastAccessed(result.getLong("lastAccessTime"));
data.setAccessed (result.getLong("accessTime"));
data.setCreated(result.getLong("createTime"));
data.setLastNode(result.getString("lastNode"));
data.setLastSaved(result.getLong("lastSavedTime"));
data.setExpiryTime(result.getLong("expiryTime"));
data.setCanonicalContext(result.getString("contextPath"));
data.setVirtualHost(result.getString("virtualHost"));
{
session = new Session(id, result.getString(_jdbcSessionIdMgr._sessionTableRowId), result.getLong("createTime"), result.getLong("accessTime"));
session.setCookieSet(result.getLong("cookieTime"));
session.setLastAccessedTime(result.getLong("lastAccessTime"));
session.setLastNode(result.getString("lastNode"));
session.setLastSaved(result.getLong("lastSavedTime"));
session.setExpiryTime(result.getLong("expiryTime"));
session.setCanonicalContext(result.getString("contextPath"));
session.setVirtualHost(result.getString("virtualHost"));
InputStream is = ((JDBCSessionIdManager)getSessionIdManager())._dbAdaptor.getBlobInputStream(result, "map");
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream (is);
Object o = ois.readObject();
data.setAttributeMap((Map<String,Object>)o);
session.addAttributes((Map<String,Object>)o);
ois.close();
if (LOG.isDebugEnabled())
LOG.debug("LOADED session "+data);
LOG.debug("LOADED session "+session);
}
_reference.set(data);
_reference.set(session);
}
catch (Exception e)
{
@ -892,7 +855,12 @@ public class JDBCSessionManager extends AbstractSessionManager
_context.getContextHandler().handle(load);
if (_exception.get()!=null)
{
//if the session could not be restored, take its id out of the pool of currently-in-use
//session ids
_jdbcSessionIdMgr.removeSession(id);
throw _exception.get();
}
return _reference.get();
}
@ -903,10 +871,10 @@ public class JDBCSessionManager extends AbstractSessionManager
* @param data
* @throws Exception
*/
protected void storeSession (SessionData data)
protected void storeSession (Session session)
throws Exception
{
if (data==null)
if (session==null)
return;
//put into the database
@ -914,38 +882,38 @@ public class JDBCSessionManager extends AbstractSessionManager
PreparedStatement statement = null;
try
{
String rowId = calculateRowId(data);
String rowId = calculateRowId(session);
long now = System.currentTimeMillis();
connection.setAutoCommit(true);
statement = connection.prepareStatement(_jdbcSessionIdMgr._insertSession);
statement.setString(1, rowId); //rowId
statement.setString(2, data.getId()); //session id
statement.setString(3, data.getCanonicalContext()); //context path
statement.setString(4, data.getVirtualHost()); //first vhost
statement.setString(2, session.getId()); //session id
statement.setString(3, session.getCanonicalContext()); //context path
statement.setString(4, session.getVirtualHost()); //first vhost
statement.setString(5, getSessionIdManager().getWorkerName());//my node id
statement.setLong(6, data.getAccessed());//accessTime
statement.setLong(7, data.getLastAccessed()); //lastAccessTime
statement.setLong(8, data.getCreated()); //time created
statement.setLong(9, data.getCookieSet());//time cookie was set
statement.setLong(6, session.getAccessed());//accessTime
statement.setLong(7, session.getLastAccessedTime()); //lastAccessTime
statement.setLong(8, session.getCreationTime()); //time created
statement.setLong(9, session.getCookieSet());//time cookie was set
statement.setLong(10, now); //last saved time
statement.setLong(11, data.getExpiryTime());
statement.setLong(11, session.getExpiryTime());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(data.getAttributeMap());
oos.writeObject(session.getAttributeMap());
byte[] bytes = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
statement.setBinaryStream(12, bais, bytes.length);//attribute map as blob
statement.executeUpdate();
data.setRowId(rowId); //set it on the in-memory data as well as in db
data.setLastSaved(now);
session.setRowId(rowId); //set it on the in-memory data as well as in db
session.setLastSaved(now);
if (LOG.isDebugEnabled())
LOG.debug("Stored session "+data);
LOG.debug("Stored session "+session);
}
finally
{
@ -958,10 +926,10 @@ public class JDBCSessionManager extends AbstractSessionManager
/**
* Update data on an existing persisted session.
*
* @param data
* @param data the session
* @throws Exception
*/
protected void updateSession (SessionData data)
protected void updateSession (Session data)
throws Exception
{
if (data==null)
@ -976,7 +944,7 @@ public class JDBCSessionManager extends AbstractSessionManager
statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSession);
statement.setString(1, getSessionIdManager().getWorkerName());//my node id
statement.setLong(2, data.getAccessed());//accessTime
statement.setLong(3, data.getLastAccessed()); //lastAccessTime
statement.setLong(3, data.getLastAccessedTime()); //lastAccessTime
statement.setLong(4, now); //last saved time
statement.setLong(5, data.getExpiryTime());
@ -1005,10 +973,10 @@ public class JDBCSessionManager extends AbstractSessionManager
/**
* Update the node on which the session was last seen to be my node.
*
* @param data
* @param data the session
* @throws Exception
*/
protected void updateSessionNode (SessionData data)
protected void updateSessionNode (Session data)
throws Exception
{
String nodeId = getSessionIdManager().getWorkerName();
@ -1035,10 +1003,10 @@ public class JDBCSessionManager extends AbstractSessionManager
/**
* Persist the time the session was last accessed.
*
* @param data
* @param data the session
* @throws Exception
*/
private void updateSessionAccessTime (SessionData data)
private void updateSessionAccessTime (Session data)
throws Exception
{
Connection connection = getConnection();
@ -1050,7 +1018,7 @@ public class JDBCSessionManager extends AbstractSessionManager
statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSessionAccessTime);
statement.setString(1, getSessionIdManager().getWorkerName());
statement.setLong(2, data.getAccessed());
statement.setLong(3, data.getLastAccessed());
statement.setLong(3, data.getLastAccessedTime());
statement.setLong(4, now);
statement.setLong(5, data.getExpiryTime());
statement.setString(6, data.getRowId());
@ -1077,7 +1045,7 @@ public class JDBCSessionManager extends AbstractSessionManager
* @param data
* @throws Exception
*/
protected void deleteSession (SessionData data)
protected void deleteSession (Session data)
throws Exception
{
Connection connection = getConnection();
@ -1118,7 +1086,7 @@ public class JDBCSessionManager extends AbstractSessionManager
* @param data
* @return
*/
private String calculateRowId (SessionData data)
private String calculateRowId (Session data)
{
String rowId = canonicalize(_context.getContextPath());
rowId = rowId + "_" + getVirtualHost(_context);
@ -1133,7 +1101,7 @@ public class JDBCSessionManager extends AbstractSessionManager
*
* @return 0.0.0.0 if no virtual host is defined
*/
private String getVirtualHost (ContextHandler.Context context)
private static String getVirtualHost (ContextHandler.Context context)
{
String vhost = "0.0.0.0";
@ -1153,7 +1121,7 @@ public class JDBCSessionManager extends AbstractSessionManager
* @param path
* @return
*/
private String canonicalize (String path)
private static String canonicalize (String path)
{
if (path==null)
return "";

View File

@ -0,0 +1,141 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.servlet;
import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.eclipse.jetty.server.Dispatcher;
import org.eclipse.jetty.server.AbstractHttpConnection;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.Resource;
/* ------------------------------------------------------------ */
/** Servlet handling JSP Property Group mappings
* <p>
* This servlet is mapped to by any URL pattern for a JSP property group.
* Resources handled by this servlet that are not directories will be passed
* directly to the JSP servlet. Resources that are directories will be
* passed directly to the default servlet.
*/
public class JspPropertyGroupServlet extends GenericServlet
{
private static final long serialVersionUID = 3681783214726776945L;
public final static String NAME = "__org.eclipse.jetty.servlet.JspPropertyGroupServlet__";
private final ServletHandler _servletHandler;
private final ContextHandler _contextHandler;
private ServletHolder _dftServlet;
private ServletHolder _jspServlet;
private boolean _starJspMapped;
public JspPropertyGroupServlet(ContextHandler context, ServletHandler servletHandler)
{
_contextHandler=context;
_servletHandler=servletHandler;
}
@Override
public void init() throws ServletException
{
String jsp_name = "jsp";
ServletMapping servlet_mapping =_servletHandler.getServletMapping("*.jsp");
if (servlet_mapping!=null)
{
_starJspMapped=true;
//now find the jsp servlet, ignoring the mapping that is for ourself
ServletMapping[] mappings = _servletHandler.getServletMappings();
for (ServletMapping m:mappings)
{
String[] paths = m.getPathSpecs();
if (paths!=null)
{
for (String path:paths)
{
if ("*.jsp".equals(path) && !NAME.equals(m.getServletName()))
servlet_mapping = m;
}
}
}
jsp_name=servlet_mapping.getServletName();
}
_jspServlet=_servletHandler.getServlet(jsp_name);
String dft_name="default";
ServletMapping default_mapping=_servletHandler.getServletMapping("/");
if (default_mapping!=null)
dft_name=default_mapping.getServletName();
_dftServlet=_servletHandler.getServlet(dft_name);
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
{
Request request=(req instanceof Request)?(Request)req:AbstractHttpConnection.getCurrentConnection().getRequest();
String servletPath=null;
String pathInfo=null;
if (request.getAttribute(Dispatcher.INCLUDE_REQUEST_URI)!=null)
{
servletPath=(String)request.getAttribute(Dispatcher.INCLUDE_SERVLET_PATH);
pathInfo=(String)request.getAttribute(Dispatcher.INCLUDE_PATH_INFO);
if (servletPath==null)
{
servletPath=request.getServletPath();
pathInfo=request.getPathInfo();
}
}
else
{
servletPath = request.getServletPath();
pathInfo = request.getPathInfo();
}
String pathInContext=URIUtil.addPaths(servletPath,pathInfo);
if (pathInContext.endsWith("/"))
{
_dftServlet.getServlet().service(req,res);
}
else if (_starJspMapped && pathInContext.toLowerCase().endsWith(".jsp"))
{
_jspServlet.getServlet().service(req,res);
}
else
{
Resource resource = _contextHandler.getResource(pathInContext);
if (resource!=null && resource.isDirectory())
_dftServlet.getServlet().service(req,res);
else
_jspServlet.getServlet().service(req,res);
}
}
}

View File

@ -525,12 +525,12 @@ public class ServletHandler extends ScopedHandler
{
UnavailableException ue = (UnavailableException)th;
if (ue.isPermanent())
response.sendError(HttpServletResponse.SC_NOT_FOUND,th.getMessage());
response.sendError(HttpServletResponse.SC_NOT_FOUND);
else
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,th.getMessage());
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
}
else
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,th.getMessage());
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
else
LOG.debug("Response already committed for handling "+th);
@ -547,7 +547,7 @@ public class ServletHandler extends ScopedHandler
{
request.setAttribute(Dispatcher.ERROR_EXCEPTION_TYPE,e.getClass());
request.setAttribute(Dispatcher.ERROR_EXCEPTION,e);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,e.getMessage());
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
else
LOG.debug("Response already committed for handling ",e);

View File

@ -0,0 +1,651 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.servlet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.Timer;
import java.util.TimerTask;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.util.IO;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class AsyncServletTest
{
protected AsyncServlet _servlet=new AsyncServlet();
protected int _port;
protected Server _server = new Server();
protected ServletHandler _servletHandler;
protected SelectChannelConnector _connector;
@Before
public void setUp() throws Exception
{
_connector = new SelectChannelConnector();
_server.setConnectors(new Connector[]{ _connector });
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SECURITY|ServletContextHandler.NO_SESSIONS);
context.setContextPath("/ctx");
_server.setHandler(context);
_servletHandler=context.getServletHandler();
ServletHolder holder=new ServletHolder(_servlet);
holder.setAsyncSupported(true);
_servletHandler.addServletWithMapping(holder,"/path/*");
_server.start();
_port=_connector.getLocalPort();
}
@After
public void tearDown() throws Exception
{
_server.stop();
}
@Test
public void testNormal() throws Exception
{
String response=process(null,null);
assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
assertContains(
"history: REQUEST\r\n"+
"history: initial\r\n",response);
assertContains("NORMAL",response);
assertNotContains("history: onTimeout",response);
assertNotContains("history: onComplete",response);
}
@Test
public void testSleep() throws Exception
{
String response=process("sleep=200",null);
assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
assertContains(
"history: REQUEST\r\n"+
"history: initial\r\n",response);
assertContains("SLEPT",response);
assertNotContains("history: onTimeout",response);
assertNotContains("history: onComplete",response);
}
@Test
public void testSuspend() throws Exception
{
String response=process("suspend=200",null);
assertEquals("HTTP/1.1 500 Async Timeout",response.substring(0,26));
assertContains(
"history: REQUEST\r\n"+
"history: initial\r\n"+
"history: suspend\r\n"+
"history: onTimeout\r\n"+
"history: ERROR\r\n"+
"history: !initial\r\n"+
"history: onComplete\r\n",response);
assertContains("ERROR: /ctx/path/info",response);
}
@Test
public void testSuspendOnTimeoutDispatch() throws Exception
{
String response=process("suspend=200&timeout=dispatch",null);
assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
assertContains(
"history: REQUEST\r\n"+
"history: initial\r\n"+
"history: suspend\r\n"+
"history: onTimeout\r\n"+
"history: dispatch\r\n"+
"history: ASYNC\r\n"+
"history: !initial\r\n"+
"history: onComplete\r\n",response);
assertContains("DISPATCHED",response);
}
@Test
public void testSuspendOnTimeoutComplete() throws Exception
{
String response=process("suspend=200&timeout=complete",null);
assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
assertContains(
"history: REQUEST\r\n"+
"history: initial\r\n"+
"history: suspend\r\n"+
"history: onTimeout\r\n"+
"history: complete\r\n"+
"history: onComplete\r\n",response);
assertContains("COMPLETED",response);
}
@Test
public void testSuspendWaitResume() throws Exception
{
String response=process("suspend=200&resume=10",null);
assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
assertContains(
"history: REQUEST\r\n"+
"history: initial\r\n"+
"history: suspend\r\n"+
"history: resume\r\n"+
"history: ASYNC\r\n"+
"history: !initial\r\n"+
"history: onComplete\r\n",response);
assertNotContains("history: onTimeout",response);
}
@Test
public void testSuspendResume() throws Exception
{
String response=process("suspend=200&resume=0",null);
assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
assertContains(
"history: REQUEST\r\n"+
"history: initial\r\n"+
"history: suspend\r\n"+
"history: resume\r\n"+
"history: ASYNC\r\n"+
"history: !initial\r\n"+
"history: onComplete\r\n",response);
assertContains("history: onComplete",response);
}
@Test
public void testSuspendWaitComplete() throws Exception
{
String response=process("suspend=200&complete=50",null);
assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
assertContains(
"history: REQUEST\r\n"+
"history: initial\r\n"+
"history: suspend\r\n"+
"history: complete\r\n"+
"history: onComplete\r\n",response);
assertContains("COMPLETED",response);
assertNotContains("history: onTimeout",response);
assertNotContains("history: !initial",response);
}
@Test
public void testSuspendComplete() throws Exception
{
String response=process("suspend=200&complete=0",null);
assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
assertContains(
"history: REQUEST\r\n"+
"history: initial\r\n"+
"history: suspend\r\n"+
"history: complete\r\n"+
"history: onComplete\r\n",response);
assertContains("COMPLETED",response);
assertNotContains("history: onTimeout",response);
assertNotContains("history: !initial",response);
}
@Test
public void testSuspendWaitResumeSuspendWaitResume() throws Exception
{
String response=process("suspend=1000&resume=10&suspend2=1000&resume2=10",null);
assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
assertContains(
"history: REQUEST\r\n"+
"history: initial\r\n"+
"history: suspend\r\n"+
"history: resume\r\n"+
"history: ASYNC\r\n"+
"history: !initial\r\n"+
"history: suspend\r\n"+
"history: resume\r\n"+
"history: ASYNC\r\n"+
"history: !initial\r\n"+
"history: onComplete\r\n",response);
assertContains("DISPATCHED",response);
}
@Test
public void testSuspendWaitResumeSuspendComplete() throws Exception
{
String response=process("suspend=1000&resume=10&suspend2=1000&complete2=10",null);
assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
assertContains(
"history: REQUEST\r\n"+
"history: initial\r\n"+
"history: suspend\r\n"+
"history: resume\r\n"+
"history: ASYNC\r\n"+
"history: !initial\r\n"+
"history: suspend\r\n"+
"history: complete\r\n"+
"history: onComplete\r\n",response);
assertContains("COMPLETED",response);
}
@Test
public void testSuspendWaitResumeSuspend() throws Exception
{
String response=process("suspend=1000&resume=10&suspend2=10",null);
assertEquals("HTTP/1.1 500 Async Timeout",response.substring(0,26));
assertContains(
"history: REQUEST\r\n"+
"history: initial\r\n"+
"history: suspend\r\n"+
"history: resume\r\n"+
"history: ASYNC\r\n"+
"history: !initial\r\n"+
"history: suspend\r\n"+
"history: onTimeout\r\n"+
"history: ERROR\r\n"+
"history: !initial\r\n"+
"history: onComplete\r\n",response);
assertContains("ERROR: /ctx/path/info",response);
}
@Test
public void testSuspendTimeoutSuspendResume() throws Exception
{
String response=process("suspend=10&suspend2=1000&resume2=10",null);
assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
assertContains(
"history: REQUEST\r\n"+
"history: initial\r\n"+
"history: suspend\r\n"+
"history: onTimeout\r\n"+
"history: ERROR\r\n"+
"history: !initial\r\n"+
"history: suspend\r\n"+
"history: resume\r\n"+
"history: ASYNC\r\n"+
"history: !initial\r\n"+
"history: onComplete\r\n",response);
assertContains("DISPATCHED",response);
}
@Test
public void testSuspendTimeoutSuspendComplete() throws Exception
{
String response=process("suspend=10&suspend2=1000&complete2=10",null);
assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
assertContains(
"history: REQUEST\r\n"+
"history: initial\r\n"+
"history: suspend\r\n"+
"history: onTimeout\r\n"+
"history: ERROR\r\n"+
"history: !initial\r\n"+
"history: suspend\r\n"+
"history: complete\r\n"+
"history: onComplete\r\n",response);
assertContains("COMPLETED",response);
}
@Test
public void testSuspendTimeoutSuspend() throws Exception
{
String response=process("suspend=10&suspend2=10",null);
assertContains(
"history: REQUEST\r\n"+
"history: initial\r\n"+
"history: suspend\r\n"+
"history: onTimeout\r\n"+
"history: ERROR\r\n"+
"history: !initial\r\n"+
"history: suspend\r\n"+
"history: onTimeout\r\n"+
"history: ERROR\r\n"+
"history: !initial\r\n"+
"history: onComplete\r\n",response);
assertContains("ERROR: /ctx/path/info",response);
}
protected void assertContains(String content,String response)
{
Assert.assertThat(response,Matchers.containsString(content));
}
protected void assertNotContains(String content,String response)
{
Assert.assertThat(response,Matchers.not(Matchers.containsString(content)));
}
public synchronized String process(String query,String content) throws Exception
{
String request = "GET /ctx/path/info";
if (query!=null)
request+="?"+query;
request+=" HTTP/1.1\r\n"+
"Host: localhost\r\n"+
"Connection: close\r\n";
if (content==null)
request+="\r\n";
else
{
request+="Content-Length: "+content.length()+"\r\n";
request+="\r\n" + content;
}
int port=_port;
String response=null;
try
{
Socket socket = new Socket("localhost",port);
socket.setSoTimeout(1000000);
socket.getOutputStream().write(request.getBytes("UTF-8"));
response = IO.toString(socket.getInputStream());
}
catch(Exception e)
{
System.err.println("failed on port "+port);
e.printStackTrace();
throw e;
}
return response;
}
private static class AsyncServlet extends HttpServlet
{
private static final long serialVersionUID = -8161977157098646562L;
private Timer _timer=new Timer();
public AsyncServlet()
{}
/* ------------------------------------------------------------ */
@Override
public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
{
response.addHeader("history",request.getDispatcherType().toString());
int read_before=0;
long sleep_for=-1;
long suspend_for=-1;
long suspend2_for=-1;
long resume_after=-1;
long resume2_after=-1;
long complete_after=-1;
long complete2_after=-1;
if (request.getParameter("read")!=null)
read_before=Integer.parseInt(request.getParameter("read"));
if (request.getParameter("sleep")!=null)
sleep_for=Integer.parseInt(request.getParameter("sleep"));
if (request.getParameter("suspend")!=null)
suspend_for=Integer.parseInt(request.getParameter("suspend"));
if (request.getParameter("suspend2")!=null)
suspend2_for=Integer.parseInt(request.getParameter("suspend2"));
if (request.getParameter("resume")!=null)
resume_after=Integer.parseInt(request.getParameter("resume"));
if (request.getParameter("resume2")!=null)
resume2_after=Integer.parseInt(request.getParameter("resume2"));
if (request.getParameter("complete")!=null)
complete_after=Integer.parseInt(request.getParameter("complete"));
if (request.getParameter("complete2")!=null)
complete2_after=Integer.parseInt(request.getParameter("complete2"));
if (request.getDispatcherType()==DispatcherType.REQUEST)
{
((HttpServletResponse)response).addHeader("history","initial");
if (read_before>0)
{
byte[] buf=new byte[read_before];
request.getInputStream().read(buf);
}
else if (read_before<0)
{
InputStream in = request.getInputStream();
int b=in.read();
while(b!=-1)
b=in.read();
}
if (suspend_for>=0)
{
final AsyncContext async=request.startAsync();
if (suspend_for>0)
async.setTimeout(suspend_for);
async.addListener(__listener);
((HttpServletResponse)response).addHeader("history","suspend");
if (complete_after>0)
{
TimerTask complete = new TimerTask()
{
@Override
public void run()
{
try
{
response.setStatus(200);
response.getOutputStream().println("COMPLETED\n");
response.addHeader("history","complete");
async.complete();
}
catch(Exception e)
{
e.printStackTrace();
}
}
};
synchronized (_timer)
{
_timer.schedule(complete,complete_after);
}
}
else if (complete_after==0)
{
response.setStatus(200);
response.getOutputStream().println("COMPLETED\n");
response.addHeader("history","complete");
async.complete();
}
else if (resume_after>0)
{
TimerTask resume = new TimerTask()
{
@Override
public void run()
{
((HttpServletResponse)async.getResponse()).addHeader("history","resume");
async.dispatch();
}
};
synchronized (_timer)
{
_timer.schedule(resume,resume_after);
}
}
else if (resume_after==0)
{
((HttpServletResponse)async.getResponse()).addHeader("history","resume");
async.dispatch();
}
}
else if (sleep_for>=0)
{
try
{
Thread.sleep(sleep_for);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
response.setStatus(200);
response.getOutputStream().println("SLEPT\n");
}
else
{
response.setStatus(200);
response.getOutputStream().println("NORMAL\n");
}
}
else
{
((HttpServletResponse)response).addHeader("history","!initial");
if (suspend2_for>=0 && request.getAttribute("2nd")==null)
{
final AsyncContext async=request.startAsync();
async.addListener(__listener);
request.setAttribute("2nd","cycle");
if (suspend2_for>0)
{
async.setTimeout(suspend2_for);
}
// continuation.addContinuationListener(__listener);
((HttpServletResponse)response).addHeader("history","suspend");
if (complete2_after>0)
{
TimerTask complete = new TimerTask()
{
@Override
public void run()
{
try
{
response.setStatus(200);
response.getOutputStream().println("COMPLETED\n");
response.addHeader("history","complete");
async.complete();
}
catch(Exception e)
{
e.printStackTrace();
}
}
};
synchronized (_timer)
{
_timer.schedule(complete,complete2_after);
}
}
else if (complete2_after==0)
{
response.setStatus(200);
response.getOutputStream().println("COMPLETED\n");
response.addHeader("history","complete");
async.complete();
}
else if (resume2_after>0)
{
TimerTask resume = new TimerTask()
{
@Override
public void run()
{
((HttpServletResponse)response).addHeader("history","resume");
async.dispatch();
}
};
synchronized (_timer)
{
_timer.schedule(resume,resume2_after);
}
}
else if (resume2_after==0)
{
((HttpServletResponse)response).addHeader("history","dispatch");
async.dispatch();
}
}
else if(request.getDispatcherType()==DispatcherType.ERROR)
{
response.getOutputStream().println("ERROR: "+request.getContextPath()+request.getServletPath()+request.getPathInfo());
}
else
{
response.setStatus(200);
response.getOutputStream().println("DISPATCHED");
}
}
}
}
private static AsyncListener __listener = new AsyncListener()
{
@Override
public void onTimeout(AsyncEvent event) throws IOException
{
((HttpServletResponse)event.getSuppliedResponse()).addHeader("history","onTimeout");
String action=((HttpServletRequest)event.getSuppliedRequest()).getParameter("timeout");
if (action!=null)
{
((HttpServletResponse)event.getSuppliedResponse()).addHeader("history",action);
if ("dispatch".equals(action))
event.getAsyncContext().dispatch();
if ("complete".equals(action))
{
((HttpServletResponse)event.getSuppliedResponse()).getOutputStream().println("COMPLETED\n");
event.getAsyncContext().complete();
}
}
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException
{
// TODO Auto-generated method stub
}
@Override
public void onError(AsyncEvent event) throws IOException
{
// TODO Auto-generated method stub
}
@Override
public void onComplete(AsyncEvent event) throws IOException
{
((HttpServletResponse)event.getSuppliedResponse()).addHeader("history","onComplete");
}
};
}

View File

@ -43,7 +43,6 @@ import org.eclipse.jetty.toolchain.test.FS;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.OS;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.util.DateCache;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.hamcrest.Matchers;

View File

@ -0,0 +1,164 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.servlet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Dispatcher;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.StdErrLog;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
*
*/
public class ErrorPageTest
{
private Server _server;
private LocalConnector _connector;
@Before
public void init() throws Exception
{
_server = new Server();
_connector = new LocalConnector();
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SECURITY|ServletContextHandler.NO_SESSIONS);
_server.setSendServerVersion(false);
_server.addConnector(_connector);
_server.setHandler(context);
context.setContextPath("/");
context.addServlet(DefaultServlet.class, "/");
context.addServlet(FailServlet.class, "/fail/*");
context.addServlet(ErrorServlet.class, "/error/*");
ErrorPageErrorHandler error = new ErrorPageErrorHandler();
context.setErrorHandler(error);
error.addErrorPage(599,"/error/599");
error.addErrorPage(IllegalStateException.class.getCanonicalName(),"/error/TestException");
error.addErrorPage(ErrorPageErrorHandler.GLOBAL_ERROR_PAGE,"/error/GlobalErrorPage");
_server.start();
((StdErrLog)Log.getLogger(ServletHandler.class)).setHideStacks(true);
}
@After
public void destroy() throws Exception
{
((StdErrLog)Log.getLogger(ServletHandler.class)).setHideStacks(false);
_server.stop();
_server.join();
}
@Test
public void testErrorCode() throws Exception
{
String response = _connector.getResponses("GET /fail/code?code=599 HTTP/1.0\r\n\r\n");
assertThat(response,Matchers.containsString("HTTP/1.1 599 599"));
assertThat(response,Matchers.containsString("ERROR_PAGE: /599"));
assertThat(response,Matchers.containsString("ERROR_CODE: 599"));
assertThat(response,Matchers.containsString("ERROR_EXCEPTION: null"));
assertThat(response,Matchers.containsString("ERROR_EXCEPTION_TYPE: null"));
assertThat(response,Matchers.containsString("ERROR_SERVLET: org.eclipse.jetty.servlet.ErrorPageTest$FailServlet-1"));
assertThat(response,Matchers.containsString("ERROR_REQUEST_URI: /fail/code"));
}
@Test
public void testErrorException() throws Exception
{
String response = _connector.getResponses("GET /fail/exception HTTP/1.0\r\n\r\n");
assertThat(response,Matchers.containsString("HTTP/1.1 500 Server Error"));
assertThat(response,Matchers.containsString("ERROR_PAGE: /TestException"));
assertThat(response,Matchers.containsString("ERROR_CODE: 500"));
assertThat(response,Matchers.containsString("ERROR_EXCEPTION: java.lang.IllegalStateException"));
assertThat(response,Matchers.containsString("ERROR_EXCEPTION_TYPE: class java.lang.IllegalStateException"));
assertThat(response,Matchers.containsString("ERROR_SERVLET: org.eclipse.jetty.servlet.ErrorPageTest$FailServlet-1"));
assertThat(response,Matchers.containsString("ERROR_REQUEST_URI: /fail/exception"));
}
@Test
public void testGlobalErrorCode() throws Exception
{
String response = _connector.getResponses("GET /fail/global?code=598 HTTP/1.0\r\n\r\n");
assertThat(response,Matchers.containsString("HTTP/1.1 598 598"));
assertThat(response,Matchers.containsString("ERROR_PAGE: /GlobalErrorPage"));
assertThat(response,Matchers.containsString("ERROR_CODE: 598"));
assertThat(response,Matchers.containsString("ERROR_EXCEPTION: null"));
assertThat(response,Matchers.containsString("ERROR_EXCEPTION_TYPE: null"));
assertThat(response,Matchers.containsString("ERROR_SERVLET: org.eclipse.jetty.servlet.ErrorPageTest$FailServlet-1"));
assertThat(response,Matchers.containsString("ERROR_REQUEST_URI: /fail/global"));
}
@Test
public void testGlobalErrorException() throws Exception
{
String response = _connector.getResponses("GET /fail/global?code=NAN HTTP/1.0\r\n\r\n");
assertThat(response,Matchers.containsString("HTTP/1.1 500 Server Error"));
assertThat(response,Matchers.containsString("ERROR_PAGE: /GlobalErrorPage"));
assertThat(response,Matchers.containsString("ERROR_CODE: 500"));
assertThat(response,Matchers.containsString("ERROR_EXCEPTION: java.lang.NumberFormatException: For input string: \"NAN\""));
assertThat(response,Matchers.containsString("ERROR_EXCEPTION_TYPE: class java.lang.NumberFormatException"));
assertThat(response,Matchers.containsString("ERROR_SERVLET: org.eclipse.jetty.servlet.ErrorPageTest$FailServlet-1"));
assertThat(response,Matchers.containsString("ERROR_REQUEST_URI: /fail/global"));
}
public static class FailServlet extends HttpServlet implements Servlet
{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String code=request.getParameter("code");
if (code!=null)
response.sendError(Integer.parseInt(code));
else
throw new ServletException(new IllegalStateException());
}
}
public static class ErrorServlet extends HttpServlet implements Servlet
{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.getWriter().println("ERROR_PAGE: "+request.getPathInfo());
response.getWriter().println("ERROR_MESSAGE: "+request.getAttribute(Dispatcher.ERROR_MESSAGE));
response.getWriter().println("ERROR_CODE: "+request.getAttribute(Dispatcher.ERROR_STATUS_CODE));
response.getWriter().println("ERROR_EXCEPTION: "+request.getAttribute(Dispatcher.ERROR_EXCEPTION));
response.getWriter().println("ERROR_EXCEPTION_TYPE: "+request.getAttribute(Dispatcher.ERROR_EXCEPTION_TYPE));
response.getWriter().println("ERROR_SERVLET: "+request.getAttribute(Dispatcher.ERROR_SERVLET_NAME));
response.getWriter().println("ERROR_REQUEST_URI: "+request.getAttribute(Dispatcher.ERROR_REQUEST_URI));
}
}
}

View File

@ -89,6 +89,12 @@
<artifactId>javax.servlet</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>test-jetty-servlet</artifactId>

View File

@ -90,6 +90,8 @@ import org.eclipse.jetty.util.log.Logger;
* deflateNoWrap The noWrap setting for deflate compression. Defaults to true. (true/false)
* See: {@link java.util.zip.Deflater#Deflater(int, boolean)}
*
* methods Comma separated list of HTTP methods to compress. If not set, only GET requests are compressed.
*
* mimeTypes Comma separated list of mime types to compress. See description above.
*
* excludedAgents Comma separated list of user agents to exclude from compression. Does a
@ -127,6 +129,8 @@ public class GzipFilter extends UserAgentFilter
protected int _minGzipSize=256;
protected int _deflateCompressionLevel=Deflater.DEFAULT_COMPRESSION;
protected boolean _deflateNoWrap = true;
protected final Set<String> _methods=new HashSet<String>();
protected Set<String> _excludedAgents;
protected Set<Pattern> _excludedAgentPatterns;
protected Set<String> _excludedPaths;
@ -166,6 +170,16 @@ public class GzipFilter extends UserAgentFilter
if (tmp!=null)
_deflateNoWrap=Boolean.parseBoolean(tmp);
tmp=filterConfig.getInitParameter("methods");
if (tmp!=null)
{
StringTokenizer tok = new StringTokenizer(tmp,",",false);
while (tok.hasMoreTokens())
_methods.add(tok.nextToken().trim().toUpperCase());
}
else
_methods.add(HttpMethods.GET);
tmp=filterConfig.getInitParameter("mimeTypes");
if (tmp!=null)
{
@ -235,9 +249,9 @@ public class GzipFilter extends UserAgentFilter
HttpServletRequest request=(HttpServletRequest)req;
HttpServletResponse response=(HttpServletResponse)res;
// If not a GET or an Excluded URI - no Vary because no matter what client, this URI is always excluded
// If not a supported method or it is an Excluded URI - no Vary because no matter what client, this URI is always excluded
String requestURI = request.getRequestURI();
if (!HttpMethods.GET.equalsIgnoreCase(request.getMethod()) || isExcludedPath(requestURI))
if (!_methods.contains(request.getMethod()) || isExcludedPath(requestURI))
{
super.doFilter(request,response,chain);
return;

View File

@ -581,6 +581,12 @@ public class ProxyServlet implements Servlet
String hdr = (String)enm.nextElement();
String lhdr = hdr.toLowerCase(Locale.ENGLISH);
if ("transfer-encoding".equals(lhdr))
{
if (request.getHeader("transfer-encoding").indexOf("chunk")>=0)
hasContent = true;
}
if (_DontProxyHeaders.contains(lhdr))
continue;
if (connectionHdr != null && connectionHdr.indexOf(lhdr) >= 0)

View File

@ -1,6 +1,6 @@
DoSFilter: Limit exposure to abuse from request flooding, whether malicious, or as a result of a misconfigured client.
maxRequestsPerSec: maximum number of requests from a connection per second. Requests in excess of this are first delayed, then throttled.
delayMs: delay (in milliseconds) that is applied to all requests over the rate limit, before they are considered at all, 0 - no delay, -1 - reject request.
delayMs: delay (in milliseconds) that is applied to all requests over the rate limit, before they are considered at all, 0 - no delay, -1 - reject request.
maxWaitMs: maximum amount of time (in milliseconds) the filter will blocking wait for the throttle semaphore.
throttledRequests: number of requests over the rate limit able to be considered at once.
throttleMs: amount of time (in milliseconds) to async wait for semaphore.
@ -9,4 +9,10 @@ maxIdleTrackerMs: maximum amount of time (in milliseconds) to keep track of requ
insertHeaders: insert the DoSFilter headers into the response.
trackSessions: usage rate is tracked by session if a session exists.
remotePort: usage rate is tracked by IP+port (effectively connection) if session tracking is not used.
ipWhitelist: list of IP addresses that will not be rate limited.
enabled: whether this filter is enabled
whitelist: comma separated list of IP addresses that will not be rate limited.
clearWhitelist(): clears the list of IP addresses that will not be rate limited.
addWhitelistAddress(java.lang.String):ACTION: adds an IP address that will not be rate limited.
addWhitelistAddress(java.lang.String)[0]:address: the IP address that will not be rate limited.
removeWhitelistAddress(java.lang.String):ACTION: removes an IP address that will not be rate limited.
removeWhitelistAddress(java.lang.String)[0]:address: the IP address that will not be rate limited.

View File

@ -36,6 +36,7 @@ import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.testing.ServletTester;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
@ -179,8 +180,8 @@ public abstract class AbstractDoSFilterTest
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
String responses = doRequests(request+request+request+request+request,2,1100,1100,last);
assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
assertEquals(2,count(responses,"DoSFilter: delayed"));
assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
}
@Test

View File

@ -0,0 +1,88 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.servlets;
import java.lang.management.ManagementFactory;
import java.util.EnumSet;
import java.util.Set;
import javax.management.Attribute;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.servlet.DispatcherType;
import org.eclipse.jetty.jmx.MBeanContainer;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.junit.Assert;
import org.junit.Test;
public class DoSFilterJMXTest
{
@Test
public void testDoSFilterJMX() throws Exception
{
Server server = new Server();
Connector connector = new SelectChannelConnector();
connector.setPort(0);
server.addConnector(connector);
ServletContextHandler context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS);
DoSFilter filter = new DoSFilter();
FilterHolder holder = new FilterHolder(filter);
String name = "dos";
holder.setName(name);
holder.setInitParameter(DoSFilter.MANAGED_ATTR_INIT_PARAM, "true");
context.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST));
context.setInitParameter(ServletContextHandler.MANAGED_ATTRIBUTES, name);
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
MBeanContainer mbeanContainer = new MBeanContainer(mbeanServer);
server.addBean(mbeanContainer);
server.getContainer().addEventListener(mbeanContainer);
server.start();
String domain = DoSFilter.class.getPackage().getName();
Set<ObjectName> mbeanNames = mbeanServer.queryNames(ObjectName.getInstance(domain + ":*"), null);
Assert.assertEquals(1, mbeanNames.size());
ObjectName objectName = mbeanNames.iterator().next();
boolean value = (Boolean)mbeanServer.getAttribute(objectName, "enabled");
mbeanServer.setAttribute(objectName, new Attribute("enabled", !value));
Assert.assertEquals(!value, filter.isEnabled());
String whitelist = (String)mbeanServer.getAttribute(objectName, "whitelist");
String address = "127.0.0.1";
Assert.assertFalse(whitelist.contains(address));
boolean result = (Boolean)mbeanServer.invoke(objectName, "addWhitelistAddress", new Object[]{address}, new String[]{String.class.getName()});
Assert.assertTrue(result);
whitelist = (String)mbeanServer.getAttribute(objectName, "whitelist");
Assert.assertTrue(whitelist.contains(address));
result = (Boolean)mbeanServer.invoke(objectName, "removeWhitelistAddress", new Object[]{address}, new String[]{String.class.getName()});
Assert.assertTrue(result);
whitelist = (String)mbeanServer.getAttribute(objectName, "whitelist");
Assert.assertFalse(whitelist.contains(address));
server.stop();
}
}

View File

@ -18,18 +18,21 @@
package org.eclipse.jetty.servlets;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.servlets.DoSFilter.RateTracker;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class DoSFilterTest extends AbstractDoSFilterTest
{
private static final Logger LOG = Log.getLogger(DoSFilterTest.class);
@ -70,6 +73,21 @@ public class DoSFilterTest extends AbstractDoSFilterTest
assertFalse("Should not exceed as we sleep 300s for each hit and thus do less than 4 hits/s",exceeded);
}
@Test
public void testWhitelist() throws Exception
{
DoSFilter filter = new DoSFilter();
List<String> whitelist = new ArrayList<String>();
whitelist.add("192.168.0.1");
whitelist.add("10.0.0.0/8");
Assert.assertTrue(filter.checkWhitelist(whitelist, "192.168.0.1"));
Assert.assertFalse(filter.checkWhitelist(whitelist, "192.168.0.2"));
Assert.assertFalse(filter.checkWhitelist(whitelist, "11.12.13.14"));
Assert.assertTrue(filter.checkWhitelist(whitelist, "10.11.12.13"));
Assert.assertTrue(filter.checkWhitelist(whitelist, "10.0.0.0"));
Assert.assertFalse(filter.checkWhitelist(whitelist, "0.0.0.0"));
}
private boolean hitRateTracker(DoSFilter doSFilter, int sleep) throws InterruptedException
{
boolean exceeded = false;

View File

@ -111,7 +111,7 @@ public class GzipFilterContentLengthTest
try
{
tester.start();
tester.assertIsResponseGzipCompressed(testfile.getName());
tester.assertIsResponseGzipCompressed("GET",testfile.getName());
}
finally
{
@ -131,7 +131,7 @@ public class GzipFilterContentLengthTest
try
{
tester.start();
tester.assertIsResponseNotGzipCompressed(testfile.getName(),filesize,HttpStatus.OK_200);
tester.assertIsResponseNotGzipCompressed("GET",testfile.getName(),filesize,HttpStatus.OK_200);
}
finally
{

View File

@ -103,6 +103,50 @@ public class GzipFilterDefaultTest
@Rule
public TestingDir testingdir = new TestingDir();
@Test
public void testIsGzipByMethod() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
// Test content that is smaller than the buffer.
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 2;
tester.prepareServerFile("file.txt",filesize);
FilterHolder holder = tester.setContentServlet(GetServlet.class);
holder.setInitParameter("mimeTypes","text/plain");
holder.setInitParameter("methods","POST,WIBBLE");
try
{
tester.start();
tester.assertIsResponseGzipCompressed("POST","file.txt");
tester.assertIsResponseGzipCompressed("WIBBLE","file.txt");
tester.assertIsResponseNotGzipCompressed("GET","file.txt",filesize,200);
}
finally
{
tester.stop();
}
}
public static class GetServlet extends DefaultServlet
{
public GetServlet()
{
super();
}
@Override
public void service(HttpServletRequest req, HttpServletResponse resp) throws IOException,ServletException
{
doGet(req,resp);
}
}
@Test
public void testIsGzipCompressedTiny() throws Exception
{
@ -118,7 +162,7 @@ public class GzipFilterDefaultTest
try
{
tester.start();
HttpTester http = tester.assertIsResponseGzipCompressed("file.txt");
HttpTester http = tester.assertIsResponseGzipCompressed("GET","file.txt");
Assert.assertEquals("Accept-Encoding",http.getHeader("Vary"));
}
finally
@ -142,7 +186,7 @@ public class GzipFilterDefaultTest
try
{
tester.start();
HttpTester http = tester.assertIsResponseGzipCompressed("file.txt");
HttpTester http = tester.assertIsResponseGzipCompressed("GET","file.txt");
Assert.assertEquals("Accept-Encoding",http.getHeader("Vary"));
}
finally
@ -166,7 +210,7 @@ public class GzipFilterDefaultTest
try
{
tester.start();
HttpTester http = tester.assertIsResponseGzipCompressed("file.txt");
HttpTester http = tester.assertIsResponseGzipCompressed("GET","file.txt");
Assert.assertEquals("Accept-Encoding",http.getHeader("Vary"));
}
finally
@ -190,7 +234,7 @@ public class GzipFilterDefaultTest
try
{
tester.start();
HttpTester http = tester.assertIsResponseGzipCompressed("file.txt");
HttpTester http = tester.assertIsResponseGzipCompressed("GET","file.txt");
Assert.assertEquals("Accept-Encoding",http.getHeader("Vary"));
}
finally
@ -213,7 +257,7 @@ public class GzipFilterDefaultTest
try
{
tester.start();
HttpTester http = tester.assertIsResponseNotGzipCompressed("file.txt", filesize, HttpStatus.OK_200);
HttpTester http = tester.assertIsResponseNotGzipCompressed("GET","file.txt", filesize, HttpStatus.OK_200);
Assert.assertEquals("Accept-Encoding",http.getHeader("Vary"));
}
finally
@ -236,7 +280,7 @@ public class GzipFilterDefaultTest
try
{
tester.start();
HttpTester http = tester.assertIsResponseNotGzipCompressed("file.mp3", filesize, HttpStatus.OK_200);
HttpTester http = tester.assertIsResponseNotGzipCompressed("GET","file.mp3", filesize, HttpStatus.OK_200);
Assert.assertNull(http.getHeader("Vary"));
}
finally
@ -257,7 +301,7 @@ public class GzipFilterDefaultTest
try
{
tester.start();
tester.assertIsResponseNotGzipCompressed(-1, 204);
tester.assertIsResponseNotGzipCompressed("GET",-1, 204);
}
finally
{
@ -278,7 +322,7 @@ public class GzipFilterDefaultTest
try
{
tester.start();
tester.assertIsResponseNotGzipCompressedAndEqualToExpectedString("error message", -1, 400);
tester.assertIsResponseNotGzipCompressedAndEqualToExpectedString("GET","error message", -1, 400);
}
finally
{
@ -302,7 +346,7 @@ public class GzipFilterDefaultTest
try
{
tester.start();
tester.assertIsResponseNotGzipCompressed("file.txt",filesize,HttpStatus.OK_200);
tester.assertIsResponseNotGzipCompressed("GET","file.txt",filesize,HttpStatus.OK_200);
}
finally
{
@ -326,7 +370,7 @@ public class GzipFilterDefaultTest
try
{
tester.start();
tester.assertIsResponseNotGzipCompressed("file.txt",filesize,HttpStatus.OK_200);
tester.assertIsResponseNotGzipCompressed("GET","file.txt",filesize,HttpStatus.OK_200);
}
finally
{
@ -348,7 +392,7 @@ public class GzipFilterDefaultTest
try
{
tester.start();
tester.assertIsResponseNotGzipCompressed("file.txt",filesize,HttpStatus.OK_200);
tester.assertIsResponseNotGzipCompressed("GET","file.txt",filesize,HttpStatus.OK_200);
}
finally
{
@ -370,7 +414,7 @@ public class GzipFilterDefaultTest
try
{
tester.start();
tester.assertIsResponseNotGzipCompressed("file.txt",filesize,HttpStatus.OK_200);
tester.assertIsResponseNotGzipCompressed("GET","file.txt",filesize,HttpStatus.OK_200);
}
finally
{

View File

@ -107,7 +107,7 @@ public class IncludableGzipFilterMinSizeTest
try {
tester.start();
tester.assertIsResponseGzipCompressed("big_script.js");
tester.assertIsResponseGzipCompressed("GET","big_script.js");
} finally {
tester.stop();
}

View File

@ -606,7 +606,7 @@ public class MultipartFilterTest
response.parse(tester.getResponses(request.generate()));
assertTrue(response.getMethod()==null);
assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
assertTrue(response.getReason().startsWith("Missing content"));
assertTrue(response.getContent().indexOf("Missing content")>=0);
}
@Test
@ -629,7 +629,7 @@ public class MultipartFilterTest
response.parse(tester.getResponses(request.generate()));
assertTrue(response.getMethod()==null);
assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
assertTrue(response.getReason().startsWith("Missing initial"));
assertTrue(response.getContent().indexOf("Missing initial")>=0);
}
@ -653,7 +653,7 @@ public class MultipartFilterTest
response.parse(tester.getResponses(request.generate()));
assertTrue(response.getMethod()==null);
assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
assertTrue(response.getReason().startsWith("Missing initial"));
assertTrue(response.getContent().indexOf("Missing initial")>=0);
}
@Test

View File

@ -32,6 +32,8 @@ import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.hamcrest.core.Is;
import org.hamcrest.core.IsEqual;
import org.junit.After;
@ -44,6 +46,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.MalformedURLException;
import java.net.Socket;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@ -239,4 +242,48 @@ public class ProxyServletTest
exchange.waitForDone();
assertThat(excepted.get(),equalTo(true));
}
@Test
public void testChunkedPut() throws Exception
{
init(new HttpServlet()
{
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
resp.setContentType("text/plain");
String message=IO.toString(req.getInputStream());
resp.getOutputStream().print(message);
}
});
Socket client = new Socket("localhost",_connector.getLocalPort());
client.setSoTimeout(1000000);
client.getOutputStream().write((
"PUT /proxy/test HTTP/1.1\r\n"+
"Host: localhost:"+_connector.getLocalPort()+"\r\n"+
"Transfer-Encoding: chunked\r\n"+
"Connection: close\r\n"+
"\r\n"+
"A\r\n"+
"0123456789\r\n"+
"9\r\n"+
"ABCDEFGHI\r\n"+
"8\r\n"+
"JKLMNOPQ\r\n"+
"7\r\n"+
"RSTUVWX\r\n"+
"2\r\n"+
"YZ\r\n"+
"0\r\n"
).getBytes(StringUtil.__ISO_8859_1));
String response=IO.toString(client.getInputStream());
Assert.assertTrue(response.contains("200 OK"));
Assert.assertTrue(response.contains("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
}
}

View File

@ -75,18 +75,18 @@ public class GzipTester
// DOES NOT WORK IN WINDOWS - this.testdir.ensureEmpty();
}
public HttpTester assertIsResponseGzipCompressed(String filename) throws Exception
public HttpTester assertIsResponseGzipCompressed(String method,String filename) throws Exception
{
return assertIsResponseGzipCompressed(filename,filename);
return assertIsResponseGzipCompressed(method,filename,filename);
}
public HttpTester assertIsResponseGzipCompressed(String requestedFilename, String serverFilename) throws Exception
public HttpTester assertIsResponseGzipCompressed(String method,String requestedFilename, String serverFilename) throws Exception
{
System.err.printf("[GzipTester] requesting /context/%s%n",requestedFilename);
HttpTester request = new HttpTester();
HttpTester response = new HttpTester();
request.setMethod("GET");
request.setMethod(method);
request.setVersion("HTTP/1.0");
request.setHeader("Host","tester");
request.setHeader("Accept-Encoding",compressionType);
@ -247,10 +247,10 @@ public class GzipTester
* passing -1 will disable the Content-Length assertion)
* @throws Exception
*/
public HttpTester assertIsResponseNotGzipCompressed(String filename, int expectedFilesize, int status) throws Exception
public HttpTester assertIsResponseNotGzipCompressed(String method,String filename, int expectedFilesize, int status) throws Exception
{
String uri = "/context/"+filename;
HttpTester response = executeRequest(uri);
HttpTester response = executeRequest(method,uri);
assertResponseHeaders(expectedFilesize,status,response);
// Assert that the contents are what we expect.
@ -278,10 +278,10 @@ public class GzipTester
* passing -1 will disable the Content-Length assertion)
* @throws Exception
*/
public void assertIsResponseNotGzipCompressedAndEqualToExpectedString(String expectedResponse, int expectedFilesize, int status) throws Exception
public void assertIsResponseNotGzipCompressedAndEqualToExpectedString(String method,String expectedResponse, int expectedFilesize, int status) throws Exception
{
String uri = "/context/";
HttpTester response = executeRequest(uri);
HttpTester response = executeRequest(method,uri);
assertResponseHeaders(expectedFilesize,status,response);
String actual = readResponse(response);
@ -297,10 +297,10 @@ public class GzipTester
* passing -1 will disable the Content-Length assertion)
* @throws Exception
*/
public void assertIsResponseNotGzipCompressed(int expectedFilesize, int status) throws Exception
public void assertIsResponseNotGzipCompressed(String method,int expectedFilesize, int status) throws Exception
{
String uri = "/context/";
HttpTester response = executeRequest(uri);
HttpTester response = executeRequest(method, uri);
assertResponseHeaders(expectedFilesize,status,response);
}
@ -317,13 +317,13 @@ public class GzipTester
}
}
private HttpTester executeRequest(String uri) throws IOException, Exception
private HttpTester executeRequest(String method,String uri) throws IOException, Exception
{
System.err.printf("[GzipTester] requesting %s%n",uri);
HttpTester request = new HttpTester();
HttpTester response = new HttpTester();
request.setMethod("GET");
request.setMethod(method);
request.setVersion("HTTP/1.0");
request.setHeader("Host","tester");
request.setHeader("Accept-Encoding",compressionType);

View File

@ -336,10 +336,20 @@ public class UrlEncoded extends MultiMap implements Cloneable
i++;
if (i+4<end)
buffer.getStringBuilder().append(Character.toChars((convertHexDigit(raw[++i])<<12) +(convertHexDigit(raw[++i])<<8) + (convertHexDigit(raw[++i])<<4) +convertHexDigit(raw[++i])));
else
{
buffer.getStringBuilder().append(Utf8Appendable.REPLACEMENT);
i=end;
}
}
else
buffer.append((byte)((convertHexDigit(raw[++i])<<4) + convertHexDigit(raw[++i])));
}
else
{
buffer.getStringBuilder().append(Utf8Appendable.REPLACEMENT);
i=end;
}
break;
default:
@ -356,13 +366,13 @@ public class UrlEncoded extends MultiMap implements Cloneable
if (key != null)
{
value = buffer.length()==0?"":buffer.toString();
value = buffer.length()==0?"":buffer.toReplacedString();
buffer.reset();
map.add(key,value);
}
else if (buffer.length()>0)
{
map.add(buffer.toString(),"");
map.add(buffer.toReplacedString(),"");
}
}
}
@ -763,7 +773,10 @@ public class UrlEncoded extends MultiMap implements Cloneable
buffer.getStringBuffer().append(unicode);
}
else
{
i=length;
buffer.getStringBuffer().append(Utf8Appendable.REPLACEMENT);
}
}
else
{
@ -773,13 +786,22 @@ public class UrlEncoded extends MultiMap implements Cloneable
buffer.append(b);
}
}
catch(NotUtf8Exception e)
{
LOG.warn(e.toString());
LOG.debug(e);
}
catch(NumberFormatException nfe)
{
LOG.debug(nfe);
buffer.getStringBuffer().append(Utf8Appendable.REPLACEMENT);
}
}
else
{
buffer.getStringBuffer().append(Utf8Appendable.REPLACEMENT);
i=length;
}
}
else if (buffer!=null)
buffer.getStringBuffer().append(c);
@ -792,7 +814,7 @@ public class UrlEncoded extends MultiMap implements Cloneable
return encoded.substring(offset,offset+length);
}
return buffer.toString();
return buffer.toReplacedString();
}
else
{
@ -843,12 +865,20 @@ public class UrlEncoded extends MultiMap implements Cloneable
{
if ('u'==encoded.charAt(offset+i+1))
{
int o=offset+i+2;
i+=6;
String unicode = new String(Character.toChars(TypeUtil.parseInt(encoded,o,4,16)));
byte[] reencoded = unicode.getBytes(charset);
System.arraycopy(reencoded,0,ba,n,reencoded.length);
n+=reencoded.length;
if (i+6<length)
{
int o=offset+i+2;
i+=6;
String unicode = new String(Character.toChars(TypeUtil.parseInt(encoded,o,4,16)));
byte[] reencoded = unicode.getBytes(charset);
System.arraycopy(reencoded,0,ba,n,reencoded.length);
n+=reencoded.length;
}
else
{
ba[n++] = (byte)'?';
i=length;
}
}
else
{
@ -866,8 +896,8 @@ public class UrlEncoded extends MultiMap implements Cloneable
}
else
{
ba[n++] = (byte)'%';
i++;
ba[n++] = (byte)'?';
i=length;
}
}
else if (c=='+')

View File

@ -20,6 +20,9 @@ package org.eclipse.jetty.util;
import java.io.IOException;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/**
* Utf8 Appendable abstract base class
@ -46,6 +49,7 @@ import java.io.IOException;
**/
public abstract class Utf8Appendable
{
protected static final Logger LOG = Log.getLogger(Utf8Appendable.class);
public static final char REPLACEMENT = '\ufffd';
private static final int UTF8_ACCEPT = 0;
private static final int UTF8_REJECT = 12;
@ -192,4 +196,43 @@ public abstract class Utf8Appendable
super("Not valid UTF8! "+reason);
}
}
protected void checkState()
{
if (!isUtf8SequenceComplete())
{
_codep=0;
_state = UTF8_ACCEPT;
try
{
_appendable.append(REPLACEMENT);
}
catch(IOException e)
{
throw new RuntimeException(e);
}
throw new NotUtf8Exception("incomplete UTF8 sequence");
}
}
public String toReplacedString()
{
if (!isUtf8SequenceComplete())
{
_codep=0;
_state = UTF8_ACCEPT;
try
{
_appendable.append(REPLACEMENT);
}
catch(IOException e)
{
throw new RuntimeException(e);
}
Throwable th= new NotUtf8Exception("incomplete UTF8 sequence");
LOG.warn(th.toString());
LOG.debug(th);
}
return _appendable.toString();
}
}

View File

@ -72,10 +72,4 @@ public class Utf8StringBuffer extends Utf8Appendable
checkState();
return _buffer.toString();
}
private void checkState()
{
if (!isUtf8SequenceComplete())
throw new IllegalArgumentException("Tried to read incomplete UTF8 decoded String");
}
}

View File

@ -74,9 +74,5 @@ public class Utf8StringBuilder extends Utf8Appendable
return _buffer.toString();
}
private void checkState()
{
if (!isUtf8SequenceComplete())
throw new IllegalArgumentException("Tried to read incomplete UTF8 decoded String");
}
}

View File

@ -65,6 +65,23 @@ class JarFileResource extends JarResource
_list=null;
_entry=null;
_file=null;
//if the jvm is not doing url caching, then the JarFiles will not be cached either,
//and so they are safe to close
if (!getUseCaches())
{
if ( _jarFile != null )
{
try
{
LOG.debug("Closing JarFile "+_jarFile.getName());
_jarFile.close();
}
catch ( IOException ioe )
{
LOG.ignore(ioe);
}
}
}
_jarFile=null;
super.release();
}
@ -166,7 +183,7 @@ class JarFileResource extends JarResource
if (jarFile!=null && _entry==null && !_directory)
{
// OK - we have a JarFile, lets look at the entries for our path
Enumeration e=jarFile.entries();
Enumeration<JarEntry> e=jarFile.entries();
while(e.hasMoreElements())
{
JarEntry entry = (JarEntry) e.nextElement();
@ -301,12 +318,11 @@ class JarFileResource extends JarResource
}
}
Enumeration e=jarFile.entries();
Enumeration<JarEntry> e=jarFile.entries();
String dir=_urlString.substring(_urlString.indexOf("!/")+2);
while(e.hasMoreElements())
{
JarEntry entry = (JarEntry) e.nextElement();
JarEntry entry = e.nextElement();
String name=entry.getName().replace('\\','/');
if(!name.startsWith(dir) || name.length()==dir.length())
{

View File

@ -22,6 +22,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.jetty.util.component.Destroyable;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -79,7 +80,7 @@ public class ShutdownThread extends Thread
catch(Exception e)
{
LOG.ignore(e);
LOG.info("shutdown already commenced");
LOG.debug("shutdown already commenced");
}
}
@ -131,6 +132,12 @@ public class ShutdownThread extends Thread
lifeCycle.stop();
LOG.debug("Stopped {}",lifeCycle);
}
if (lifeCycle instanceof Destroyable)
{
((Destroyable)lifeCycle).destroy();
LOG.debug("Destroyed {}",lifeCycle);
}
}
catch (Exception ex)
{

View File

@ -159,11 +159,38 @@ public class URLEncodedTest
public void testBadEncoding() throws UnsupportedEncodingException
{
UrlEncoded url_encoded = new UrlEncoded();
url_encoded.decode("Name15=xx%zz", "UTF-8");
url_encoded.decode("Name15=xx%zzyy", "UTF-8");
assertEquals("encoded param size",1, url_encoded.size());
assertEquals("encoded get", "xx\ufffd", url_encoded.getString("Name15"));
assertEquals("encoded get", "xx\ufffdyy", url_encoded.getString("Name15"));
byte[] bad="Name=%FF%FF%FF".getBytes("UTF-8");
MultiMap<String> map = new MultiMap<String>();
UrlEncoded.decodeUtf8To(bad,0,bad.length,map);
assertEquals("encoded param size",1, map.size());
assertEquals("encoded get", "\ufffd\ufffd\ufffd", map.getString("Name"));
assertEquals("xxx",UrlEncoded.decodeString("xxx%u123",0,5,"UTF-8"));
url_encoded.clear();
url_encoded.decode("Name=%FF%FF%FF", "UTF-8");
assertEquals("encoded param size",1, url_encoded.size());
assertEquals("encoded get", "\ufffd\ufffd\ufffd", url_encoded.getString("Name"));
url_encoded.clear();
url_encoded.decode("Name=%EF%EF%EF", "UTF-8");
assertEquals("encoded param size",1, url_encoded.size());
assertEquals("encoded get", "\ufffd\ufffd", url_encoded.getString("Name"));
assertEquals("x",UrlEncoded.decodeString("x",0,1,"UTF-8"));
assertEquals("x\ufffd",UrlEncoded.decodeString("x%",0,2,"UTF-8"));
assertEquals("x\ufffd",UrlEncoded.decodeString("x%2",0,3,"UTF-8"));
assertEquals("x ",UrlEncoded.decodeString("x%20",0,4,"UTF-8"));
assertEquals("xxx",UrlEncoded.decodeString("xxx",0,3,"UTF-8"));
assertEquals("xxx\ufffd",UrlEncoded.decodeString("xxx%",0,4,"UTF-8"));
assertEquals("xxx\ufffd",UrlEncoded.decodeString("xxx%u",0,5,"UTF-8"));
assertEquals("xxx\ufffd",UrlEncoded.decodeString("xxx%u1",0,6,"UTF-8"));
assertEquals("xxx\ufffd",UrlEncoded.decodeString("xxx%u12",0,7,"UTF-8"));
assertEquals("xxx\ufffd",UrlEncoded.decodeString("xxx%u123",0,8,"UTF-8"));
assertEquals("xxx\u1234",UrlEncoded.decodeString("xxx%u1234",0,9,"UTF-8"));
}

View File

@ -35,8 +35,11 @@ import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.TimeZone;
import java.util.jar.JarFile;
import java.util.zip.ZipFile;
import junit.framework.Assert;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.OS;
import org.eclipse.jetty.util.IO;
@ -124,15 +127,15 @@ public class ResourceTest
file=new File(file.getCanonicalPath());
URI uri = file.toURI();
__userURL=uri.toURL();
__userURL = new URL(__userURL.toString() + "src/test/java/org/eclipse/jetty/util/resource/");
FilePermission perm = (FilePermission) __userURL.openConnection().getPermission();
__userDir = new File(perm.getName()).getCanonicalPath() + File.separatorChar;
__relDir = "src/test/java/org/eclipse/jetty/util/resource/".replace('/', File.separatorChar);
System.err.println("User Dir="+__userDir);
System.err.println("Rel Dir="+__relDir);
System.err.println("User URL="+__userURL);
__userURL = MavenTestingUtils.getTestResourcesDir().toURI().toURL();
FilePermission perm = (FilePermission) __userURL.openConnection().getPermission();
__userDir = new File(perm.getName()).getCanonicalPath() + File.separatorChar;
__relDir = "src/test/resources/".replace('/', File.separatorChar);
//System.err.println("User Dir="+__userDir);
//System.err.println("Rel Dir="+__relDir);
//System.err.println("User URL="+__userURL);
tmpFile=File.createTempFile("test",null).getCanonicalFile();
tmpFile.deleteOnExit();
@ -146,15 +149,15 @@ public class ResourceTest
data[i++]=new Data(__userURL,EXISTS,DIR);
data[i++]=new Data(__userDir,EXISTS,DIR);
data[i++]=new Data(__relDir,EXISTS,DIR);
data[i++]=new Data(__userURL+"ResourceTest.java",EXISTS,!DIR);
data[i++]=new Data(__userDir+"ResourceTest.java",EXISTS,!DIR);
data[i++]=new Data(__relDir+"ResourceTest.java",EXISTS,!DIR);
data[i++]=new Data(__userURL+"jetty-logging.properties",EXISTS,!DIR);
data[i++]=new Data(__userDir+"jetty-logging.properties",EXISTS,!DIR);
data[i++]=new Data(__relDir+"jetty-logging.properties",EXISTS,!DIR);
data[i++]=new Data(__userURL+"NoName.txt",!EXISTS,!DIR);
data[i++]=new Data(__userDir+"NoName.txt",!EXISTS,!DIR);
data[i++]=new Data(__relDir+"NoName.txt",!EXISTS,!DIR);
data[i++]=new Data(data[rt],"ResourceTest.java",EXISTS,!DIR);
data[i++]=new Data(data[rt],"/ResourceTest.java",EXISTS,!DIR);
data[i++]=new Data(data[rt],"jetty-logging.properties",EXISTS,!DIR);
data[i++]=new Data(data[rt],"/jetty-logging.properties",EXISTS,!DIR);
data[i++]=new Data(data[rt],"NoName.txt",!EXISTS,!DIR);
data[i++]=new Data(data[rt],"/NoName.txt",!EXISTS,!DIR);
@ -327,15 +330,14 @@ public class ResourceTest
{
String s = "jar:"+__userURL+"TestData/test.zip!/subdir/numbers";
// TODO move this into src/test/resources!!!
ZipFile zf = new ZipFile(MavenTestingUtils.getProjectFile("src/test/java/org/eclipse/jetty/util/resource/TestData/test.zip"));
ZipFile zf = new ZipFile(MavenTestingUtils.getTestResourceFile("TestData/test.zip"));
long last = zf.getEntry("subdir/numbers").getTime();
Resource r = Resource.newResource(s);
assertEquals(last,r.lastModified()); // Known date value inside zip
}
/* ------------------------------------------------------------ */
@Test
public void testJarFileCopyToDirectoryTraversal () throws Exception

View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
Created-By: 1.2.2 (Sun Microsystems Inc.)

View File

@ -0,0 +1 @@
ABCDEFGHIJKLMNOPQRSTUVWXYZ

View File

@ -0,0 +1 @@
1234567890

View File

@ -0,0 +1 @@
ABCDEFGHIJKLMNOPQRSTUVWXYZ

View File

@ -0,0 +1 @@
1234567890

View File

@ -0,0 +1 @@
ABCDEFGHIJKLMNOPQRSTUVWXYZ

View File

@ -0,0 +1 @@
1234567890

View File

@ -60,10 +60,14 @@ public abstract class Descriptor
if (_root == null)
{
//boolean oldValidating = _processor.getParser().getValidating();
//_processor.getParser().setValidating(_validating);
_root = _parser.parse(_xml.getURL().toString());
//_processor.getParser().setValidating(oldValidating);
try
{
_root = _parser.parse(_xml.getInputStream());
}
finally
{
_xml.release();
}
}
}

View File

@ -49,7 +49,9 @@ import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.FilterMapping;
import org.eclipse.jetty.servlet.Holder;
import org.eclipse.jetty.servlet.JspPropertyGroupServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletContextHandler.JspConfig;
import org.eclipse.jetty.servlet.ServletContextHandler.JspPropertyGroup;
import org.eclipse.jetty.servlet.ServletContextHandler.TagLib;
@ -1342,21 +1344,18 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
if (paths.size() > 0)
{
String jspName = "jsp";
Map.Entry entry = context.getServletHandler().getHolderEntry("test.jsp");
if (entry != null)
ServletHandler handler = context.getServletHandler();
ServletHolder jsp_pg_servlet = handler.getServlet(JspPropertyGroupServlet.NAME);
if (jsp_pg_servlet==null)
{
ServletHolder holder = (ServletHolder) entry.getValue();
jspName = holder.getName();
}
if (jspName != null)
{
ServletMapping mapping = new ServletMapping();
mapping.setServletName(jspName);
mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
context.getServletHandler().addServletMapping(mapping);
jsp_pg_servlet=new ServletHolder(JspPropertyGroupServlet.NAME,new JspPropertyGroupServlet(context,handler));
handler.addServlet(jsp_pg_servlet);
}
ServletMapping mapping = new ServletMapping();
mapping.setServletName(JspPropertyGroupServlet.NAME);
mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
context.getServletHandler().addServletMapping(mapping);
}
}

View File

@ -121,6 +121,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
"org.eclipse.jetty.plus.jaas.", // webapp cannot change jaas classes
"org.eclipse.jetty.websocket.WebSocket", // WebSocket is a jetty extension
"org.eclipse.jetty.websocket.WebSocketFactory", // WebSocket is a jetty extension
"org.eclipse.jetty.websocket.WebSocketServlet", // webapp cannot change WebSocketServlet
"org.eclipse.jetty.servlet.DefaultServlet" // webapp cannot change default servlets
} ;
@ -135,6 +136,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
"-org.eclipse.jetty.plus.jaas.", // don't hide jaas classes
"-org.eclipse.jetty.websocket.WebSocket", // WebSocket is a jetty extension
"-org.eclipse.jetty.websocket.WebSocketFactory", // WebSocket is a jetty extension
"-org.eclipse.jetty.websocket.WebSocketServlet", // don't hide WebSocketServlet
"-org.eclipse.jetty.servlet.DefaultServlet", // don't hide default servlet
"-org.eclipse.jetty.servlet.listener.", // don't hide useful listeners
"org.eclipse.jetty." // hide other jetty classes

View File

@ -230,6 +230,8 @@ public class WebSocketFactory extends AbstractLifeCycle
// Old pre-RFC version specifications (header not present in RFC-6455)
draft = request.getIntHeader("Sec-WebSocket-Draft");
}
// Remember requested version for possible error message later
int requestedVersion = draft;
AbstractHttpConnection http = AbstractHttpConnection.getCurrentConnection();
if (http instanceof BlockingHttpConnection)
throw new IllegalStateException("Websockets not supported on blocking connectors");
@ -252,7 +254,7 @@ public class WebSocketFactory extends AbstractLifeCycle
draft=Integer.MAX_VALUE;
switch (draft)
{
case -1: // unspecified draft/version
case -1: // unspecified draft/version (such as early OSX Safari 5.1 and iOS 5.x)
case 0: // Old school draft/version
{
connection = new WebSocketServletConnectionD00(this, websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol);
@ -283,7 +285,6 @@ public class WebSocketFactory extends AbstractLifeCycle
}
default:
{
LOG.warn("Unsupported Websocket version: " + draft);
// Per RFC 6455 - 4.4 - Supporting Multiple Versions of WebSocket Protocol
// Using the examples as outlined
String versions="13";
@ -295,7 +296,20 @@ public class WebSocketFactory extends AbstractLifeCycle
versions+=", 0";
response.setHeader("Sec-WebSocket-Version", versions);
throw new HttpException(400, "Unsupported websocket version specification: " + draft);
// Make error clear for developer / end-user
StringBuilder err = new StringBuilder();
err.append("Unsupported websocket client version specification ");
if(requestedVersion >= 0) {
err.append("[").append(requestedVersion).append("]");
} else {
err.append("<Unspecified, likely a pre-draft version of websocket>");
}
err.append(", configured minVersion [").append(_minVersion).append("]");
err.append(", reported supported versions [").append(versions).append("]");
LOG.warn(err.toString()); // Log it
// use spec language for unsupported versions
throw new HttpException(400, "Unsupported websocket version specification"); // Tell client
}
}

View File

@ -0,0 +1,116 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.websocket;
import static org.hamcrest.Matchers.*;
import java.net.URI;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.websocket.helper.CaptureSocket;
import org.eclipse.jetty.websocket.helper.SafariD00;
import org.eclipse.jetty.websocket.helper.WebSocketCaptureServlet;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class WebSocketMinVersionTest
{
private Server server;
private WebSocketCaptureServlet servlet;
private URI serverUri;
@BeforeClass
public static void initLogging()
{
// Configure Logging
// System.setProperty("org.eclipse.jetty.util.log.class",StdErrLog.class.getName());
System.setProperty("org.eclipse.jetty.websocket.helper.LEVEL","DEBUG");
}
@Before
public void startServer() throws Exception
{
// Configure Server
server = new Server(0);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
server.setHandler(context);
// Serve capture servlet
servlet = new WebSocketCaptureServlet();
ServletHolder holder = new ServletHolder(servlet);
holder.setInitParameter("minVersion","8");
context.addServlet(holder,"/");
// Start Server
server.start();
Connector conn = server.getConnectors()[0];
String host = conn.getHost();
if (host == null)
{
host = "localhost";
}
int port = conn.getLocalPort();
serverUri = new URI(String.format("ws://%s:%d/",host,port));
// System.out.printf("Server URI: %s%n",serverUri);
}
@Test
public void testAttemptUpgrade() throws Exception
{
SafariD00 safari = new SafariD00(serverUri);
try
{
safari.connect();
safari.issueHandshake();
Assert.fail("Expected upgrade failure");
}
catch(IllegalStateException e) {
String respHeader = e.getMessage();
Assert.assertThat("Response Header", respHeader, containsString("HTTP/1.1 400 Unsupported websocket version specification"));
}
finally
{
// System.out.println("Closing client socket");
safari.disconnect();
}
}
public static void threadSleep(int dur, TimeUnit unit) throws InterruptedException
{
long ms = TimeUnit.MILLISECONDS.convert(dur,unit);
Thread.sleep(ms);
}
@After
public void stopServer() throws Exception
{
server.stop();
}
}

View File

@ -23,6 +23,7 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
@ -35,6 +36,7 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.log.StdErrLog;
import org.junit.Assert;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.is;
public class SafariD00
@ -109,12 +111,14 @@ public class SafariD00
// Read HTTP 101 Upgrade / Handshake Response
InputStreamReader reader = new InputStreamReader(in);
StringBuilder respHeaders = new StringBuilder();
LOG.debug("Reading http headers");
int crlfs = 0;
while (true)
{
int read = in.read();
respHeaders.append((char)read);
if (read == '\r' || read == '\n')
++crlfs;
else
@ -122,6 +126,16 @@ public class SafariD00
if (crlfs == 4)
break;
}
if(respHeaders.toString().startsWith("HTTP/1.1 101 ") == false) {
String respLine = respHeaders.toString();
int idx = respLine.indexOf('\r');
if(idx > 0) {
respLine = respLine.substring(0,idx);
}
LOG.debug("Response Headers: {}",respHeaders.toString());
throw new IllegalStateException(respLine);
}
// Read expected handshake hixie bytes
byte hixieHandshakeExpected[] = TypeUtil.fromHexString("c7438d956cf611a6af70603e6fa54809");

View File

@ -177,7 +177,7 @@
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-websocket</artifactId>
<version>${project.version}</version>
<!--scope>provided</scope-->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>

View File

@ -29,12 +29,10 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.jasper.servlet.JspServlet;
import org.eclipse.jetty.security.HashLoginService;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.NoJspServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
@ -44,7 +42,6 @@ import org.eclipse.jetty.util.log.Logger;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@ -65,16 +62,22 @@ public class JspAndDefaultWithAliasesTest
public static Collection<String[]> data()
{
List<String[]> data = new ArrayList<String[]>();
double javaVersion = Double.parseDouble(System.getProperty("java.specification.version"));
// @formatter:off
data.add(new String[] { "false","/dump.jsp" });
data.add(new String[] { "true", "/dump.jsp%00" });
data.add(new String[] { "false","/dump.jsp%00x" });
data.add(new String[] { "false","/dump.jsp%00/" });
data.add(new String[] { "false","/dump.jsp%00x/" });
data.add(new String[] { "false","/dump.jsp%00x/dump.jsp" });
data.add(new String[] { "false","/dump.jsp%00/dump.jsp" });
data.add(new String[] { "false","/dump.jsp%00/index.html" });
if (javaVersion >= 1.7)
{
data.add(new String[] { "false","/dump.jsp%00x" });
data.add(new String[] { "false","/dump.jsp%00x/" });
data.add(new String[] { "false","/dump.jsp%00/index.html" });
}
// @formatter:on
return data;

View File

@ -29,7 +29,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.jasper.servlet.JspServlet;
import org.eclipse.jetty.security.HashLoginService;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
@ -64,16 +63,22 @@ public class JspAndDefaultWithoutAliasesTest
public static Collection<Object[]> data()
{
List<Object[]> data = new ArrayList<Object[]>();
double javaVersion = Double.parseDouble(System.getProperty("java.specification.version"));
// @formatter:off
data.add(new Object[] { "/dump.jsp" });
data.add(new Object[] { "/dump.jsp%00" });
data.add(new Object[] { "/dump.jsp%00x" });
data.add(new Object[] { "/dump.jsp%00/" });
data.add(new Object[] { "/dump.jsp%00x/" });
data.add(new Object[] { "/dump.jsp%00x/dump.jsp" });
data.add(new Object[] { "/dump.jsp%00/dump.jsp" });
data.add(new Object[] { "/dump.jsp%00/index.html" });
if (javaVersion >= 1.7)
{
data.add(new Object[] { "/dump.jsp%00/" });
data.add(new Object[] { "/dump.jsp%00x/" });
}
// @formatter:on
return data;

View File

@ -53,11 +53,7 @@ public class JdbcTestServer extends AbstractTestServer
super(port, maxInactivePeriod, scavengePeriod, DEFAULT_CONNECTION_URL);
}
public JdbcTestServer (int port, boolean optimize)
{
super(port);
}
/**
* @see org.eclipse.jetty.server.session.AbstractTestServer#newSessionHandler(org.eclipse.jetty.server.SessionManager)
*/

View File

@ -0,0 +1,153 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.server.session;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.FileWriter;
import java.net.URL;
import java.net.URLClassLoader;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.ContentExchange;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.http.HttpMethods;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.StdErrLog;
import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.Test;
/**
* ReloadedSessionMissingClassTest
*
*
*
*/
public class ReloadedSessionMissingClassTest
{
@Test
public void testSessionReloadWithMissingClass() throws Exception
{
((StdErrLog)Log.getLogger(org.eclipse.jetty.server.session.JDBCSessionManager.class)).setHideStacks(true);
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 webInfDir = new File (unpackedWarDir, "WEB-INF");
webInfDir.mkdir();
File webXml = new File(webInfDir, "web.xml");
String xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<web-app xmlns=\"http://java.sun.com/xml/ns/j2ee\"\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
" xsi:schemaLocation=\"http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd\"\n" +
" version=\"2.4\">\n" +
"\n" +
"<session-config>\n"+
" <session-timeout>1</session-timeout>\n" +
"</session-config>\n"+
"</web-app>";
FileWriter w = new FileWriter(webXml);
w.write(xml);
w.close();
File foobarJar = new File (resourcesDir, "foobar.jar");
File foobarNOfooJar = new File (resourcesDir, "foobarNOfoo.jar");
URL[] foobarUrls = new URL[]{foobarJar.toURI().toURL()};
URL[] barUrls = new URL[]{foobarNOfooJar.toURI().toURL()};
URLClassLoader loaderWithFoo = new URLClassLoader(foobarUrls, Thread.currentThread().getContextClassLoader());
URLClassLoader loaderWithoutFoo = new URLClassLoader(barUrls, Thread.currentThread().getContextClassLoader());
AbstractTestServer server1 = new JdbcTestServer(0);
WebAppContext webApp = server1.addWebAppContext(unpackedWarDir.getCanonicalPath(), contextPath);
webApp.setClassLoader(loaderWithFoo);
webApp.addServlet("Bar", "/bar");
server1.start();
int port1 = server1.getPort();
try
{
HttpClient client = new HttpClient();
client.setConnectorType(HttpClient.CONNECTOR_SOCKET);
client.start();
try
{
// Perform one request to server1 to create a session
ContentExchange exchange1 = new ContentExchange(true);
exchange1.setMethod(HttpMethods.GET);
exchange1.setURL("http://localhost:" + port1 + contextPath +"/bar?action=set");
client.send(exchange1);
exchange1.waitForDone();
assertEquals( HttpServletResponse.SC_OK, exchange1.getResponseStatus());
String sessionCookie = exchange1.getResponseFields().getStringField("Set-Cookie");
assertTrue(sessionCookie != null);
String sessionId = (String)webApp.getServletContext().getAttribute("foo");
assertNotNull(sessionId);
// Mangle the cookie, replacing Path with $Path, etc.
sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
//Stop the webapp
webApp.stop();
webApp.setClassLoader(loaderWithoutFoo);
webApp.addServlet("Bar", "/bar");
//restart webapp
webApp.start();
ContentExchange exchange2 = new ContentExchange(true);
exchange2.setMethod(HttpMethods.GET);
exchange2.setURL("http://localhost:" + port1 + contextPath + "/bar?action=get");
exchange2.getRequestFields().add("Cookie", sessionCookie);
client.send(exchange2);
exchange2.waitForDone();
assertEquals(HttpServletResponse.SC_OK,exchange2.getResponseStatus());
String afterStopSessionId = (String)webApp.getServletContext().getAttribute("foo.session");
assertNotNull(afterStopSessionId);
assertTrue(!afterStopSessionId.equals(sessionId));
}
finally
{
client.stop();
}
}
finally
{
server1.stop();
}
}
}