Merge branch 'master' into release

This commit is contained in:
Jesse McConnell 2012-03-02 08:12:55 -06:00
commit 66fcd1ff7d
28 changed files with 4488 additions and 241 deletions

View File

@ -28,6 +28,9 @@ public class ContextProvider extends ScanningAppProvider
if (!dir.exists())
return false;
String lowername = name.toLowerCase();
if (lowername.startsWith("."))
return false;
return (lowername.endsWith(".xml") && !new File(dir,name).isDirectory());
}
});

View File

@ -50,12 +50,21 @@ public class WebAppProvider extends ScanningAppProvider
return false;
}
// is it a directory for an existing war file?
if (file.isDirectory() &&
(new File(dir,name+".war").exists() ||
new File(dir,name+".WAR").exists()))
{
//ignore hidden files
if (lowername.startsWith("."))
return false;
if (file.isDirectory())
{
// is it a directory for an existing war file?
if (new File(dir,name+".war").exists() ||
new File(dir,name+".WAR").exists())
return false;
//is it a sccs dir?
if ("cvs".equals(lowername) || "cvsroot".equals(lowername))
return false;
}
// is there a contexts config file

View File

@ -18,6 +18,8 @@ import java.io.InputStream;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
/* ------------------------------------------------------------ */
@ -41,6 +43,8 @@ public interface HttpContent
/* ------------------------------------------------------------ */
public class ResourceAsHttpContent implements HttpContent
{
private static final Logger LOG = Log.getLogger(ResourceAsHttpContent.class);
final Resource _resource;
final Buffer _mimeType;
final int _maxBuffer;
@ -80,18 +84,34 @@ public interface HttpContent
/* ------------------------------------------------------------ */
public Buffer getIndirectBuffer()
{
InputStream inputStream = null;
try
{
if (_resource.length()<=0 || _maxBuffer<_resource.length())
if (_resource.length() <= 0 || _maxBuffer < _resource.length())
return null;
ByteArrayBuffer buffer = new ByteArrayBuffer((int)_resource.length());
buffer.readFrom(_resource.getInputStream(),(int)_resource.length());
inputStream = _resource.getInputStream();
buffer.readFrom(inputStream,(int)_resource.length());
return buffer;
}
catch(IOException e)
catch (IOException e)
{
throw new RuntimeException(e);
}
finally
{
if (inputStream != null)
{
try
{
inputStream.close();
}
catch (IOException e)
{
LOG.warn("Couldn't close inputStream. Possible file handle leak",e);
}
}
}
}
/* ------------------------------------------------------------ */

View File

@ -186,10 +186,23 @@ public class HttpGenerator extends AbstractGenerator
flushBuffer();
if (_content != null && _content.length()>0)
{
Buffer nc=_buffers.getBuffer(_content.length()+content.length());
nc.put(_content);
nc.put(content);
content=nc;
if (_bufferChunked)
{
Buffer nc=_buffers.getBuffer(_content.length()+CHUNK_SPACE+content.length());
nc.put(_content);
nc.put(HttpTokens.CRLF);
BufferUtil.putHexInt(nc, content.length());
nc.put(HttpTokens.CRLF);
nc.put(content);
content=nc;
}
else
{
Buffer nc=_buffers.getBuffer(_content.length()+content.length());
nc.put(_content);
nc.put(content);
content=nc;
}
}
}

View File

@ -17,10 +17,13 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.Buffers.Type;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.ByteArrayEndPoint;
import org.eclipse.jetty.io.PooledBuffers;
import org.eclipse.jetty.io.SimpleBuffers;
import org.eclipse.jetty.io.View;
import org.junit.Test;
@ -112,6 +115,62 @@ public class HttpGeneratorClientTest
assertEquals("GET /usr HTTP/1.1|Header: Value|Content-Type: text/plain|Transfer-Encoding: chunked||2C|"+content+"|0||",result);
}
/**
* When the endpoint experiences back pressure, check that chunked transfer does not
* screw up the chunking by leaving out the second chunk header.
*/
@Test
public void testChunkedWithBackPressure() throws Exception
{
final AtomicInteger availableChannelBytes = new AtomicInteger(500);
ByteArrayEndPoint endp = new ByteArrayEndPoint(new byte[0],4096)
{
@Override
public int flush(Buffer buffer) throws IOException
{
// Simulate a socket that can only take 500 bytes at a time
View view = new View(buffer, buffer.markIndex(), buffer.getIndex(),
Math.min(buffer.putIndex(), buffer.getIndex()+availableChannelBytes.get()), buffer.isReadOnly()?Buffer.READONLY:Buffer.READWRITE);
int read = super.flush(view);
buffer.skip(read);
availableChannelBytes.getAndAdd(-1*read);
return read;
}
};
PooledBuffers pool = new PooledBuffers(Type.BYTE_ARRAY,1416,Type.BYTE_ARRAY,8096,Type.BYTE_ARRAY,10240);
HttpGenerator generator = new HttpGenerator(pool,endp);
generator.setRequest("GET","/usr");
HttpFields fields = new HttpFields();
fields.add("Header","Value");
fields.add("Content-Type","text/plain");
String content = "The quick brown fox jumped, ";
// addContent only goes into "bypass" mode if the content is longer than 1024 characters.
while (content.length() < 1024)
{
content = content + content;
}
String content2 = "over the lazy dog";
generator.completeHeader(fields,false);
generator.addContent(new ByteArrayBuffer(content).asMutableBuffer(),false);
generator.addContent(new ByteArrayBuffer(content2).asMutableBuffer(),false);
// Now we'll allow more bytes to flow
availableChannelBytes.set(5000);
generator.flushBuffer();
generator.complete();
generator.flushBuffer();
String result=endp.getOut().toString();
System.err.println("result:"+result);
result=result.replace("\r\n","|").replace('\r','|').replace('\n','|');
assertEquals("GET /usr HTTP/1.1|Header: Value|Content-Type: text/plain|Transfer-Encoding: chunked||700|"+content+"|11|"+content2+"|0||",result);
}
@Test
public void testHTTP() throws Exception
{

View File

@ -4,11 +4,11 @@
// 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
// 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.
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.io;
@ -18,13 +18,13 @@ import java.io.IOException;
/* ------------------------------------------------------------ */
/** Abstract Connection used by Jetty Connectors.
* <p>
* Jetty will call the handle method of a connection when there is work
* to be done on the connection. For blocking connections, this is soon
* as the connection is open and handle will keep being called until the
* Jetty will call the handle method of a connection when there is work
* to be done on the connection. For blocking connections, this is soon
* as the connection is open and handle will keep being called until the
* connection is closed. For non-blocking connections, handle will only
* be called if there are bytes to be read or the connection becomes writable
* after being write blocked.
*
*
* @see org.eclipse.jetty.io.nio.SelectorManager
*/
public interface Connection
@ -32,29 +32,41 @@ public interface Connection
/* ------------------------------------------------------------ */
/**
* Handle the connection.
* @return The Connection to use for the next handling of the connection.
* @return The Connection to use for the next handling of the connection.
* This allows protocol upgrades and support for CONNECT.
* @throws IOException
* @throws IOException if the handling of I/O operations fail
*/
Connection handle() throws IOException;
/**
* @return the timestamp at which the connection was created
*/
long getTimeStamp();
boolean isIdle();
boolean isSuspended();
/**
* Called when the connection is closed
* @return whether this connection is idle, that is not parsing and not generating
* @see #onIdleExpired(long)
*/
boolean isIdle();
/**
* <p>The semantic of this method is to return true to indicate interest in further reads,
* or false otherwise, but it is misnamed and should be really called <code>isReadInterested()</code>.</p>
*
* @return true to indicate interest in further reads, false otherwise
*/
// TODO: rename to isReadInterested() in the next release
boolean isSuspended();
/**
* Called after the connection is closed
*/
void onClose();
/**
* Called when the connection idle timeout expires
* @param idleForMs TODO
* @param idleForMs how long the connection has been idle
* @see #isIdle()
*/
void onIdleExpired(long idleForMs);
}

View File

@ -685,6 +685,16 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
@Override
public void close() throws IOException
{
try
{
if (_key!=null)
_key.cancel();
}
catch (Throwable e)
{
LOG.ignore(e);
}
try
{
super.close();

View File

@ -157,7 +157,8 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
DBCursor checkSessions = _sessions.find(query, new BasicDBObject(MongoSessionManager.__ID, 1));
for ( DBObject session : checkSessions )
{
{
__log.debug("SessionIdManager:scavenge: invalidating " + (String)session.get(MongoSessionManager.__ID));
invalidateAll((String)session.get(MongoSessionManager.__ID));
}
}

View File

@ -183,7 +183,7 @@ public class PackageAdminServiceTracker implements ServiceListener
{
return;
}
StringTokenizer tokenizer = new StringTokenizer(requiredBundleHeader, ",");
StringTokenizer tokenizer = new ManifestTokenizer(requiredBundleHeader);
while (tokenizer.hasMoreTokens())
{
String tok = tokenizer.nextToken().trim();
@ -313,4 +313,55 @@ public class PackageAdminServiceTracker implements ServiceListener
return _startLevel == null ? true : _startLevel.getStartLevel() >= _maxStartLevel;
}
private static class ManifestTokenizer extends StringTokenizer {
public ManifestTokenizer(String header) {
super(header, ",");
}
@Override
public String nextToken() {
String token = super.nextToken();
while (hasOpenQuote(token) && hasMoreTokens()) {
token += "," + super.nextToken();
}
return token;
}
private boolean hasOpenQuote(String token) {
int i = -1;
do {
int quote = getQuote(token, i+1);
if (quote < 0) {
return false;
}
i = token.indexOf(quote, i+1);
i = token.indexOf(quote, i+1);
} while (i >= 0);
return true;
}
private int getQuote(String token, int offset) {
int i = token.indexOf('"', offset);
int j = token.indexOf('\'', offset);
if (i < 0) {
if (j < 0) {
return -1;
} else {
return '\'';
}
}
if (j < 0) {
return '"';
}
if (i < j) {
return '"';
}
return '\'';
}
}
}

View File

@ -55,25 +55,24 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
*
* Here is a typical jetty.xml configuration would be: <pre>
*
* &lt;Set name="handler"&gt;
* &lt;New id="Handlers" class="org.eclipse.jetty.rewrite.handler.RewriteHandler"&gt;
* &lt;New id="RewriteHandler" class="org.eclipse.jetty.rewrite.handler.RewriteHandler"&gt;
* &lt;Set name="rules"&gt;
* &lt;Array type="org.eclipse.jetty.rewrite.handler.Rule"&gt;
*
*
* &lt;Item&gt;
* &lt;New id="rewrite" class="org.eclipse.jetty.rewrite.handler.RewritePatternRule"&gt;
* &lt;Set name="pattern"&gt;/*&lt;/Set&gt;
* &lt;Set name="replacement"&gt;/test&lt;/Set&gt;
* &lt;/New&gt;
* &lt;/Item&gt;
*
*
* &lt;Item&gt;
* &lt;New id="rewrite" class="org.eclipse.jetty.rewrite.handler.ProxyRule"&gt;
* &lt;Set name="pattern"&gt;/*&lt;/Set&gt;
* &lt;Set name="proxyTo"&gt;http://webtide.com:8080&lt;/Set&gt;
* &lt;/New&gt;
* &lt;/Item&gt;
*
*
* &lt;Item&gt;
* &lt;New id="response" class="org.eclipse.jetty.rewrite.handler.ResponsePatternRule"&gt;
* &lt;Set name="pattern"&gt;/session/&lt;/Set&gt;
@ -81,7 +80,7 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
* &lt;Set name="reason"&gt;Setting error code 400&lt;/Set&gt;
* &lt;/New&gt;
* &lt;/Item&gt;
*
*
* &lt;Item&gt;
* &lt;New id="header" class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule"&gt;
* &lt;Set name="pattern"&gt;*.jsp&lt;/Set&gt;
@ -89,7 +88,7 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
* &lt;Set name="value"&gt;dexter webserver&lt;/Set&gt;
* &lt;/New&gt;
* &lt;/Item&gt;
*
*
* &lt;Item&gt;
* &lt;New id="header" class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule"&gt;
* &lt;Set name="pattern"&gt;*.jsp&lt;/Set&gt;
@ -97,21 +96,21 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
* &lt;Set name="value"&gt;driven header purpose&lt;/Set&gt;
* &lt;/New&gt;
* &lt;/Item&gt;
*
*
* &lt;Item&gt;
* &lt;New id="redirect" class="org.eclipse.jetty.rewrite.handler.RedirectPatternRule"&gt;
* &lt;Set name="pattern"&gt;/test/dispatch&lt;/Set&gt;
* &lt;Set name="location"&gt;http://jetty.eclipse.org&lt;/Set&gt;
* &lt;/New&gt;
* &lt;/Item&gt;
*
*
* &lt;Item&gt;
* &lt;New id="regexRewrite" class="org.eclipse.jetty.rewrite.handler.RewriteRegexRule"&gt;
* &lt;Set name="regex"&gt;/test-jaas/$&lt;/Set&gt;
* &lt;Set name="replacement"&gt;/demo&lt;/Set&gt;
* &lt;/New&gt;
* &lt;/Item&gt;
*
*
* &lt;Item&gt;
* &lt;New id="forwardedHttps" class="org.eclipse.jetty.rewrite.handler.ForwardedSchemeHeaderRule"&gt;
* &lt;Set name="header"&gt;X-Forwarded-Scheme&lt;/Set&gt;
@ -119,10 +118,10 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
* &lt;Set name="scheme"&gt;https&lt;/Set&gt;
* &lt;/New&gt;
* &lt;/Item&gt;
*
*
* &lt;Item&gt;
* &lt;New id="virtualHost" class="org.eclipse.jetty.rewrite.handler.VirtualHostRuleContainer"&gt;
*
*
* &lt;Set name="virtualHosts"&gt;
* &lt;Array type="java.lang.String"&gt;
* &lt;Item&gt;eclipse.com&lt;/Item&gt;
@ -131,7 +130,7 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
* &lt;Item&gt;www.eclipse.org&lt;/Item&gt;
* &lt;/Array&gt;
* &lt;/Set&gt;
*
*
* &lt;Call name="addRule"&gt;
* &lt;Arg&gt;
* &lt;New class="org.eclipse.jetty.rewrite.handler.CookiePatternRule"&gt;
@ -141,33 +140,34 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
* &lt;/New&gt;
* &lt;/Arg&gt;
* &lt;/Call&gt;
*
*
* &lt;/New&gt;
* &lt;/ Item&gt;
*
* &lt;/Item&gt;
*
* &lt;/Array&gt;
* &lt;/Set&gt;
*
* &lt;Set name="handler"&gt;
* &lt;New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection"&gt;
* &lt;Set name="handlers"&gt;
* &lt;Array type="org.eclipse.jetty.server.Handler"&gt;
* &lt;Item&gt;
* &lt;New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/&gt;
* &lt;/Item&gt;
* &lt;Item&gt;
* &lt;New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/&gt;
* &lt;/Item&gt;
* &lt;Item&gt;
* &lt;New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler"/&gt;
* &lt;/Item&gt;
* &lt;/Array&gt;
* &lt;/Set&gt;
* &lt;/New&gt;
* &lt;/Set&gt;
*
* &lt;/New&gt;
* &lt;/Set&gt;
*
* &lt;Set name="handler"&gt;
* &lt;New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection"&gt;
* &lt;Set name="handlers"&gt;
* &lt;Array type="org.eclipse.jetty.server.Handler"&gt;
* &lt;Item&gt;
* &lt;Ref id="RewriteHandler"/&gt;
* &lt;/Item&gt;
* &lt;Item&gt;
* &lt;New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/&gt;
* &lt;/Item&gt;
* &lt;Item&gt;
* &lt;New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/&gt;
* &lt;/Item&gt;
* &lt;Item&gt;
* &lt;New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler"/&gt;
* &lt;/Item&gt;
* &lt;/Array&gt;
* &lt;/Set&gt;
* &lt;/New&gt;
* &lt;/Set&gt;
* </pre>
*
*/

View File

@ -11,7 +11,7 @@ The easiest place to put these lines are in the start.ini file.
For debugging the spengo authentication the following options are helpful:
Dorg.eclipse.jetty.util.log.DEBUG=true
-Dorg.eclipse.jetty.LEVEL=debug
-Dsun.security.spnego.debug=all
@ -62,54 +62,4 @@ embedded, via the jetty.xml or in a context file for the webapp.
</Get>
Important Configuration Files:
spengo.properties - configures the user realm with runtime properties
krb5.ini - configures the underlying kerberos setup
spnego.conf - configures the glue between gssapi and kerberos
It is important to note that the keytab file referenced in the krb5.ini and the spengo.conf files needs to
contain the keytab for the targetName for the http server. To do this use a process similar to this:
On the windows active domain controller run:
> setspn -A HTTP/linux.mortbay.org ADUser
To create the keytab file use the following process:
> ktpass -out c:\dir\krb5.keytab -princ HTTP/linux.mortbay.org@MORTBAY.ORG -mapUser ADUser -mapOp set -pass ADUserPWD -crypto RC4-HMAC-NT -pType KRB5_NT_PRINCIPAL
This step should give you the keytab file which should then be copied over to the machine running this
http server and referenced from the configuration files. For our testing we put the keytab into the etc
directory of jetty and referenced it from there.
Setting up your Browser:
Firefox:
* browse to about:config and agree to the warnings
* search through to find the 'network' settings
** set network.negotiate-auth.delegation-uris to http://,https://
** set network.negotiate-auth.trusted-uris to http://,https://
IE:
* Tools -> Options -> Security -> Local Intranet -> Sites
** make sure everything is checked here
* Tools -> Options -> Security -> Local Intranet -> Sites -> Advanced
** add url to server (http:// and/or https://) making sure to use the hostname
* Tools -> Options -> Security -> Local Intranet -> Sites -> Advanced -> Close
* Tools -> Options -> Security -> Local Intranet -> Sites -> Ok
* Tools -> Options -> Advanced -> Security (in the checkbox list)
** locate and check 'Enable Integrated Windows Authentication'
* Tools -> Options -> Advanced -> Security -> Ok
* close IE then reopen and browse to your spengo protected resource
NOTE: you must go to the hostname and not the IP, if you go to the IP it will default to NTLM authentication...the following conditions apply to having spnego work
* Intranet Zone
* Accessing the server using a Hostname rather then IP
* Integrated Windows Authentication in IE is enabled, the host is trusted in Firefox
* The Server is not local to the browser
* The client's Kerberos system is authenticated to a domain controller
8

View File

@ -19,14 +19,17 @@ import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationListener;
import org.eclipse.jetty.continuation.ContinuationThrowable;
import org.eclipse.jetty.http.PathMap;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
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;
@ -291,7 +294,7 @@ public class AsyncContinuation implements AsyncContext, Continuation
/* (non-Javadoc)
* @see javax.servlet.ServletRequest#suspend(long)
*/
protected void suspend(final ServletContext context,
private void doSuspend(final ServletContext context,
final ServletRequest request,
final ServletResponse response)
{
@ -309,9 +312,8 @@ public class AsyncContinuation implements AsyncContext, Continuation
else
{
_event._dispatchContext=null;
_event._path=null;
_event._pathInContext=null;
}
_state=__ASYNCSTARTED;
break;
@ -319,7 +321,6 @@ public class AsyncContinuation implements AsyncContext, Continuation
throw new IllegalStateException(this.getStatusString());
}
}
}
/* ------------------------------------------------------------ */
@ -710,14 +711,14 @@ public class AsyncContinuation implements AsyncContext, Continuation
public void dispatch(ServletContext context, String path)
{
_event._dispatchContext=context;
_event._path=path;
_event._pathInContext=path;
dispatch();
}
/* ------------------------------------------------------------ */
public void dispatch(String path)
{
_event._path=path;
_event._pathInContext=path;
dispatch();
}
@ -810,6 +811,22 @@ public class AsyncContinuation implements AsyncContext, Continuation
dispatch();
}
/* ------------------------------------------------------------ */
protected void suspend(final ServletContext context,
final ServletRequest request,
final ServletResponse response)
{
synchronized (this)
{
doSuspend(context,request,response);
if ( request instanceof HttpServletRequest)
_event._pathInContext=URIUtil.addPaths(((HttpServletRequest)request).getServletPath(),((HttpServletRequest)request).getPathInfo());
}
}
/* ------------------------------------------------------------ */
/**
* @see Continuation#suspend()
@ -817,7 +834,7 @@ public class AsyncContinuation implements AsyncContext, Continuation
public void suspend(ServletResponse response)
{
_responseWrapped=!(response instanceof Response);
AsyncContinuation.this.suspend(_connection.getRequest().getServletContext(),_connection.getRequest(),response);
doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),response);
}
/* ------------------------------------------------------------ */
@ -827,7 +844,7 @@ public class AsyncContinuation implements AsyncContext, Continuation
public void suspend()
{
_responseWrapped=false;
AsyncContinuation.this.suspend(_connection.getRequest().getServletContext(),_connection.getRequest(),_connection.getResponse());
doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),_connection.getResponse());
}
/* ------------------------------------------------------------ */
@ -892,13 +909,43 @@ public class AsyncContinuation implements AsyncContext, Continuation
private final ServletRequest _request;
private final ServletResponse _response;
private ServletContext _dispatchContext;
private String _path;
private String _pathInContext;
public AsyncEventState(ServletContext context, ServletRequest request, ServletResponse response)
{
_suspendedContext=context;
_request=request;
_response=response;
// Get the base request So we can remember the initial paths
Request r=_connection.getRequest();
// If we haven't been async dispatched before
if (r.getAttribute(AsyncContext.ASYNC_REQUEST_URI)==null)
{
// We are setting these attributes during startAsync, when the spec implies that
// they are only available after a call to AsyncContext.dispatch(...);
// have we been forwarded before?
String uri=(String)r.getAttribute(Dispatcher.FORWARD_REQUEST_URI);
if (uri!=null)
{
r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,uri);
r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getAttribute(Dispatcher.FORWARD_CONTEXT_PATH));
r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getAttribute(Dispatcher.FORWARD_SERVLET_PATH));
r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getAttribute(Dispatcher.FORWARD_PATH_INFO));
r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getAttribute(Dispatcher.FORWARD_QUERY_STRING));
}
else
{
r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,r.getRequestURI());
r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getContextPath());
r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getServletPath());
r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getPathInfo());
r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getQueryString());
}
}
}
public ServletContext getSuspendedContext()
@ -926,9 +973,13 @@ public class AsyncContinuation implements AsyncContext, Continuation
return _response;
}
/* ------------------------------------------------------------ */
/**
* @return The path in the context
*/
public String getPath()
{
return _path;
return _pathInContext;
}
@Override

