Merge branch 'jetty-8' into release-8
This commit is contained in:
commit
fdda21c167
45
VERSION.txt
45
VERSION.txt
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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>
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 "";
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -107,7 +107,7 @@ public class IncludableGzipFilterMinSizeTest
|
|||
|
||||
try {
|
||||
tester.start();
|
||||
tester.assertIsResponseGzipCompressed("big_script.js");
|
||||
tester.assertIsResponseGzipCompressed("GET","big_script.js");
|
||||
} finally {
|
||||
tester.stop();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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=='+')
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Manifest-Version: 1.0
|
||||
Created-By: 1.2.2 (Sun Microsystems Inc.)
|
||||
|
|
@ -0,0 +1 @@
|
|||
ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
|
@ -0,0 +1 @@
|
|||
1234567890
|
|
@ -0,0 +1 @@
|
|||
ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
|
@ -0,0 +1 @@
|
|||
1234567890
|
|
@ -0,0 +1 @@
|
|||
ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
|
@ -0,0 +1 @@
|
|||
1234567890
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
*/
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue