Merge branch 'master' into release
This commit is contained in:
commit
66fcd1ff7d
|
@ -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());
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 '\'';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -55,25 +55,24 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
|
|||
*
|
||||
* Here is a typical jetty.xml configuration would be: <pre>
|
||||
*
|
||||
* <Set name="handler">
|
||||
* <New id="Handlers" class="org.eclipse.jetty.rewrite.handler.RewriteHandler">
|
||||
* <New id="RewriteHandler" class="org.eclipse.jetty.rewrite.handler.RewriteHandler">
|
||||
* <Set name="rules">
|
||||
* <Array type="org.eclipse.jetty.rewrite.handler.Rule">
|
||||
*
|
||||
*
|
||||
* <Item>
|
||||
* <New id="rewrite" class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
|
||||
* <Set name="pattern">/*</Set>
|
||||
* <Set name="replacement">/test</Set>
|
||||
* </New>
|
||||
* </Item>
|
||||
*
|
||||
*
|
||||
* <Item>
|
||||
* <New id="rewrite" class="org.eclipse.jetty.rewrite.handler.ProxyRule">
|
||||
* <Set name="pattern">/*</Set>
|
||||
* <Set name="proxyTo">http://webtide.com:8080</Set>
|
||||
* </New>
|
||||
* </Item>
|
||||
*
|
||||
*
|
||||
* <Item>
|
||||
* <New id="response" class="org.eclipse.jetty.rewrite.handler.ResponsePatternRule">
|
||||
* <Set name="pattern">/session/</Set>
|
||||
|
@ -81,7 +80,7 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
|
|||
* <Set name="reason">Setting error code 400</Set>
|
||||
* </New>
|
||||
* </Item>
|
||||
*
|
||||
*
|
||||
* <Item>
|
||||
* <New id="header" class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
|
||||
* <Set name="pattern">*.jsp</Set>
|
||||
|
@ -89,7 +88,7 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
|
|||
* <Set name="value">dexter webserver</Set>
|
||||
* </New>
|
||||
* </Item>
|
||||
*
|
||||
*
|
||||
* <Item>
|
||||
* <New id="header" class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
|
||||
* <Set name="pattern">*.jsp</Set>
|
||||
|
@ -97,21 +96,21 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
|
|||
* <Set name="value">driven header purpose</Set>
|
||||
* </New>
|
||||
* </Item>
|
||||
*
|
||||
*
|
||||
* <Item>
|
||||
* <New id="redirect" class="org.eclipse.jetty.rewrite.handler.RedirectPatternRule">
|
||||
* <Set name="pattern">/test/dispatch</Set>
|
||||
* <Set name="location">http://jetty.eclipse.org</Set>
|
||||
* </New>
|
||||
* </Item>
|
||||
*
|
||||
*
|
||||
* <Item>
|
||||
* <New id="regexRewrite" class="org.eclipse.jetty.rewrite.handler.RewriteRegexRule">
|
||||
* <Set name="regex">/test-jaas/$</Set>
|
||||
* <Set name="replacement">/demo</Set>
|
||||
* </New>
|
||||
* </Item>
|
||||
*
|
||||
*
|
||||
* <Item>
|
||||
* <New id="forwardedHttps" class="org.eclipse.jetty.rewrite.handler.ForwardedSchemeHeaderRule">
|
||||
* <Set name="header">X-Forwarded-Scheme</Set>
|
||||
|
@ -119,10 +118,10 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
|
|||
* <Set name="scheme">https</Set>
|
||||
* </New>
|
||||
* </Item>
|
||||
*
|
||||
*
|
||||
* <Item>
|
||||
* <New id="virtualHost" class="org.eclipse.jetty.rewrite.handler.VirtualHostRuleContainer">
|
||||
*
|
||||
*
|
||||
* <Set name="virtualHosts">
|
||||
* <Array type="java.lang.String">
|
||||
* <Item>eclipse.com</Item>
|
||||
|
@ -131,7 +130,7 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
|
|||
* <Item>www.eclipse.org</Item>
|
||||
* </Array>
|
||||
* </Set>
|
||||
*
|
||||
*
|
||||
* <Call name="addRule">
|
||||
* <Arg>
|
||||
* <New class="org.eclipse.jetty.rewrite.handler.CookiePatternRule">
|
||||
|
@ -141,33 +140,34 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
|
|||
* </New>
|
||||
* </Arg>
|
||||
* </Call>
|
||||
*
|
||||
*
|
||||
* </New>
|
||||
* </ Item>
|
||||
*
|
||||
* </Item>
|
||||
*
|
||||
* </Array>
|
||||
* </Set>
|
||||
*
|
||||
* <Set name="handler">
|
||||
* <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
|
||||
* <Set name="handlers">
|
||||
* <Array type="org.eclipse.jetty.server.Handler">
|
||||
* <Item>
|
||||
* <New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
|
||||
* </Item>
|
||||
* <Item>
|
||||
* <New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>
|
||||
* </Item>
|
||||
* <Item>
|
||||
* <New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler"/>
|
||||
* </Item>
|
||||
* </Array>
|
||||
* </Set>
|
||||
* </New>
|
||||
* </Set>
|
||||
*
|
||||
* </New>
|
||||
* </Set>
|
||||
*
|
||||
* <Set name="handler">
|
||||
* <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
|
||||
* <Set name="handlers">
|
||||
* <Array type="org.eclipse.jetty.server.Handler">
|
||||
* <Item>
|
||||
* <Ref id="RewriteHandler"/>
|
||||
* </Item>
|
||||
* <Item>
|
||||
* <New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
|
||||
* </Item>
|
||||
* <Item>
|
||||
* <New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>
|
||||
* </Item>
|
||||
* <Item>
|
||||
* <New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler"/>
|
||||
* </Item>
|
||||
* </Array>
|
||||
* </Set>
|
||||
* </New>
|
||||
* </Set>
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
@ -0,0 +1 @@
|
|||
b49b039adf40b695217e6e369513767a7c1e7dc6 lots-of-fantasy-names.txt
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in New Issue