View File

@ -38,6 +38,7 @@ public class AsyncHttpConnection extends AbstractHttpConnection implements Async
private static final Logger LOG = Log.getLogger(AsyncHttpConnection.class);
private int _total_no_progress;
private final AsyncEndPoint _asyncEndp;
private boolean _readInterested = true;
public AsyncHttpConnection(Connector connector, EndPoint endpoint, Server server)
{
@ -103,29 +104,44 @@ public class AsyncHttpConnection extends AbstractHttpConnection implements Async
{
some_progress|=progress;
// Is this request/response round complete and are fully flushed?
if (_parser.isComplete() && _generator.isComplete())
boolean parserComplete = _parser.isComplete();
boolean generatorComplete = _generator.isComplete();
boolean complete = parserComplete && generatorComplete;
if (parserComplete)
{
// Reset the parser/generator
progress=true;
// look for a switched connection instance?
if (_response.getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101)
if (generatorComplete)
{
Connection switched=(Connection)_request.getAttribute("org.eclipse.jetty.io.Connection");
if (switched!=null)
connection=switched;
// Reset the parser/generator
progress=true;
// look for a switched connection instance?
if (_response.getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101)
{
Connection switched=(Connection)_request.getAttribute("org.eclipse.jetty.io.Connection");
if (switched!=null)
connection=switched;
}
reset();
// TODO Is this still required?
if (!_generator.isPersistent() && !_endp.isOutputShutdown())
{
LOG.warn("Safety net oshut!!! IF YOU SEE THIS, PLEASE RAISE BUGZILLA");
_endp.shutdownOutput();
}
}
reset();
// TODO Is this still required?
if (!_generator.isPersistent() && !_endp.isOutputShutdown())
else
{
LOG.warn("Safety net oshut!!! IF YOU SEE THIS, PLEASE RAISE BUGZILLA");
_endp.shutdownOutput();
// We have finished parsing, but not generating so
// we must not be interested in reading until we
// have finished generating and we reset the generator
_readInterested = false;
LOG.debug("Disabled read interest while writing response {}", _endp);
}
}
else if (_request.getAsyncContinuation().isAsyncStarted())
if (!complete && _request.getAsyncContinuation().isAsyncStarted())
{
// The request is suspended, so even though progress has been made,
// exit the while loop by setting progress to false
@ -177,10 +193,23 @@ public class AsyncHttpConnection extends AbstractHttpConnection implements Async
// then no more can happen, so close.
_endp.close();
}
// Make idle parser seek EOF
if (_parser.isIdle())
_parser.setPersistent(false);
}
@Override
public void reset()
{
_readInterested = true;
LOG.debug("Enabled read interest {}", _endp);
super.reset();
}
@Override
public boolean isSuspended()
{
return !_readInterested || super.isSuspended();
}
}

View File

@ -287,22 +287,8 @@ public class Dispatcher implements RequestDispatcher
_contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
if (baseRequest.getResponse().isWriting())
{
try {response.getWriter().close();}
catch(IllegalStateException e)
{
response.getOutputStream().close();
}
}
else
{
try {response.getOutputStream().close();}
catch(IllegalStateException e)
{
response.getWriter().close();
}
}
if (!baseRequest.getAsyncContinuation().isAsyncStarted())
commitResponse(response,baseRequest);
}
}
finally
@ -320,6 +306,34 @@ public class Dispatcher implements RequestDispatcher
}
/* ------------------------------------------------------------ */
private void commitResponse(ServletResponse response, Request baseRequest) throws IOException
{
if (baseRequest.getResponse().isWriting())
{
try
{
response.getWriter().close();
}
catch (IllegalStateException e)
{
response.getOutputStream().close();
}
}
else
{
try
{
response.getOutputStream().close();
}
catch (IllegalStateException e)
{
response.getWriter().close();
}
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */

View File

@ -46,7 +46,6 @@ import javax.servlet.http.HttpSession;
import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationListener;
import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpException;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeaders;
import org.eclipse.jetty.http.HttpMethods;
@ -1885,7 +1884,7 @@ public class Request implements HttpServletRequest
{
if (!_asyncSupported)
throw new IllegalStateException("!asyncSupported");
_async.suspend(_context,this,_connection._response);
_async.suspend();
return _async;
}

View File

@ -366,11 +366,6 @@ public class Server extends HandlerWrapper implements Attributes
if (path!=null)
{
// this is a dispatch with a path
baseRequest.setAttribute(AsyncContext.ASYNC_REQUEST_URI,baseRequest.getRequestURI());
baseRequest.setAttribute(AsyncContext.ASYNC_QUERY_STRING,baseRequest.getQueryString());
baseRequest.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,state.getSuspendedContext().getContextPath());
final String contextPath=state.getServletContext().getContextPath();
HttpURI uri = new HttpURI(URIUtil.addPaths(contextPath,path));
baseRequest.setUri(uri);

View File

@ -71,6 +71,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
protected long _lastScavengeTime;
protected long _scavengeIntervalMs = 1000L * 60 * 10; //10mins
protected String _blobType; //if not set, is deduced from the type of the database at runtime
protected String _longType; //if not set, is deduced from the type of the database at runtime
protected String _createSessionIdTable;
protected String _createSessionTable;
@ -82,6 +83,13 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
protected String _deleteId;
protected String _queryId;
protected String _insertSession;
protected String _deleteSession;
protected String _selectSession;
protected String _updateSession;
protected String _updateSessionNode;
protected String _updateSessionAccessTime;
protected DatabaseAdaptor _dbAdaptor;
@ -146,6 +154,17 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
return "blob";
}
public String getLongType ()
{
if (_longType != null)
return _longType;
if (_dbName.startsWith("oracle"))
return "number(20)";
return "bigint";
}
public InputStream getBlobInputStream (ResultSet result, String columnName)
throws SQLException
{
@ -158,6 +177,18 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
Blob blob = result.getBlob(columnName);
return blob.getBinaryStream();
}
/**
* rowId is a reserved word for Oracle, so change the name of this column
* @return
*/
public String getRowIdColumnName ()
{
if (_dbName != null && _dbName.startsWith("oracle"))
return "srowId";
return "rowId";
}
}
@ -239,6 +270,18 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
return _blobType;
}
public String getLongType()
{
return _longType;
}
public void setLongType(String longType)
{
this._longType = longType;
}
public void setScavengeInterval (long sec)
{
if (sec<=0)
@ -528,7 +571,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
connection.setAutoCommit(true);
DatabaseMetaData metaData = connection.getMetaData();
_dbAdaptor = new DatabaseAdaptor(metaData);
_sessionTableRowId = (_dbAdaptor.getDBName() != null && _dbAdaptor.getDBName().contains("oracle") ? "srowId":_sessionTableRowId);
_sessionTableRowId = _dbAdaptor.getRowIdColumnName();
//checking for table existence is case-sensitive, but table creation is not
String tableName = _dbAdaptor.convertIdentifier(_sessionIdTable);
@ -546,10 +589,11 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
{
//table does not exist, so create it
String blobType = _dbAdaptor.getBlobType();
String longType = _dbAdaptor.getLongType();
_createSessionTable = "create table "+_sessionTable+" ("+_sessionTableRowId+" varchar(120), sessionId varchar(120), "+
" contextPath varchar(60), virtualHost varchar(60), lastNode varchar(60), accessTime bigint, "+
" lastAccessTime bigint, createTime bigint, cookieTime bigint, "+
" lastSavedTime bigint, expiryTime bigint, map "+blobType+", primary key("+_sessionTableRowId+"))";
" contextPath varchar(60), virtualHost varchar(60), lastNode varchar(60), accessTime "+longType+", "+
" lastAccessTime "+longType+", createTime "+longType+", cookieTime "+longType+", "+
" lastSavedTime "+longType+", expiryTime "+longType+", map "+blobType+", primary key("+_sessionTableRowId+"))";
connection.createStatement().executeUpdate(_createSessionTable);
}
@ -576,6 +620,28 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
if (!index2Exists)
statement.executeUpdate("create index "+index2+" on "+_sessionTable+" (sessionId, contextPath)");
}
//set up some strings representing the statements for session manipulation
_insertSession = "insert into "+_sessionTable+
" ("+_sessionTableRowId+", sessionId, contextPath, virtualHost, lastNode, accessTime, lastAccessTime, createTime, cookieTime, lastSavedTime, expiryTime, map) "+
" values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
_deleteSession = "delete from "+_sessionTable+
" where "+_sessionTableRowId+" = ?";
_selectSession = "select * from "+_sessionTable+
" where sessionId = ? and contextPath = ? and virtualHost = ?";
_updateSession = "update "+_sessionTable+
" set lastNode = ?, accessTime = ?, lastAccessTime = ?, lastSavedTime = ?, expiryTime = ?, map = ? where "+_sessionTableRowId+" = ?";
_updateSessionNode = "update "+_sessionTable+
" set lastNode = ? where "+_sessionTableRowId+" = ?";
_updateSessionAccessTime = "update "+_sessionTable+
" set lastNode = ?, accessTime = ?, lastAccessTime = ?, lastSavedTime = ?, expiryTime = ? where "+_sessionTableRowId+" = ?";
}
finally
{

View File

@ -68,15 +68,8 @@ public class JDBCSessionManager extends AbstractSessionManager
{
private static final Logger LOG = Log.getLogger(JDBCSessionManager.class);
protected String __insertSession;
protected String __deleteSession;
protected String __selectSession;
protected String __updateSession;
protected String __updateSessionNode;
protected String __updateSessionAccessTime;
protected String __sessionTableRowId;
private ConcurrentHashMap<String, AbstractSession> _sessions;
protected JDBCSessionIdManager _jdbcSessionIdMgr = null;
protected long _saveIntervalSec = 60; //only persist changes to session access times every 60 secs
/**
@ -603,8 +596,8 @@ public class JDBCSessionManager extends AbstractSessionManager
if (_sessionIdManager==null)
throw new IllegalStateException("No session id manager defined");
prepareTables();
_jdbcSessionIdMgr = (JDBCSessionIdManager)_sessionIdManager;
_sessions = new ConcurrentHashMap<String, AbstractSession>();
super.doStart();
}
@ -816,30 +809,6 @@ public class JDBCSessionManager extends AbstractSessionManager
}
protected void prepareTables ()
{
__sessionTableRowId = ((JDBCSessionIdManager)_sessionIdManager)._sessionTableRowId;
__insertSession = "insert into "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
" ("+__sessionTableRowId+", sessionId, contextPath, virtualHost, lastNode, accessTime, lastAccessTime, createTime, cookieTime, lastSavedTime, expiryTime, map) "+
" values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
__deleteSession = "delete from "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
" where "+__sessionTableRowId+" = ?";
__selectSession = "select * from "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
" where sessionId = ? and contextPath = ? and virtualHost = ?";
__updateSession = "update "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
" set lastNode = ?, accessTime = ?, lastAccessTime = ?, lastSavedTime = ?, expiryTime = ?, map = ? where "+__sessionTableRowId+" = ?";
__updateSessionNode = "update "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
" set lastNode = ? where "+__sessionTableRowId+" = ?";
__updateSessionAccessTime = "update "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+
" set lastNode = ?, accessTime = ?, lastAccessTime = ?, lastSavedTime = ?, expiryTime = ? where "+__sessionTableRowId+" = ?";
}
/**
* Load a session from the database
* @param id
@ -862,7 +831,7 @@ public class JDBCSessionManager extends AbstractSessionManager
try
{
connection = getConnection();
statement = connection.prepareStatement(__selectSession);
statement = connection.prepareStatement(_jdbcSessionIdMgr._selectSession);
statement.setString(1, id);
statement.setString(2, canonicalContextPath);
statement.setString(3, vhost);
@ -870,7 +839,7 @@ public class JDBCSessionManager extends AbstractSessionManager
if (result.next())
{
data = new SessionData(id);
data.setRowId(result.getString(__sessionTableRowId));
data.setRowId(result.getString(_jdbcSessionIdMgr._sessionTableRowId));
data.setCookieSet(result.getLong("cookieTime"));
data.setLastAccessed(result.getLong("lastAccessTime"));
data.setAccessed (result.getLong("accessTime"));
@ -939,7 +908,7 @@ public class JDBCSessionManager extends AbstractSessionManager
long now = System.currentTimeMillis();
connection.setAutoCommit(true);
statement = connection.prepareStatement(__insertSession);
statement = connection.prepareStatement(_jdbcSessionIdMgr._insertSession);
statement.setString(1, rowId); //rowId
statement.setString(2, data.getId()); //session id
statement.setString(3, data.getCanonicalContext()); //context path
@ -994,7 +963,7 @@ public class JDBCSessionManager extends AbstractSessionManager
{
long now = System.currentTimeMillis();
connection.setAutoCommit(true);
statement = connection.prepareStatement(__updateSession);
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
@ -1038,7 +1007,7 @@ public class JDBCSessionManager extends AbstractSessionManager
try
{
connection.setAutoCommit(true);
statement = connection.prepareStatement(__updateSessionNode);
statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSessionNode);
statement.setString(1, nodeId);
statement.setString(2, data.getRowId());
statement.executeUpdate();
@ -1068,7 +1037,7 @@ public class JDBCSessionManager extends AbstractSessionManager
{
long now = System.currentTimeMillis();
connection.setAutoCommit(true);
statement = connection.prepareStatement(__updateSessionAccessTime);
statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSessionAccessTime);
statement.setString(1, getSessionIdManager().getWorkerName());
statement.setLong(2, data.getAccessed());
statement.setLong(3, data.getLastAccessed());
@ -1106,7 +1075,7 @@ public class JDBCSessionManager extends AbstractSessionManager
try
{
connection.setAutoCommit(true);
statement = connection.prepareStatement(__deleteSession);
statement = connection.prepareStatement(_jdbcSessionIdMgr._deleteSession);
statement.setString(1, data.getRowId());
statement.executeUpdate();
if (LOG.isDebugEnabled())

View File

@ -0,0 +1,159 @@
package org.eclipse.jetty.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.nio.AsyncConnection;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import static org.hamcrest.Matchers.lessThan;
public class SlowClientWithPipelinedRequestTest
{
private final AtomicInteger handles = new AtomicInteger();
private Server server;
private SelectChannelConnector connector;
public void startServer(Handler handler) throws Exception
{
server = new Server();
connector = new SelectChannelConnector()
{
@Override
protected AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint)
{
return new AsyncHttpConnection(this, endpoint, getServer())
{
@Override
public Connection handle() throws IOException
{
handles.incrementAndGet();
return super.handle();
}
};
}
};
server.addConnector(connector);
connector.setPort(0);
server.setHandler(handler);
server.start();
}
@After
public void stopServer() throws Exception
{
if (server != null)
{
server.stop();
server.join();
}
}
@Test
public void testSlowClientWithPipelinedRequest() throws Exception
{
final int contentLength = 512 * 1024;
startServer(new AbstractHandler()
{
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
baseRequest.setHandled(true);
System.err.println("target = " + target);
if ("/content".equals(target))
{
// We simulate what the DefaultServlet does, bypassing the blocking
// write mechanism otherwise the test does not reproduce the bug
OutputStream outputStream = response.getOutputStream();
AbstractHttpConnection.Output output = (AbstractHttpConnection.Output)outputStream;
// Since the test is via localhost, we need a really big buffer to stall the write
byte[] bytes = new byte[contentLength];
Arrays.fill(bytes, (byte)'9');
Buffer buffer = new ByteArrayBuffer(bytes);
// Do a non blocking write
output.sendContent(buffer);
}
}
});
Socket client = new Socket("localhost", connector.getLocalPort());
OutputStream output = client.getOutputStream();
output.write(("" +
"GET /content HTTP/1.1\r\n" +
"Host: localhost:" + connector.getLocalPort() + "\r\n" +
"\r\n" +
"").getBytes("UTF-8"));
output.flush();
InputStream input = client.getInputStream();
int read = input.read();
Assert.assertTrue(read >= 0);
// As soon as we can read the response, send a pipelined request
// so it is a different read for the server and it will trigger NIO
output.write(("" +
"GET /pipelined HTTP/1.1\r\n" +
"Host: localhost:" + connector.getLocalPort() + "\r\n" +
"\r\n" +
"").getBytes("UTF-8"));
output.flush();
// Simulate a slow reader
Thread.sleep(1000);
Assert.assertThat(handles.get(), lessThan(10));
// We are sure we are not spinning, read the content
StringBuilder lines = new StringBuilder().append((char)read);
int crlfs = 0;
while (true)
{
read = input.read();
lines.append((char)read);
if (read == '\r' || read == '\n')
++crlfs;
else
crlfs = 0;
if (crlfs == 4)
break;
}
Assert.assertTrue(lines.toString().contains(" 200 "));
// Read the body
for (int i = 0; i < contentLength; ++i)
input.read();
// Read the pipelined response
lines.setLength(0);
crlfs = 0;
while (true)
{
read = input.read();
lines.append((char)read);
if (read == '\r' || read == '\n')
++crlfs;
else
crlfs = 0;
if (crlfs == 4)
break;
}
Assert.assertTrue(lines.toString().contains(" 200 "));
client.close();
}
}

View File

@ -0,0 +1,343 @@
package org.eclipse.jetty.servlet;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;
import junit.framework.Assert;
import org.eclipse.jetty.continuation.ContinuationSupport;
import org.eclipse.jetty.server.AsyncContext;
import org.eclipse.jetty.server.AsyncContinuation;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.DispatcherType;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* This tests the correct functioning of the AsyncContext
*
* tests for #371649 and #371635
*/
public class AsyncContextTest
{
private Server _server = new Server();
private ServletContextHandler _contextHandler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
private LocalConnector _connector = new LocalConnector();
@Before
public void setUp() throws Exception
{
_connector.setMaxIdleTime(3000000);
_server.setConnectors(new Connector[]
{ _connector });
_contextHandler.setContextPath("/");
_contextHandler.addServlet(new ServletHolder(new TestServlet()),"/servletPath");
_contextHandler.addServlet(new ServletHolder(new TestServlet2()),"/servletPath2");
_contextHandler.addServlet(new ServletHolder(new ForwardingServlet()),"/forward");
_contextHandler.addServlet(new ServletHolder(new AsyncDispatchingServlet()),"/dispatchingServlet");
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[]
{ _contextHandler, new DefaultHandler() });
_server.setHandler(handlers);
_server.start();
}
@Test
public void testSimpleAsyncContext() throws Exception
{
String request = "GET /servletPath HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Connection: close\r\n" + "\r\n";
String responseString = _connector.getResponses(request);
BufferedReader br = new BufferedReader(new StringReader(responseString));
Assert.assertEquals("HTTP/1.1 200 OK",br.readLine());
br.readLine();// connection close
br.readLine();// server
br.readLine();// empty
Assert.assertEquals("servlet gets right path","doGet:getServletPath:/servletPath", br.readLine());
Assert.assertEquals("async context gets right path in get","doGet:async:getServletPath:/servletPath", br.readLine());
Assert.assertEquals("async context gets right path in async","async:run:attr:servletPath:/servletPath", br.readLine());
}
@Test
public void testDispatchAsyncContext() throws Exception
{
String request = "GET /servletPath?dispatch=true HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Connection: close\r\n" + "\r\n";
String responseString = _connector.getResponses(request);
BufferedReader br = new BufferedReader(new StringReader(responseString));
Assert.assertEquals("HTTP/1.1 200 OK",br.readLine());
br.readLine();// connection close
br.readLine();// server
br.readLine();// empty
Assert.assertEquals("servlet gets right path","doGet:getServletPath:/servletPath2", br.readLine());
Assert.assertEquals("async context gets right path in get","doGet:async:getServletPath:/servletPath2", br.readLine());
Assert.assertEquals("servlet path attr is original","async:run:attr:servletPath:/servletPath", br.readLine());
Assert.assertEquals("path info attr is correct","async:run:attr:pathInfo:null", br.readLine());
Assert.assertEquals("query string attr is correct","async:run:attr:queryString:dispatch=true", br.readLine());
Assert.assertEquals("context path attr is correct","async:run:attr:contextPath:", br.readLine());
Assert.assertEquals("request uri attr is correct","async:run:attr:requestURI:/servletPath", br.readLine());
}
@Test
public void testSimpleWithContextAsyncContext() throws Exception
{
_contextHandler.setContextPath("/foo");
String request = "GET /foo/servletPath HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Connection: close\r\n" + "\r\n";
String responseString = _connector.getResponses(request);
BufferedReader br = new BufferedReader(new StringReader(responseString));
Assert.assertEquals("HTTP/1.1 200 OK",br.readLine());
br.readLine();// connection close
br.readLine();// server
br.readLine();// empty
Assert.assertEquals("servlet gets right path","doGet:getServletPath:/servletPath", br.readLine());
Assert.assertEquals("async context gets right path in get","doGet:async:getServletPath:/servletPath", br.readLine());
Assert.assertEquals("async context gets right path in async","async:run:attr:servletPath:/servletPath", br.readLine());
}
@Test
public void testDispatchWithContextAsyncContext() throws Exception
{
_contextHandler.setContextPath("/foo");
String request = "GET /foo/servletPath?dispatch=true HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Connection: close\r\n" + "\r\n";
String responseString = _connector.getResponses(request);
System.out.println(responseString);
BufferedReader br = new BufferedReader(new StringReader(responseString));
Assert.assertEquals("HTTP/1.1 200 OK",br.readLine());
br.readLine();// connection close
br.readLine();// server
br.readLine();// empty
Assert.assertEquals("servlet gets right path","doGet:getServletPath:/servletPath2", br.readLine());
Assert.assertEquals("async context gets right path in get","doGet:async:getServletPath:/servletPath2", br.readLine());
Assert.assertEquals("servlet path attr is original","async:run:attr:servletPath:/servletPath", br.readLine());
Assert.assertEquals("path info attr is correct","async:run:attr:pathInfo:null", br.readLine());
Assert.assertEquals("query string attr is correct","async:run:attr:queryString:dispatch=true", br.readLine());
Assert.assertEquals("context path attr is correct","async:run:attr:contextPath:/foo", br.readLine());
Assert.assertEquals("request uri attr is correct","async:run:attr:requestURI:/foo/servletPath", br.readLine());
}
@Test
public void testDispatch() throws Exception
{
String request = "GET /forward HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Connection: close\r\n"
+ "\r\n";
String responseString = _connector.getResponses(request);
BufferedReader br = new BufferedReader(new StringReader(responseString));
assertEquals("HTTP/1.1 200 OK",br.readLine());
br.readLine();// connection close
br.readLine();// server
br.readLine();// empty
assertThat("!ForwardingServlet",br.readLine(),equalTo("Dispatched back to ForwardingServlet"));
}
@Test
public void testDispatchRequestResponse() throws Exception
{
String request = "GET /forward?dispatchRequestResponse=true HTTP/1.1\r\n" + "Host: localhost\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n" + "Connection: close\r\n" + "\r\n";
String responseString = _connector.getResponses(request);
BufferedReader br = new BufferedReader(new StringReader(responseString));
assertEquals("HTTP/1.1 200 OK",br.readLine());
br.readLine();// connection close
br.readLine();// server
br.readLine();// empty
assertThat("!AsyncDispatchingServlet",br.readLine(),equalTo("Dispatched back to AsyncDispatchingServlet"));
}
private class ForwardingServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
@Override
protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
{
if (((Request)request).getDispatcherType() == DispatcherType.ASYNC)
{
response.getOutputStream().print("Dispatched back to ForwardingServlet");
}
else
{
request.getRequestDispatcher("/dispatchingServlet").forward(request,response);
}
}
}
private class AsyncDispatchingServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, final HttpServletResponse response) throws ServletException, IOException
{
Request request = (Request)req;
if (request.getDispatcherType() == DispatcherType.ASYNC)
{
response.getOutputStream().print("Dispatched back to AsyncDispatchingServlet");
}
else
{
final AsyncContext asyncContext;
if (request.getParameter("dispatchRequestResponse") != null)
asyncContext = request.startAsync(request,response);
else
asyncContext = request.startAsync();
new Thread(new DispatchingRunnable(asyncContext)).start();
}
}
}
private class DispatchingRunnable implements Runnable
{
private AsyncContext asyncContext;
public DispatchingRunnable(AsyncContext asyncContext)
{
this.asyncContext = asyncContext;
}
public void run()
{
asyncContext.dispatch();
}
}
@After
public void tearDown() throws Exception
{
_server.stop();
_server.join();
}
private class TestServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
AsyncContinuation continuation = (AsyncContinuation)ContinuationSupport.getContinuation(request);
if (request.getParameter("dispatch") != null)
{
continuation.suspend();
continuation.dispatch("/servletPath2");
// AsyncContext asyncContext = request.startAsync(request,response);
}
else
{
response.getOutputStream().print("doGet:getServletPath:" + request.getServletPath() + "\n");
continuation.suspend();
// AsyncContext asyncContext = request.startAsync(request,response);
response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)continuation.getRequest()).getServletPath() + "\n");
Runnable runable = new AsyncRunnable(continuation);
new Thread(runable).start();
// asyncContext.start(new AsyncRunnable(asyncContext));
}
return;
}
}
private class TestServlet2 extends HttpServlet
{
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
AsyncContinuation continuation = (AsyncContinuation)ContinuationSupport.getContinuation(request);
response.getOutputStream().print("doGet:getServletPath:" + request.getServletPath() + "\n");
continuation.suspend();
// AsyncContext asyncContext = request.startAsync(request, response);
response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)continuation.getRequest()).getServletPath() + "\n");
Runnable runable = new AsyncRunnable(continuation);
new Thread(runable).start();
// asyncContext.start(new AsyncRunnable(asyncContext));
return;
}
}
private class AsyncRunnable implements Runnable
{
private AsyncContinuation _continuation;
public AsyncRunnable(AsyncContinuation continuation)
{
_continuation = continuation;
}
public void run()
{
HttpServletRequest req = (HttpServletRequest)_continuation.getRequest();
try
{
_continuation.getResponse().getOutputStream().print("async:run:attr:servletPath:" + req.getAttribute(AsyncContext.ASYNC_SERVLET_PATH) + "\n");
_continuation.getResponse().getOutputStream().print("async:run:attr:pathInfo:" + req.getAttribute(AsyncContext.ASYNC_PATH_INFO) + "\n");
_continuation.getResponse().getOutputStream().print("async:run:attr:queryString:" + req.getAttribute(AsyncContext.ASYNC_QUERY_STRING) + "\n");
_continuation.getResponse().getOutputStream().print("async:run:attr:contextPath:" + req.getAttribute(AsyncContext.ASYNC_CONTEXT_PATH) + "\n");
_continuation.getResponse().getOutputStream().print("async:run:attr:requestURI:" + req.getAttribute(AsyncContext.ASYNC_REQUEST_URI) + "\n");
}
catch (IOException e)
{
e.printStackTrace();
}
_continuation.complete();
}
}
}

