Merged branch 'jetty-9.4.x' into 'master'.
This commit is contained in:
commit
a51f800054
|
@ -0,0 +1,94 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.client.ssl;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import org.eclipse.jetty.io.AbstractConnection;
|
||||
import org.eclipse.jetty.io.ByteArrayEndPoint;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.io.ssl.SslConnection;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SslConnectionTest
|
||||
{
|
||||
@Test
|
||||
public void testSslConnectionClosedBeforeFill() throws Exception
|
||||
{
|
||||
File keyStore = MavenTestingUtils.getTestResourceFile("keystore.jks");
|
||||
SslContextFactory sslContextFactory = new SslContextFactory();
|
||||
sslContextFactory.setKeyStorePath(keyStore.getAbsolutePath());
|
||||
sslContextFactory.setKeyStorePassword("storepwd");
|
||||
sslContextFactory.start();
|
||||
|
||||
ByteBufferPool byteBufferPool = new MappedByteBufferPool();
|
||||
QueuedThreadPool threadPool = new QueuedThreadPool();
|
||||
threadPool.start();
|
||||
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
|
||||
SSLEngine sslEngine = sslContextFactory.newSSLEngine();
|
||||
sslEngine.setUseClientMode(false);
|
||||
SslConnection sslConnection = new SslConnection(byteBufferPool, threadPool, endPoint, sslEngine);
|
||||
EndPoint sslEndPoint = sslConnection.getDecryptedEndPoint();
|
||||
sslEndPoint.setConnection(new AbstractConnection(sslEndPoint, threadPool)
|
||||
{
|
||||
@Override
|
||||
public void onFillable()
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
// There are no bytes in the endPoint, so we fill zero.
|
||||
// However, this will trigger state changes in SSLEngine
|
||||
// that will later cause it to throw ISE("Internal error").
|
||||
sslEndPoint.fill(BufferUtil.EMPTY_BUFFER);
|
||||
|
||||
// Close the connection before filling.
|
||||
sslEndPoint.shutdownOutput();
|
||||
|
||||
// Put some bytes in the endPoint to trigger
|
||||
// the required state changes in SSLEngine.
|
||||
byte[] bytes = new byte[]{0x16, 0x03, 0x03, 0x00, 0x00};
|
||||
endPoint.addInput(ByteBuffer.wrap(bytes));
|
||||
|
||||
// This attempt to read, if not guarded, throws ISE("Internal error").
|
||||
// We want SSLHandshakeException to be thrown instead, because it is
|
||||
// handled better (it is an IOException) by the Connection code that
|
||||
// reads from the EndPoint.
|
||||
try
|
||||
{
|
||||
sslEndPoint.fill(BufferUtil.EMPTY_BUFFER);
|
||||
Assert.fail();
|
||||
}
|
||||
catch (SSLHandshakeException x)
|
||||
{
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@
|
|||
==== Setting up the Classpath
|
||||
|
||||
You will need to place the following Jetty jar files onto the classpath of your application.
|
||||
You can obtain them from the http://download.eclipse.org/jetty/stable-9/dist/[Jetty distribution], or the http://central.maven.org/maven2/org/eclipse/jetty/jetty-annotations[Maven repository]:
|
||||
You can obtain them from the https://www.eclipse.org/jetty/download.html[Jetty distribution], or the http://central.maven.org/maven2/org/eclipse/jetty/jetty-annotations[Maven repository]:
|
||||
|
||||
....
|
||||
jetty-plus.jar
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
[[startup-base-and-home]]
|
||||
=== Managing Jetty Base and Jetty Home
|
||||
|
||||
Starting with Jetty 9.1, it is possible to maintain a separation between the binary installation of the standalone Jetty (known as `${jetty.home}`), and the customizations for your specific environment (known as `${jetty.base}`).
|
||||
Instead of managing multiple Jetty implementations out of several different distribution locations, it is possible to maintain a separation between the binary installation of the standalone Jetty (known as `${jetty.home}`), and the customizations for your specific environment(s) (known as `${jetty.base}`).
|
||||
There should always only be one Jetty Home, but there can be multiple Jetty Base directories that references.
|
||||
|
||||
Jetty Base::
|
||||
* Also known as the `${jetty.base}` property.
|
||||
|
@ -26,6 +27,14 @@ Jetty Home::
|
|||
* Also known as the `${jetty.home}` property.
|
||||
* This is the location for the Jetty distribution binaries, default XML IoC configurations, and default module definitions.
|
||||
|
||||
____
|
||||
[IMPORTANT]
|
||||
Jetty Home should always be treated as a standard of truth.
|
||||
All configuration modifications, changes and additions should be made in the appropriate Jetty Base directory.
|
||||
____
|
||||
|
||||
[[base-vs-home-resolution]]
|
||||
|
||||
Potential configuration is resolved from these 2 directory locations.
|
||||
|
||||
Check Jetty Base::
|
||||
|
@ -241,8 +250,8 @@ jetty.dump.stop=false
|
|||
--module=annotations
|
||||
....
|
||||
|
||||
The `${jetty.base}/start.ini` is the main startup configuration entry point for Jetty.
|
||||
In this example you will see that we are enabling a few modules for Jetty, specifying some properties, and also referencing some Jetty IoC XML files (namely the `etc/demo-rewrite-rules.xml` and `etc/test-realm.xml` files)
|
||||
In this example, `${jetty.base}/start.ini` is the main startup configuration entry point for Jetty.
|
||||
You will see that we are enabling a few modules for Jetty, specifying some properties, and also referencing some Jetty IoC XML files (namely the `etc/demo-rewrite-rules.xml` and `etc/test-realm.xml` files)
|
||||
|
||||
When Jetty's `start.jar` resolves the entries in the `start.ini`, it will follow the link:#base-vs-home-resolution[resolution rules above].
|
||||
|
||||
|
|
|
@ -18,9 +18,9 @@
|
|||
=== Using the $\{jetty.home} and $\{jetty.base} Concepts to Configure
|
||||
Security
|
||||
|
||||
Jetty 9.1 introduced `${jetty.base}` and `${jetty.home}`.
|
||||
Jetty implementations are structured around the idea of `${jetty.base}` and `${jetty.home}` directories.
|
||||
|
||||
* `${jetty.home}` is the directory location for the jetty distribution (the binaries).
|
||||
* `${jetty.home}` is the directory location for the Jetty distribution (the binaries) should not be modified.
|
||||
* `${jetty.base}` is the directory location for your customizations to the distribution.
|
||||
|
||||
This separation:
|
||||
|
|
|
@ -35,7 +35,7 @@ Its purpose is to provide almost the same functionality as the Jetty plugin for
|
|||
|
||||
To set up your project for Ant to run Jetty, you need a Jetty distribution and the jetty-ant Jar:
|
||||
|
||||
1. http://download.eclipse.org/jetty/[Download] a Jetty distribution and unpack it in the local filesystem.
|
||||
1. https://www.eclipse.org/jetty/download.html[Download] a Jetty distribution and unpack it in the local filesystem.
|
||||
2. http://central.maven.org/maven2/org/eclipse/jetty/jetty-ant/[Get] the jetty-ant Jar.
|
||||
3. Make a directory in your project called `jetty-lib/`.
|
||||
4. Copy all of the Jars in your Jetty distribution's `lib` directory, and all its subdirectories, into your new `jetty-lib` dir.
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
The standalone Jetty distribution is available for download from the Eclipse Foundation:
|
||||
____
|
||||
*Jetty*
|
||||
http://download.eclipse.org/jetty
|
||||
https://www.eclipse.org/jetty/download.html
|
||||
____
|
||||
|
||||
It is available in both zip and gzip formats; download the one most appropriate for your system.
|
||||
|
|
|
@ -105,6 +105,12 @@ jetty.home::
|
|||
jetty.base::
|
||||
The property that defines the location of a specific instance of a jetty server, its configuration, logs and web applications (typically start.ini, start.d, logs and webapps)
|
||||
|
||||
____
|
||||
[IMPORTANT]
|
||||
Your Jetty Home directory should be treated as a standard of truth and remain unmodified or changed.
|
||||
Changes or additions to your configuration should take place in the Jetty Base directory.
|
||||
____
|
||||
|
||||
The `jetty.home` and `jetty.base` properties may be explicitly set on the command line, or they can be inferred from the environment if used with commands like:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<assembly-directory>${basedir}/target/home</assembly-directory>
|
||||
<assembly-directory>${basedir}/target/jetty-home</assembly-directory>
|
||||
<jetty-setuid-version>1.0.3</jetty-setuid-version>
|
||||
</properties>
|
||||
|
||||
|
@ -340,6 +340,24 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>set jetty.sh</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<tasks>
|
||||
<chmod dir="${assembly-directory}/bin" perm="755" includes="**/*.sh" />
|
||||
</tasks>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
|
|
|
@ -274,7 +274,8 @@ fi
|
|||
#####################################################
|
||||
if [ -z "$JETTY_RUN" ]
|
||||
then
|
||||
JETTY_RUN=$(findDirectory -w /var/run /usr/var/run $JETTY_BASE /tmp)
|
||||
JETTY_RUN=$(findDirectory -w /var/run /usr/var/run $JETTY_BASE /tmp)/jetty
|
||||
[ -d "$JETTY_RUN" ] || mkdir $JETTY_RUN
|
||||
fi
|
||||
|
||||
#####################################################
|
||||
|
@ -433,7 +434,7 @@ case "$ACTION" in
|
|||
CH_USER="-c$JETTY_USER"
|
||||
fi
|
||||
|
||||
start-stop-daemon -S -p"$JETTY_PID" $CH_USER -d"$JETTY_BASE" -b -m -a "$JAVA" -- "${RUN_ARGS[@]}" start-log-file="$JETTY_LOGS/start.log"
|
||||
start-stop-daemon -S -p"$JETTY_PID" $CH_USER -d"$JETTY_BASE" -b -m -a "$JAVA" -- "${RUN_ARGS[@]}" start-log-file="$JETTY_RUN/start.log"
|
||||
|
||||
else
|
||||
|
||||
|
@ -455,7 +456,7 @@ case "$ACTION" in
|
|||
chown "$JETTY_USER" "$JETTY_PID"
|
||||
# FIXME: Broken solution: wordsplitting, pathname expansion, arbitrary command execution, etc.
|
||||
su - "$JETTY_USER" $SU_SHELL -c "
|
||||
exec ${RUN_CMD[*]} start-log-file="$JETTY_LOGS/start.log" > /dev/null &
|
||||
exec ${RUN_CMD[*]} start-log-file="$JETTY_RUN/start.log" > /dev/null &
|
||||
disown \$!
|
||||
echo \$! > '$JETTY_PID'"
|
||||
else
|
|
@ -28,8 +28,6 @@ import org.eclipse.jetty.http.HttpTokens.EndOfContent;
|
|||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
||||
import org.eclipse.jetty.util.ArrayTrie;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.HostPort;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.Trie;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.Utf8StringBuilder;
|
||||
|
@ -133,6 +131,7 @@ public class HttpParser
|
|||
CHUNK_SIZE,
|
||||
CHUNK_PARAMS,
|
||||
CHUNK,
|
||||
CHUNK_TRAILER,
|
||||
CHUNK_END,
|
||||
END,
|
||||
CLOSE, // The associated stream/endpoint should be closed
|
||||
|
@ -682,7 +681,7 @@ public class HttpParser
|
|||
if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
|
||||
{
|
||||
LOG.warn("URI is too large >"+_maxHeaderBytes);
|
||||
throw new BadMessageException(HttpStatus.REQUEST_URI_TOO_LONG_414);
|
||||
throw new BadMessageException(HttpStatus.URI_TOO_LONG_414);
|
||||
}
|
||||
_uri.append(array,p-1,len+1);
|
||||
buffer.position(i-buffer.arrayOffset());
|
||||
|
@ -1383,6 +1382,7 @@ public class HttpParser
|
|||
break;
|
||||
|
||||
case EOF_CONTENT:
|
||||
case CHUNK_END:
|
||||
setState(State.CLOSED);
|
||||
return _handler.messageComplete();
|
||||
|
||||
|
@ -1391,6 +1391,7 @@ public class HttpParser
|
|||
case CHUNK_SIZE:
|
||||
case CHUNK_PARAMS:
|
||||
case CHUNK:
|
||||
case CHUNK_TRAILER:
|
||||
setState(State.CLOSED);
|
||||
_handler.earlyEOF();
|
||||
break;
|
||||
|
@ -1592,7 +1593,6 @@ public class HttpParser
|
|||
|
||||
case CHUNK_END:
|
||||
{
|
||||
// TODO handle chunk trailer
|
||||
ch=next(buffer);
|
||||
if (ch==0)
|
||||
break;
|
||||
|
@ -1601,7 +1601,19 @@ public class HttpParser
|
|||
setState(State.END);
|
||||
return _handler.messageComplete();
|
||||
}
|
||||
throw new IllegalCharacterException(_state,ch,buffer);
|
||||
setState(State.CHUNK_TRAILER);
|
||||
break;
|
||||
}
|
||||
|
||||
case CHUNK_TRAILER:
|
||||
{
|
||||
// TODO handle chunk trailer values
|
||||
ch=next(buffer);
|
||||
if (ch==0)
|
||||
break;
|
||||
if (ch == HttpTokens.LINE_FEED)
|
||||
setState(State.CHUNK_END);
|
||||
break;
|
||||
}
|
||||
|
||||
case CLOSED:
|
||||
|
|
|
@ -802,6 +802,101 @@ public class HttpParserTest
|
|||
Assert.assertTrue(_messageCompleted);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChunkParseTrailer() throws Exception
|
||||
{
|
||||
ByteBuffer buffer = BufferUtil.toBuffer(
|
||||
"GET /chunk HTTP/1.0\r\n"
|
||||
+ "Header1: value1\r\n"
|
||||
+ "Transfer-Encoding: chunked\r\n"
|
||||
+ "\r\n"
|
||||
+ "a;\r\n"
|
||||
+ "0123456789\r\n"
|
||||
+ "1a\r\n"
|
||||
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n"
|
||||
+ "0\r\n"
|
||||
+ "Trailer: value\r\n"
|
||||
+ "\r\n");
|
||||
HttpParser.RequestHandler handler = new Handler();
|
||||
HttpParser parser = new HttpParser(handler);
|
||||
parseAll(parser, buffer);
|
||||
|
||||
Assert.assertEquals("GET", _methodOrVersion);
|
||||
Assert.assertEquals("/chunk", _uriOrStatus);
|
||||
Assert.assertEquals("HTTP/1.0", _versionOrReason);
|
||||
Assert.assertEquals(1, _headers);
|
||||
Assert.assertEquals("Header1", _hdr[0]);
|
||||
Assert.assertEquals("value1", _val[0]);
|
||||
Assert.assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
|
||||
|
||||
Assert.assertTrue(_headerCompleted);
|
||||
Assert.assertTrue(_messageCompleted);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChunkParseBadTrailer() throws Exception
|
||||
{
|
||||
ByteBuffer buffer = BufferUtil.toBuffer(
|
||||
"GET /chunk HTTP/1.0\r\n"
|
||||
+ "Header1: value1\r\n"
|
||||
+ "Transfer-Encoding: chunked\r\n"
|
||||
+ "\r\n"
|
||||
+ "a;\r\n"
|
||||
+ "0123456789\r\n"
|
||||
+ "1a\r\n"
|
||||
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n"
|
||||
+ "0\r\n"
|
||||
+ "Trailer: value");
|
||||
HttpParser.RequestHandler handler = new Handler();
|
||||
HttpParser parser = new HttpParser(handler);
|
||||
parseAll(parser, buffer);
|
||||
parser.atEOF();
|
||||
parser.parseNext(BufferUtil.EMPTY_BUFFER);
|
||||
|
||||
Assert.assertEquals("GET", _methodOrVersion);
|
||||
Assert.assertEquals("/chunk", _uriOrStatus);
|
||||
Assert.assertEquals("HTTP/1.0", _versionOrReason);
|
||||
Assert.assertEquals(1, _headers);
|
||||
Assert.assertEquals("Header1", _hdr[0]);
|
||||
Assert.assertEquals("value1", _val[0]);
|
||||
Assert.assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
|
||||
|
||||
Assert.assertTrue(_headerCompleted);
|
||||
Assert.assertTrue(_early);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testChunkParseNoTrailer() throws Exception
|
||||
{
|
||||
ByteBuffer buffer = BufferUtil.toBuffer(
|
||||
"GET /chunk HTTP/1.0\r\n"
|
||||
+ "Header1: value1\r\n"
|
||||
+ "Transfer-Encoding: chunked\r\n"
|
||||
+ "\r\n"
|
||||
+ "a;\r\n"
|
||||
+ "0123456789\r\n"
|
||||
+ "1a\r\n"
|
||||
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n"
|
||||
+ "0\r\n");
|
||||
HttpParser.RequestHandler handler = new Handler();
|
||||
HttpParser parser = new HttpParser(handler);
|
||||
parseAll(parser, buffer);
|
||||
parser.atEOF();
|
||||
parser.parseNext(BufferUtil.EMPTY_BUFFER);
|
||||
|
||||
Assert.assertEquals("GET", _methodOrVersion);
|
||||
Assert.assertEquals("/chunk", _uriOrStatus);
|
||||
Assert.assertEquals("HTTP/1.0", _versionOrReason);
|
||||
Assert.assertEquals(1, _headers);
|
||||
Assert.assertEquals("Header1", _hdr[0]);
|
||||
Assert.assertEquals("value1", _val[0]);
|
||||
Assert.assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
|
||||
|
||||
Assert.assertTrue(_headerCompleted);
|
||||
Assert.assertTrue(_messageCompleted);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartEOF() throws Exception
|
||||
{
|
||||
|
|
|
@ -33,9 +33,12 @@ import javax.servlet.http.HttpServlet;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HostPortHttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http2.ErrorCode;
|
||||
import org.eclipse.jetty.http2.api.Session;
|
||||
|
@ -54,12 +57,26 @@ import org.junit.Test;
|
|||
|
||||
public class PushCacheFilterTest extends AbstractTest
|
||||
{
|
||||
private String contextPath = "/push";
|
||||
|
||||
@Override
|
||||
protected void customizeContext(ServletContextHandler context)
|
||||
{
|
||||
context.setContextPath(contextPath);
|
||||
context.addFilter(PushCacheFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MetaData.Request newRequest(String method, String pathInfo, HttpFields fields)
|
||||
{
|
||||
return new MetaData.Request(method, HttpScheme.HTTP, new HostPortHttpField("localhost:" + connector.getLocalPort()), contextPath + servletPath + pathInfo, HttpVersion.HTTP_2, fields);
|
||||
}
|
||||
|
||||
private String newURI(String pathInfo)
|
||||
{
|
||||
return "http://localhost:" + connector.getLocalPort() + contextPath + servletPath + pathInfo;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPush() throws Exception
|
||||
{
|
||||
|
@ -83,7 +100,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
final Session session = newClient(new Session.Listener.Adapter());
|
||||
|
||||
// Request for the primary and secondary resource to build the cache.
|
||||
final String referrerURI = "http://localhost:" + connector.getLocalPort() + servletPath + primaryResource;
|
||||
final String referrerURI = newURI(primaryResource);
|
||||
HttpFields primaryFields = new HttpFields();
|
||||
MetaData.Request primaryRequest = newRequest("GET", primaryResource, primaryFields);
|
||||
final CountDownLatch warmupLatch = new CountDownLatch(1);
|
||||
|
@ -115,23 +132,16 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
|
||||
// Request again the primary resource, we should get the secondary resource pushed.
|
||||
primaryRequest = newRequest("GET", primaryResource, primaryFields);
|
||||
final CountDownLatch primaryResponseLatch = new CountDownLatch(1);
|
||||
final CountDownLatch pushLatch = new CountDownLatch(1);
|
||||
final CountDownLatch primaryResponseLatch = new CountDownLatch(2);
|
||||
final CountDownLatch pushLatch = new CountDownLatch(2);
|
||||
session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
|
||||
public void onHeaders(Stream stream, HeadersFrame frame)
|
||||
{
|
||||
return new Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
callback.succeeded();
|
||||
if (frame.isEndStream())
|
||||
pushLatch.countDown();
|
||||
}
|
||||
};
|
||||
MetaData.Response response = (MetaData.Response)frame.getMetaData();
|
||||
if (response.getStatus() == HttpStatus.OK_200)
|
||||
primaryResponseLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -141,6 +151,29 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
if (frame.isEndStream())
|
||||
primaryResponseLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
|
||||
{
|
||||
return new Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(Stream stream, HeadersFrame frame)
|
||||
{
|
||||
MetaData.Response response = (MetaData.Response)frame.getMetaData();
|
||||
if (response.getStatus() == HttpStatus.OK_200)
|
||||
pushLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
callback.succeeded();
|
||||
if (frame.isEndStream())
|
||||
pushLatch.countDown();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(pushLatch.await(5, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(primaryResponseLatch.await(5, TimeUnit.SECONDS));
|
||||
|
@ -257,7 +290,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
final Session session = newClient(new Session.Listener.Adapter());
|
||||
|
||||
// Request for the primary and secondary resource to build the cache.
|
||||
final String primaryURI = "http://localhost:" + connector.getLocalPort() + servletPath + primaryResource;
|
||||
final String primaryURI = newURI(primaryResource);
|
||||
HttpFields primaryFields = new HttpFields();
|
||||
MetaData.Request primaryRequest = newRequest("GET", primaryResource, primaryFields);
|
||||
final CountDownLatch warmupLatch = new CountDownLatch(1);
|
||||
|
@ -360,7 +393,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
final Session session = newClient(new Session.Listener.Adapter());
|
||||
|
||||
// Request for the primary and secondary resource to build the cache.
|
||||
final String primaryURI = "http://localhost:" + connector.getLocalPort() + servletPath + primaryResource;
|
||||
final String primaryURI = newURI(primaryResource);
|
||||
HttpFields primaryFields = new HttpFields();
|
||||
MetaData.Request primaryRequest = newRequest("GET", primaryResource, primaryFields);
|
||||
final CountDownLatch warmupLatch = new CountDownLatch(1);
|
||||
|
@ -451,7 +484,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
final Session session = newClient(new Session.Listener.Adapter());
|
||||
|
||||
// Request for the primary, secondary and tertiary resource to build the cache.
|
||||
final String primaryURI = "http://localhost:" + connector.getLocalPort() + servletPath + primaryResource;
|
||||
final String primaryURI = newURI(primaryResource);
|
||||
HttpFields primaryFields = new HttpFields();
|
||||
MetaData.Request primaryRequest = newRequest("GET", primaryResource, primaryFields);
|
||||
final CountDownLatch warmupLatch = new CountDownLatch(2);
|
||||
|
@ -464,7 +497,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
if (frame.isEndStream())
|
||||
{
|
||||
// Request for the secondary resources.
|
||||
String secondaryURI1 = "http://localhost:" + connector.getLocalPort() + servletPath + secondaryResource1;
|
||||
String secondaryURI1 = newURI(secondaryResource1);
|
||||
HttpFields secondaryFields1 = new HttpFields();
|
||||
secondaryFields1.put(HttpHeader.REFERER, primaryURI);
|
||||
MetaData.Request secondaryRequest1 = newRequest("GET", secondaryResource1, secondaryFields1);
|
||||
|
@ -636,7 +669,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
}
|
||||
}
|
||||
});
|
||||
final String primaryURI = "http://localhost:" + connector.getLocalPort() + servletPath + primaryResource;
|
||||
final String primaryURI = newURI(primaryResource);
|
||||
|
||||
final Session session = newClient(new Session.Listener.Adapter());
|
||||
|
||||
|
@ -733,7 +766,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
final Session session = newClient(new Session.Listener.Adapter());
|
||||
|
||||
// Request for the primary and secondary resource to build the cache.
|
||||
final String primaryURI = "http://localhost:" + connector.getLocalPort() + servletPath + primaryResource;
|
||||
final String primaryURI = newURI(primaryResource);
|
||||
HttpFields primaryFields = new HttpFields();
|
||||
MetaData.Request primaryRequest = newRequest("GET", primaryResource, primaryFields);
|
||||
final CountDownLatch warmupLatch = new CountDownLatch(1);
|
||||
|
@ -776,7 +809,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
MetaData metaData = frame.getMetaData();
|
||||
Assert.assertTrue(metaData instanceof MetaData.Request);
|
||||
MetaData.Request pushedRequest = (MetaData.Request)metaData;
|
||||
Assert.assertEquals(servletPath + secondaryResource, pushedRequest.getURI().getPathQuery());
|
||||
Assert.assertEquals(contextPath + servletPath + secondaryResource, pushedRequest.getURI().getPathQuery());
|
||||
return new Adapter()
|
||||
{
|
||||
@Override
|
||||
|
@ -826,7 +859,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
final Session session = newClient(new Session.Listener.Adapter());
|
||||
|
||||
// Request for the primary and secondary resource to build the cache.
|
||||
final String referrerURI = "http://localhost:" + connector.getLocalPort() + servletPath + primaryResource;
|
||||
final String referrerURI = newURI(primaryResource);
|
||||
HttpFields primaryFields = new HttpFields();
|
||||
MetaData.Request primaryRequest = newRequest("GET", primaryResource, primaryFields);
|
||||
final CountDownLatch warmupLatch = new CountDownLatch(1);
|
||||
|
@ -921,7 +954,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
});
|
||||
|
||||
// Request for the primary and secondary resource to build the cache.
|
||||
final String referrerURI = "http://localhost:" + connector.getLocalPort() + servletPath + primaryResource;
|
||||
final String referrerURI = newURI(primaryResource);
|
||||
HttpFields primaryFields = new HttpFields();
|
||||
MetaData.Request primaryRequest = newRequest("GET", primaryResource, primaryFields);
|
||||
final CountDownLatch warmupLatch = new CountDownLatch(1);
|
||||
|
|
|
@ -606,6 +606,9 @@ public class SslConnection extends AbstractConnection
|
|||
// Let's try reading some encrypted data... even if we have some already.
|
||||
int net_filled = getEndPoint().fill(_encryptedInput);
|
||||
|
||||
if (net_filled > 0 && !_handshaken && _sslEngine.isOutboundDone())
|
||||
throw new SSLHandshakeException("Closed during handshake");
|
||||
|
||||
decryption: while (true)
|
||||
{
|
||||
// Let's unwrap even if we have no net data because in that
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<Set name="minGzipSize"><Property name="jetty.gzip.minGzipSize" deprecated="gzip.minGzipSize" default="2048"/></Set>
|
||||
<Set name="checkGzExists"><Property name="jetty.gzip.checkGzExists" deprecated="gzip.checkGzExists" default="false"/></Set>
|
||||
<Set name="compressionLevel"><Property name="jetty.gzip.compressionLevel" deprecated="gzip.compressionLevel" default="-1"/></Set>
|
||||
<Set name="inflateBufferSize"><Property name="jetty.gzip.inflateBufferSize" default="0/></Set>
|
||||
<Set name="inflateBufferSize"><Property name="jetty.gzip.inflateBufferSize" default="0"/></Set>
|
||||
|
||||
<Set name="excludedAgentPatterns">
|
||||
<Array type="String">
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.eclipse.jetty.server.Handler;
|
|||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.SessionIdManager;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
@ -151,16 +152,21 @@ public class DefaultSessionIdManager extends ContainerLifeCycle implements Sessi
|
|||
* lookup the worker name that can be dynamically set by a request
|
||||
* Customizer.
|
||||
*
|
||||
* @param workerName the name of the worker
|
||||
* @param workerName the name of the worker, if null it is coerced to empty string
|
||||
*/
|
||||
public void setWorkerName(String workerName)
|
||||
{
|
||||
if (isRunning())
|
||||
throw new IllegalStateException(getState());
|
||||
if (workerName == null)
|
||||
_workerName = "";
|
||||
else
|
||||
{
|
||||
if (workerName.contains("."))
|
||||
throw new IllegalArgumentException("Name cannot contain '.'");
|
||||
_workerName=workerName;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
|
@ -281,7 +287,7 @@ public class DefaultSessionIdManager extends ContainerLifeCycle implements Sessi
|
|||
|
||||
//add in the id of the node to ensure unique id across cluster
|
||||
//NOTE this is different to the node suffix which denotes which node the request was received on
|
||||
if (_workerName!=null)
|
||||
if (!StringUtil.isBlank(_workerName))
|
||||
id=_workerName + id;
|
||||
|
||||
id = id+Long.toString(COUNTER.getAndIncrement());
|
||||
|
@ -417,7 +423,7 @@ public class DefaultSessionIdManager extends ContainerLifeCycle implements Sessi
|
|||
@Override
|
||||
public String getExtendedId(String clusterId, HttpServletRequest request)
|
||||
{
|
||||
if (_workerName!=null)
|
||||
if (!StringUtil.isBlank(_workerName))
|
||||
{
|
||||
if (_workerAttr==null)
|
||||
return clusterId+'.'+_workerName;
|
||||
|
|
|
@ -605,9 +605,12 @@ public class Session implements SessionHandler.SessionIf
|
|||
{
|
||||
checkLocked();
|
||||
|
||||
if (_state != State.VALID)
|
||||
if (_state == State.INVALID)
|
||||
throw new IllegalStateException("Not valid for write: id="+_sessionData.getId()+" created="+_sessionData.getCreated()+" accessed="+_sessionData.getAccessed()+" lastaccessed="+_sessionData.getLastAccessed()+" maxInactiveMs="+_sessionData.getMaxInactiveMs()+" expiry="+_sessionData.getExpiry());
|
||||
|
||||
if (_state == State.INVALIDATING)
|
||||
return; //in the process of being invalidated, listeners may try to remove attributes
|
||||
|
||||
if (!isResident())
|
||||
throw new IllegalStateException("Not valid for write: id="+_sessionData.getId()+" not resident");
|
||||
}
|
||||
|
@ -626,6 +629,9 @@ public class Session implements SessionHandler.SessionIf
|
|||
if (_state == State.INVALID)
|
||||
throw new IllegalStateException("Invalid for read: id="+_sessionData.getId()+" created="+_sessionData.getCreated()+" accessed="+_sessionData.getAccessed()+" lastaccessed="+_sessionData.getLastAccessed()+" maxInactiveMs="+_sessionData.getMaxInactiveMs()+" expiry="+_sessionData.getExpiry());
|
||||
|
||||
if (_state == State.INVALIDATING)
|
||||
return;
|
||||
|
||||
if (!isResident())
|
||||
throw new IllegalStateException("Invalid for read: id="+_sessionData.getId()+" not resident");
|
||||
}
|
||||
|
|
|
@ -632,7 +632,9 @@ public class ResponseTest
|
|||
DefaultSessionCache ss = new DefaultSessionCache(handler);
|
||||
NullSessionDataStore ds = new NullSessionDataStore();
|
||||
ss.setSessionDataStore(ds);
|
||||
handler.setSessionIdManager(new DefaultSessionIdManager(_server));
|
||||
DefaultSessionIdManager idMgr = new DefaultSessionIdManager(_server);
|
||||
idMgr.setWorkerName(null);
|
||||
handler.setSessionIdManager(idMgr);
|
||||
request.setSessionHandler(handler);
|
||||
TestSession tsession = new TestSession(handler, "12345");
|
||||
tsession.setExtendedId(handler.getSessionIdManager().getExtendedId("12345", null));
|
||||
|
@ -717,7 +719,9 @@ public class ResponseTest
|
|||
DefaultSessionCache ss = new DefaultSessionCache(handler);
|
||||
handler.setSessionCache(ss);
|
||||
ss.setSessionDataStore(ds);
|
||||
handler.setSessionIdManager(new DefaultSessionIdManager(_server));
|
||||
DefaultSessionIdManager idMgr = new DefaultSessionIdManager(_server);
|
||||
idMgr.setWorkerName(null);
|
||||
handler.setSessionIdManager(idMgr);
|
||||
request.setSessionHandler(handler);
|
||||
request.setSession(new TestSession(handler, "12345"));
|
||||
handler.setCheckingRemoteSessionIdEncoding(false);
|
||||
|
|
|
@ -49,7 +49,6 @@ import org.eclipse.jetty.http.HttpURI;
|
|||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
||||
|
@ -161,7 +160,7 @@ public class PushCacheFilter implements Filter
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} {} referrer={} conditional={}", request.getMethod(), request.getRequestURI(), referrer, conditional);
|
||||
|
||||
String path = URIUtil.addPaths(request.getServletPath(), request.getPathInfo());
|
||||
String path = request.getRequestURI();
|
||||
String query = request.getQueryString();
|
||||
if (query != null)
|
||||
path += "?" + query;
|
||||
|
@ -183,12 +182,11 @@ public class PushCacheFilter implements Filter
|
|||
String referrerPath = referrerURI.getPath();
|
||||
if (referrerPath == null)
|
||||
referrerPath = "/";
|
||||
if (referrerPath.startsWith(request.getContextPath()))
|
||||
if (referrerPath.startsWith(request.getContextPath() + "/"))
|
||||
{
|
||||
String referrerPathNoContext = referrerPath.substring(request.getContextPath().length());
|
||||
if (!referrerPathNoContext.equals(path))
|
||||
if (!referrerPath.equals(path))
|
||||
{
|
||||
PrimaryResource primaryResource = _cache.get(referrerPathNoContext);
|
||||
PrimaryResource primaryResource = _cache.get(referrerPath);
|
||||
if (primaryResource != null)
|
||||
{
|
||||
long primaryTimestamp = primaryResource._timestamp.get();
|
||||
|
@ -203,19 +201,19 @@ public class PushCacheFilter implements Filter
|
|||
if (associated.add(path))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Associated {} to {}", path, referrerPathNoContext);
|
||||
LOG.debug("Associated {} to {}", path, referrerPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Not associated {} to {}, exceeded max associations of {}", path, referrerPathNoContext, _maxAssociations);
|
||||
LOG.debug("Not associated {} to {}, exceeded max associations of {}", path, referrerPath, _maxAssociations);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Not associated {} to {}, outside associate period of {}ms", path, referrerPathNoContext, _associatePeriod);
|
||||
LOG.debug("Not associated {} to {}, outside associate period of {}ms", path, referrerPath, _associatePeriod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -223,9 +221,14 @@ public class PushCacheFilter implements Filter
|
|||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Not associated {} to {}, referring to self", path, referrerPathNoContext);
|
||||
LOG.debug("Not associated {} to {}, referring to self", path, referrerPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Not associated {} to {}, different context", path, referrerPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -145,7 +145,12 @@ public class WebSocketServerContainerInitializer implements ServletContainerInit
|
|||
// Create Filter
|
||||
if(isEnabledViaContext(context.getServletContext(), ADD_DYNAMIC_FILTER_KEY, true))
|
||||
{
|
||||
WebSocketUpgradeFilter.configureContext(context);
|
||||
String instanceKey = WebSocketUpgradeFilter.class.getName() + ".SCI";
|
||||
if(context.getAttribute(instanceKey) == null)
|
||||
{
|
||||
WebSocketUpgradeFilter wsuf = WebSocketUpgradeFilter.configureContext(context);
|
||||
context.setAttribute(instanceKey, wsuf);
|
||||
}
|
||||
}
|
||||
|
||||
return jettyContainer;
|
||||
|
|
|
@ -110,6 +110,7 @@ public class WebSocketUpgradeFilter implements Filter, MappedWebSocketCreator, D
|
|||
}
|
||||
|
||||
private NativeWebSocketConfiguration configuration;
|
||||
private String instanceKey;
|
||||
private boolean localConfiguration = false;
|
||||
private boolean alreadySetToAttribute = false;
|
||||
|
||||
|
@ -343,15 +344,15 @@ public class WebSocketUpgradeFilter implements Filter, MappedWebSocketCreator, D
|
|||
getFactory().getPolicy().setInputBufferSize(Integer.parseInt(max));
|
||||
}
|
||||
|
||||
String key = config.getInitParameter(CONTEXT_ATTRIBUTE_KEY);
|
||||
if (key == null)
|
||||
instanceKey = config.getInitParameter(CONTEXT_ATTRIBUTE_KEY);
|
||||
if (instanceKey == null)
|
||||
{
|
||||
// assume default
|
||||
key = WebSocketUpgradeFilter.class.getName();
|
||||
instanceKey = WebSocketUpgradeFilter.class.getName();
|
||||
}
|
||||
|
||||
// Set instance of this filter to context attribute
|
||||
setToAttribute(config.getServletContext(), key);
|
||||
setToAttribute(config.getServletContext(), instanceKey);
|
||||
}
|
||||
catch (ServletException e)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.BlockheadClient;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SubProtocolTest
|
||||
{
|
||||
@WebSocket
|
||||
public static class ProtocolEchoSocket
|
||||
{
|
||||
private Session session;
|
||||
private String acceptedProtocol;
|
||||
|
||||
@OnWebSocketConnect
|
||||
public void onConnect(Session session)
|
||||
{
|
||||
this.session = session;
|
||||
this.acceptedProtocol = session.getUpgradeResponse().getAcceptedSubProtocol();
|
||||
}
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onMsg(String msg)
|
||||
{
|
||||
session.getRemote().sendStringByFuture("acceptedSubprotocol=" + acceptedProtocol);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ProtocolCreator implements WebSocketCreator
|
||||
{
|
||||
@Override
|
||||
public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
|
||||
{
|
||||
// Accept first sub-protocol
|
||||
if (req.getSubProtocols() != null)
|
||||
{
|
||||
if (!req.getSubProtocols().isEmpty())
|
||||
{
|
||||
String subProtocol = req.getSubProtocols().get(0);
|
||||
resp.setAcceptedSubProtocol(subProtocol);
|
||||
}
|
||||
}
|
||||
|
||||
return new ProtocolEchoSocket();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ProtocolServlet extends WebSocketServlet
|
||||
{
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory)
|
||||
{
|
||||
factory.setCreator(new ProtocolCreator());
|
||||
}
|
||||
}
|
||||
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new ProtocolServlet());
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleProtocol() throws Exception
|
||||
{
|
||||
testSubProtocol("echo", "echo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleProtocols() throws Exception
|
||||
{
|
||||
testSubProtocol("chat,info,echo", "chat");
|
||||
}
|
||||
|
||||
private void testSubProtocol(String requestProtocols, String acceptedSubProtocols) throws Exception
|
||||
{
|
||||
try (BlockheadClient client = new BlockheadClient(server.getServerUri()))
|
||||
{
|
||||
client.setTimeout(1, TimeUnit.SECONDS);
|
||||
|
||||
client.connect();
|
||||
client.addHeader("Sec-WebSocket-Protocol: "+ requestProtocols + "\r\n");
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
client.write(new TextFrame().setPayload("showme"));
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1, 30, TimeUnit.SECONDS);
|
||||
WebSocketFrame tf = frames.poll();
|
||||
|
||||
assertThat(ProtocolEchoSocket.class.getSimpleName() + ".onMessage()", tf.getPayloadAsUTF8(), is("acceptedSubprotocol=" + acceptedSubProtocols));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -108,6 +108,8 @@ public class BrowserDebugTool implements WebSocketCreator
|
|||
|
||||
LOG.debug("User-Agent: {}",ua);
|
||||
LOG.debug("Sec-WebSocket-Extensions (Request) : {}",rexts);
|
||||
LOG.debug("Sec-WebSocket-Protocol (Request): {}",req.getHeader("Sec-WebSocket-Protocol"));
|
||||
LOG.debug("Sec-WebSocket-Protocol (Response): {}",resp.getAcceptedSubProtocol());
|
||||
|
||||
req.getExtensions();
|
||||
return new BrowserSocket(ua,rexts);
|
||||
|
|
|
@ -111,8 +111,9 @@ public class ServletUpgradeResponse implements UpgradeResponse
|
|||
{
|
||||
String name = entry.getKey();
|
||||
Collection<String> prepend = entry.getValue();
|
||||
List<String> values = headers.getOrDefault(name,headers.containsKey(name)?null:new ArrayList<>());
|
||||
List<String> values = headers.getOrDefault(name,new ArrayList<>());
|
||||
values.addAll(0,prepend);
|
||||
headers.put(name, values);
|
||||
}
|
||||
|
||||
status = response.getStatus();
|
||||
|
|
|
@ -25,6 +25,8 @@ import static org.junit.Assert.assertNull;
|
|||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletContext;
|
||||
|
@ -46,6 +48,7 @@ import org.junit.After;
|
|||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
|
@ -59,8 +62,16 @@ import org.junit.Test;
|
|||
public abstract class AbstractCreateAndInvalidateTest extends AbstractTestBase
|
||||
{
|
||||
|
||||
protected TestServlet _servlet = new TestServlet();
|
||||
protected TestServlet _servlet;
|
||||
protected AbstractTestServer _server1 = null;
|
||||
protected CountDownLatch _synchronizer;
|
||||
|
||||
@Before
|
||||
public void setUp ()
|
||||
{
|
||||
_synchronizer = new CountDownLatch(1);
|
||||
_servlet = new TestServlet(_synchronizer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -156,6 +167,9 @@ public abstract class AbstractCreateAndInvalidateTest extends AbstractTestBase
|
|||
ContentResponse response = client.GET(url+"?action=forward");
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
|
||||
//ensure work has finished on the server side
|
||||
_synchronizer.await();
|
||||
|
||||
//check that the sessions exist persisted
|
||||
checkSession(_servlet._id, true);
|
||||
checkSessionByKey (_servlet._id, "0_0_0_0:", true);
|
||||
|
@ -204,6 +218,8 @@ public abstract class AbstractCreateAndInvalidateTest extends AbstractTestBase
|
|||
ContentResponse response = client.GET(url+"?action=forwardinv");
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
|
||||
//wait for the request to have finished before checking session
|
||||
_synchronizer.await(10, TimeUnit.SECONDS);
|
||||
|
||||
//check that the session does not exist
|
||||
checkSession(_servlet._id, false);
|
||||
|
@ -220,6 +236,12 @@ public abstract class AbstractCreateAndInvalidateTest extends AbstractTestBase
|
|||
public static class TestServlet extends HttpServlet
|
||||
{
|
||||
public String _id = null;
|
||||
public CountDownLatch _synchronizer;
|
||||
|
||||
public TestServlet (CountDownLatch latch)
|
||||
{
|
||||
_synchronizer = latch;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
|
@ -240,6 +262,8 @@ public abstract class AbstractCreateAndInvalidateTest extends AbstractTestBase
|
|||
if (action.endsWith("inv"))
|
||||
session.invalidate();
|
||||
|
||||
_synchronizer.countDown();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -247,6 +271,9 @@ public abstract class AbstractCreateAndInvalidateTest extends AbstractTestBase
|
|||
_id = session.getId();
|
||||
session.setAttribute("value", new Integer(1));
|
||||
session.invalidate();
|
||||
assertNull(request.getSession(false));
|
||||
assertNotNull(session);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,10 +283,11 @@ public abstract class AbstractCreateAndInvalidateTest extends AbstractTestBase
|
|||
protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException
|
||||
{
|
||||
HttpSession session = request.getSession(false);
|
||||
assertNull(session);
|
||||
if (session == null) session = request.getSession(true);
|
||||
|
||||
// Be sure nothing from contextA is present
|
||||
Object objectA = session.getAttribute("A");
|
||||
Object objectA = session.getAttribute("value");
|
||||
assertTrue(objectA == null);
|
||||
|
||||
// Add something, so in contextA we can check if it is visible (it must not).
|
||||
|
|
Loading…
Reference in New Issue