View File

@ -0,0 +1,199 @@
package org.eclipse.jetty.servlets;
import static org.hamcrest.Matchers.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlets.gzip.Hex;
import org.eclipse.jetty.servlets.gzip.NoOpOutputStream;
import org.eclipse.jetty.toolchain.test.IO;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
/**
* Test the effects of Gzip filtering when in the context of HTTP/1.1 Pipelining.
*/
public class GzipWithPipeliningTest
{
@Rule
public TestingDir testingdir = new TestingDir();
private Server server;
private URI serverUri;
@Before
public void startServer() throws Exception
{
// Configure Server
server = new Server(0);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
DefaultServlet servlet = new DefaultServlet();
ServletHolder holder = new ServletHolder(servlet);
holder.setInitParameter("resourceBase",MavenTestingUtils.getTestResourcesDir().getAbsolutePath());
context.addServlet(holder,"/");
FilterHolder filter = context.addFilter(GzipFilter.class,"/*",0);
filter.setInitParameter("mimeTypes","text/plain");
server.setHandler(context);
// 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);
}
@After
public void stopServer() throws Exception
{
server.stop();
}
@Test
public void testGzipThenImagePipelining() throws Exception
{
testingdir.ensureEmpty();
File outputDir = testingdir.getDir();
PipelineHelper client = new PipelineHelper(serverUri);
try
{
File txtFile = MavenTestingUtils.getTestResourceFile("lots-of-fantasy-names.txt");
File pngFile = MavenTestingUtils.getTestResourceFile("jetty_logo.png");
// Size of content, as it exists on disk, without gzip compression.
long rawsize = txtFile.length() + pngFile.length();
Assert.assertThat("Ensure that we have sufficient file size to trigger chunking",rawsize,greaterThan(300000L));
String respHeader;
client.connect();
// Request text that will be gzipped + chunked in the response
client.issueGET("/lots-of-fantasy-names.txt",true, false);
respHeader = client.readResponseHeader();
System.out.println("Response Header #1 --\n" + respHeader);
Assert.assertThat("Content-Encoding should be gzipped",respHeader,containsString("Content-Encoding: gzip\r\n"));
Assert.assertThat("Transfer-Encoding should be chunked",respHeader,containsString("Transfer-Encoding: chunked\r\n"));
// Raw output / gzipped, writted to disk (checked for sha1sum later)
File rawOutputFile = new File(outputDir, "response-1.gz");
FileOutputStream rawOutputStream = new FileOutputStream(rawOutputFile);
long chunkSize = client.readChunkSize();
System.out.println("Chunk Size: " + chunkSize);
// Read only 20% - intentionally a partial read.
System.out.println("Attempting to read partial content ...");
int readBytes = client.readBody(rawOutputStream,(int)((float)chunkSize * 0.20f));
System.out.printf("Read %,d bytes%n",readBytes);
// Issue another request
client.issueGET("/jetty_logo.png",true, false);
// Finish reading chunks
System.out.println("Finish reading remaining chunks ...");
String line;
chunkSize = chunkSize - readBytes;
while (chunkSize > 0)
{
readBytes = client.readBody(rawOutputStream,(int)chunkSize);
System.out.printf("Read %,d bytes%n",readBytes);
line = client.readLine();
Assert.assertThat("Chunk delim should be an empty line with CR+LF",line,is(""));
chunkSize = client.readChunkSize();
System.out.printf("Next Chunk: (0x%X) %,d bytes%n",chunkSize,chunkSize);
}
// Inter-pipeline delim
line = client.readLine();
Assert.assertThat("Inter-pipeline delim should be an empty line with CR+LF",line,is(""));
// Sha1tracking for 1st Request
MessageDigest digestTxt = MessageDigest.getInstance("SHA1");
DigestOutputStream digesterTxt = new DigestOutputStream(new NoOpOutputStream(),digestTxt);
// Decompress 1st request and calculate sha1sum
IO.close(rawOutputStream);
FileInputStream rawInputStream = new FileInputStream(rawOutputFile);
GZIPInputStream ungzipStream = new GZIPInputStream(rawInputStream);
IO.copy(ungzipStream, digesterTxt);
// Read 2nd request http response header
respHeader = client.readResponseHeader();
System.out.println("Response Header #2 --\n" + respHeader);
Assert.assertThat("Content-Encoding should NOT be gzipped",respHeader,not(containsString("Content-Encoding: gzip\r\n")));
Assert.assertThat("Transfer-Encoding should NOT be chunked",respHeader,not(containsString("Transfer-Encoding: chunked\r\n")));
// Sha1tracking for 2nd Request
MessageDigest digestImg = MessageDigest.getInstance("SHA1");
DigestOutputStream digesterImg = new DigestOutputStream(new NoOpOutputStream(),digestImg);
// Read 2nd request body
int contentLength = client.getContentLength(respHeader);
Assert.assertThat("Image Content Length",(long)contentLength,is(pngFile.length()));
client.readBody(digesterImg,contentLength);
// Validate checksums
IO.close(rawOutputStream);
assertChecksum("lots-of-fantasy-names.txt",digestTxt);
IO.close(digesterImg);
assertChecksum("jetty_logo.png",digestImg);
}
finally
{
client.disconnect();
}
}
private void assertChecksum(String testResourceFile, MessageDigest digest) throws IOException
{
String expectedSha1 = loadSha1sum(testResourceFile + ".sha1");
String actualSha1 = Hex.asHex(digest.digest());
Assert.assertEquals(testResourceFile + " / SHA1Sum of content",expectedSha1,actualSha1);
}
private String loadSha1sum(String testResourceSha1Sum) throws IOException
{
File sha1File = MavenTestingUtils.getTestResourceFile(testResourceSha1Sum);
String contents = IO.readToString(sha1File);
Pattern pat = Pattern.compile("^[0-9A-Fa-f]*");
Matcher mat = pat.matcher(contents);
Assert.assertTrue("Should have found HEX code in SHA1 file: " + sha1File,mat.find());
return mat.group();
}
}

View File

@ -0,0 +1,286 @@
package org.eclipse.jetty.servlets;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.URI;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.log.StdErrLog;
import org.junit.Assert;
import static org.hamcrest.Matchers.not;
public class PipelineHelper
{
private static final Logger LOG = Log.getLogger(PipelineHelper.class);
private URI uri;
private SocketAddress endpoint;
private Socket socket;
private OutputStream outputStream;
private InputStream inputStream;
public PipelineHelper(URI uri)
{
if (LOG instanceof StdErrLog)
{
((StdErrLog)LOG).setLevel(StdErrLog.LEVEL_DEBUG);
}
this.uri = uri;
this.endpoint = new InetSocketAddress(uri.getHost(),uri.getPort());
}
/**
* Open the Socket to the destination endpoint and
*
* @return the open java Socket.
* @throws IOException
*/
public Socket connect() throws IOException
{
LOG.info("Connecting to endpoint: " + endpoint);
socket = new Socket();
socket.setTcpNoDelay(true);
socket.connect(endpoint,1000);
outputStream = socket.getOutputStream();
inputStream = socket.getInputStream();
return socket;
}
/**
* Issue a HTTP/1.1 GET request with Connection:keep-alive set.
*
* @param path
* the path to GET
* @param acceptGzipped
* to turn on acceptance of GZIP compressed responses
* @throws IOException
*/
public void issueGET(String path, boolean acceptGzipped, boolean close) throws IOException
{
LOG.debug("Issuing GET on " + path);
StringBuilder req = new StringBuilder();
req.append("GET ").append(uri.resolve(path).getPath()).append(" HTTP/1.1\r\n");
req.append("Host: ").append(uri.getHost()).append(":").append(uri.getPort()).append("\r\n");
req.append("User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 5_0_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A405 Safari/7534.48.3\r\n");
req.append("Accept: */*\r\n");
req.append("Referer: http://mycompany.com/index.html\r\n");
req.append("Accept-Language: en-us\r\n");
if (acceptGzipped)
{
req.append("Accept-Encoding: gzip, deflate\r\n");
}
req.append("Cookie: JSESSIONID=spqx8v8szylt1336t96vc6mw0\r\n");
if ( close )
{
req.append("Connection: close\r\n");
}
else
{
req.append("Connection: keep-alive\r\n");
}
req.append("\r\n");
LOG.debug("Request:" + req);
// Send HTTP GET Request
byte buf[] = req.toString().getBytes();
outputStream.write(buf,0,buf.length);
outputStream.flush();
}
public String readResponseHeader() throws IOException
{
// Read Response Header
socket.setSoTimeout(10000);
LOG.debug("Reading http header");
StringBuilder response = new StringBuilder();
boolean foundEnd = false;
String line;
while (!foundEnd)
{
line = readLine();
// System.out.printf("RESP: \"%s\"%n",line);
if (line.length() == 0)
{
foundEnd = true;
LOG.debug("Got full http response header");
}
else
{
response.append(line).append("\r\n");
}
}
return response.toString();
}
public String readLine() throws IOException
{
StringBuilder line = new StringBuilder();
boolean foundCR = false;
boolean foundLF = false;
int b;
while (!(foundCR && foundLF))
{
b = inputStream.read();
Assert.assertThat("Should not have hit EOL (yet) during chunk size read",(int)b,not(-1));
if (b == 0x0D)
{
foundCR = true;
}
else if (b == 0x0A)
{
foundLF = true;
}
else
{
foundCR = false;
foundLF = false;
line.append((char)b);
}
}
return line.toString();
}
public long readChunkSize() throws IOException
{
StringBuilder chunkSize = new StringBuilder();
String validHex = "0123456789ABCDEF";
boolean foundCR = false;
boolean foundLF = false;
int b;
while (!(foundCR && foundLF))
{
b = inputStream.read();
Assert.assertThat("Should not have hit EOL (yet) during chunk size read",(int)b,not(-1));
if (b == 0x0D)
{
foundCR = true;
}
else if (b == 0x0A)
{
foundLF = true;
}
else
{
foundCR = false;
foundLF = false;
// Must be valid char
char c = (char)b;
if (validHex.indexOf(c) >= 0)
{
chunkSize.append(c);
}
else
{
Assert.fail(String.format("Encountered invalid chunk size byte 0x%X",b));
}
}
}
return Long.parseLong(chunkSize.toString(),16);
}
public int readBody(OutputStream stream, int size) throws IOException
{
int left = size;
while (left > 0)
{
int val = inputStream.read();
try
{
if (left % 10 == 0)
Thread.sleep(1);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
if (val == (-1))
{
Assert.fail(String.format("Encountered an early EOL (expected another %,d bytes)",left));
}
stream.write(val);
left--;
}
return size - left;
}
public byte[] readResponseBody(int size) throws IOException
{
byte partial[] = new byte[size];
int readBytes = 0;
int bytesLeft = size;
while (readBytes < size)
{
int len = inputStream.read(partial,readBytes,bytesLeft);
Assert.assertThat("Read should not have hit EOL yet",len,not(-1));
System.out.printf("Read %,d bytes%n",len);
if (len > 0)
{
readBytes += len;
bytesLeft -= len;
}
}
return partial;
}
public OutputStream getOutputStream()
{
return outputStream;
}
public InputStream getInputStream()
{
return inputStream;
}
public SocketAddress getEndpoint()
{
return endpoint;
}
public Socket getSocket()
{
return socket;
}
public void disconnect() throws IOException
{
LOG.debug("disconnect");
socket.close();
}
public int getContentLength(String respHeader)
{
Pattern pat = Pattern.compile("Content-Length: ([0-9]*)",Pattern.CASE_INSENSITIVE);
Matcher mat = pat.matcher(respHeader);
if (mat.find())
{
try
{
return Integer.parseInt(mat.group(1));
}
catch (NumberFormatException e)
{
return -1;
}
}
else
{
// Undefined content length
return -1;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
b49b039adf40b695217e6e369513767a7c1e7dc6 lots-of-fantasy-names.txt

View File

@ -641,7 +641,7 @@ public class Main
}
File xml = new File(xmlFilename);
if (xml.exists() && xml.isFile() && xml.isAbsolute())
if (xml.exists() && xml.isFile())
{
return xml.getAbsolutePath();
}

View File

@ -576,6 +576,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
* @return Returns the Override Descriptor.
* @deprecated use {@link #getOverrideDescriptors()}
*/
@Deprecated
public String getOverrideDescriptor()
{
if (_overrideDescriptors.size()!=1)
@ -687,20 +688,26 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
private void loadServerClasses()
{
if (_serverClasses != null)
{
return;
}
//look for a Server attribute with the list of Server classes
//to apply to every web application. If not present, use our defaults.
// look for a Server attribute with the list of Server classes
// to apply to every web application. If not present, use our defaults.
Server server = getServer();
if (server != null)
{
Object serverClasses = server.getAttribute(SERVER_SRV_CLASSES);
if (serverClasses != null || serverClasses instanceof String[])
if (serverClasses != null && serverClasses instanceof String[])
{
_serverClasses = new ClasspathPattern((String[])serverClasses);
}
}
if (_serverClasses == null)
{
_serverClasses = new ClasspathPattern(__dftServerClasses);
}
}
/* ------------------------------------------------------------ */
@ -874,6 +881,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
* @param overrideDescriptor The overrideDescritpor to set.
* @deprecated use {@link #setOverrideDescriptors(List)}
*/
@Deprecated
public void setOverrideDescriptor(String overrideDescriptor)
{
_overrideDescriptors.clear();
@ -961,7 +969,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
@Override
public void addEventListener(EventListener listener)
{
setEventListeners((EventListener[])LazyList.addToArray(getEventListeners(), listener, EventListener.class));
setEventListeners(LazyList.addToArray(getEventListeners(), listener, EventListener.class));
}
@ -1218,6 +1226,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
public class Context extends ServletContextHandler.Context
{
/* ------------------------------------------------------------ */
@Override
public URL getResource(String path) throws MalformedURLException
{
Resource resource=WebAppContext.this.getResource(path);

View File

@ -15,9 +15,6 @@
*******************************************************************************/
package org.eclipse.jetty.websocket.helper;
import static org.hamcrest.Matchers.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@ -35,6 +32,8 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.log.StdErrLog;
import org.junit.Assert;
import static org.hamcrest.Matchers.is;
public class SafariD00
{
private static final Logger LOG = Log.getLogger(SafariD00.class);
@ -56,7 +55,7 @@ public class SafariD00
/**
* Open the Socket to the destination endpoint and
*
*
* @return the open java Socket.
* @throws IOException
*/
@ -75,7 +74,7 @@ public class SafariD00
/**
* Issue an Http websocket (Draft-0) upgrade request (using an example request captured from OSX/Safari)
*
*
* @throws UnsupportedEncodingException
*/
public void issueHandshake() throws IOException
@ -103,23 +102,22 @@ public class SafariD00
out.write(buf,0,buf.length);
out.flush();
// Read HTTP 101 Upgrade / Handshake Response
InputStreamReader reader = new InputStreamReader(in);
BufferedReader br = new BufferedReader(reader);
socket.setSoTimeout(10000);
LOG.debug("Reading http header");
boolean foundEnd = false;
String line;
while (!foundEnd)
// Read HTTP 101 Upgrade / Handshake Response
InputStreamReader reader = new InputStreamReader(in);
LOG.debug("Reading http headers");
int crlfs = 0;
while (true)
{
line = br.readLine();
// System.out.printf("RESP: %s%n",line);
if (line.length() == 0)
{
foundEnd = true;
}
int read = in.read();
if (read == '\r' || read == '\n')
++crlfs;
else
crlfs = 0;
if (crlfs == 4)
break;
}
// Read expected handshake hixie bytes

View File

@ -131,10 +131,11 @@ public abstract class AbstractClientCrossContextSessionTest
{
HttpSession session = request.getSession(false);
if (session == null)
{
session = request.getSession(true);
sessionId = session.getId();
}
sessionId = session.getId();
// Add something to the session
session.setAttribute("B", "B");