Merge remote-tracking branch 'origin/jetty-9.4.x'
This commit is contained in:
commit
1b7ebf5599
174
VERSION.txt
174
VERSION.txt
|
@ -1,5 +1,101 @@
|
|||
jetty-10.0.0-SNAPSHOT
|
||||
|
||||
jetty-9.4.7.v20170914 - 14 September 2017
|
||||
+ 215 Consider native ALPN/SSL provider
|
||||
+ 487 JDK 9 build compatibility
|
||||
+ 1116 Support empty HTTP header values
|
||||
+ 1200 Use PathWatcher in DeploymentManager
|
||||
+ 1357 RolloverFileOutputStream: No rollout performed at midnight
|
||||
+ 1416 GzipHandler generated ETag suffix has problems with If-Match header
|
||||
logic
|
||||
+ 1468 Configure PKIX Revocation Checker for SslContextFactory
|
||||
+ 1469 RolloverFileOutputStream: IllegalStateException Task already scheduled
|
||||
+ 1498 Add JRTResource to support future Java 9 classloader behaviors
|
||||
+ 1499 ClasspathPattern needs MODULE ruleset to support future Java 9
|
||||
classloader behaviors
|
||||
+ 1503 IPv6 address needs normalization (without brackets) in
|
||||
ForwardedRequestCustomizer
|
||||
+ 1507 RolloverFileOutputStream: Negative delay Timer.schedule exception
|
||||
+ 1513 RolloverFileOutputStream: can't handle multiple instances
|
||||
+ 1515 Improved RollOverFileOutputStream removeOldFiles() behavior
|
||||
+ 1520 PropertyUserStore should extract packed config file
|
||||
+ 1556 Remove a timing channel in Password matching
|
||||
+ 1571 Support Hazelcast session management in 9.4
|
||||
+ 1590 Improve RolloverFileOutputStream functionality with multiple TimeZones
|
||||
+ 1591 JDBCSessionDataStore doesn't work with root context on Oracle DB
|
||||
+ 1592 CompressedContentFormat.tagEquals() - incorrect comparison of entity
|
||||
tag hashes
|
||||
+ 1595 HTTP/2: Avoid sending unnecessary stream WINDOW_UPDATE frames
|
||||
+ 1600 Update jndi.mod and plus.mod
|
||||
+ 1603 WebSocketServerFactory NPE in toString()
|
||||
+ 1604 WebSocketContainer stop needs improvement
|
||||
+ 1605 ContainerProvider.getWebSocketContainer() behavior is not to spec
|
||||
+ 1618 AsyncContext.dispatch() does not use raw/encoded URI
|
||||
+ 1622 HeaderFilter doesn't work if the response has been committed
|
||||
+ 1623 JettyRunMojo use dependencies from reactor (outputdirectory)
|
||||
+ 1625 Support new IANA declared Websocket Close Status Codes
|
||||
+ 1637 Thread per connection retained in HTTP/2
|
||||
+ 1638 Add it test for Maven Plugin
|
||||
+ 1642 Using RewriteHandler with AsyncContext.dispatch() and
|
||||
HttpServletRequestWrapper not possible
|
||||
+ 1643 ProxyServlet always uses default number of selector threads -
|
||||
constructor should allow to overwrite the default.
|
||||
+ 1645 NotSerializableException: DoSFilter when using Non-Clustered Session
|
||||
Management: File System
|
||||
+ 1655 Improve extensibility of ServerConnector
|
||||
+ 1656 Improve configurability of ConnectionPools
|
||||
+ 1661 AbstractProxyServlet onProxyResponseFailure Error
|
||||
+ 1662 NPE with WebSocket Compress Extensions
|
||||
+ 1664 IPAccessHandler CIDR IP range check is incorrect
|
||||
+ 1671 Asymmetric usage of trailers in MetaData.Request
|
||||
+ 1675 Session id should not be logged with INFO level in AbstractSessionCache
|
||||
+ 1679 DeploymentManagerMBean not usable through JMX
|
||||
+ 1682 Jetty-WarFragmentFolderPath directive has no effect in eclipse runtime
|
||||
mode except for the first launch
|
||||
+ 1685 Update ALPN support for Java 8u141
|
||||
+ 1687 HTTP2: Correcting missing callback notification when channel not found
|
||||
+ 1692 Annotation scanning should ignore `module-info.class` files
|
||||
+ 1698 Missing WWW-Authenticate from SpnegoAuthenticator when other
|
||||
Authorization header provided
|
||||
+ 1702 Update ALPN support for Java 8u144
|
||||
+ 1703 Improve HttpInput failure logging
|
||||
+ 1706 Log Implementation ignored when executing under OSGi
|
||||
+ 1709 SpnegoAuthenticator improperly handling case-insensitive Negotiate
|
||||
header
|
||||
+ 1713 Do not over allocate selectors for small thread pools
|
||||
+ 1715 Standardise properties and ids in jetty XML files
|
||||
+ 1717 DoSFilter getRateTracker IP/Port loadId minor improvement
|
||||
+ 1718 QueuedThreadPool not exposed on JMX
|
||||
+ 1719 HTTP/2: Improve handling of queued requests
|
||||
+ 1721 Async I/O POST fails with big files
|
||||
+ 1724 Add dependency on jetty-annotations for apache-jsp
|
||||
+ 1732 Allow pause accepting new connections during high load
|
||||
+ 1737 DefaultServlet wrong welcome dispatcher using non-root URL path
|
||||
+ 1738 jetty-bom fails oss.sonatype.org validation
|
||||
+ 1741 Java 9 javadoc failure in build
|
||||
+ 1749 Dump HttpDestination exchange queue
|
||||
+ 1750 PoolingHttpDestination creates ConnectionPool twice
|
||||
+ 1759 HTTP/2: producer can block in onReset
|
||||
+ 1766 JettyClientContainerProvider does not actually use common objects
|
||||
correctly
|
||||
+ 1789 PropertyUserStoreTest failures in Windows
|
||||
+ 1790 HTTP/2: 100% CPU usage seen during close/shutdown of endpoint
|
||||
+ 1792 Accept ISO-8859-1 characters in response reason
|
||||
+ 1794 Config properties typos in session-store-cache.mod
|
||||
+ 1795 Fix session id manager workerName
|
||||
+ 1796 ReservedThreadExecutor defaulting to capacity=1 only
|
||||
+ 1797 JEP 238 - Multi-Release JAR files break bytecode scanning
|
||||
+ 1798 JMXify EatWhatYouKill
|
||||
+ 1804 Make EndPoint creation and destroy a non-blocking task
|
||||
+ 1805 ReservedThreadExecutor should start ReservedThreads lazily
|
||||
+ 1809 NPE: StandardDescriptorProcessor.visitSecurityConstraint() with null/no
|
||||
security manager
|
||||
+ 1814 Move JavaVersion to jetty-util for future Java 9 support requirements
|
||||
+ 1816 HttpClientTest.testClientCannotValidateServerCertificate() hangs with
|
||||
JDK 9
|
||||
+ 475546 ClosedChannelException when connection to HTTPS over HTTP proxy
|
||||
with CONNECT
|
||||
|
||||
jetty-9.4.6.v20170531 - 31 May 2017
|
||||
+ 523 TLS close behaviour breaking session resumption
|
||||
+ 1108 Please improve logging in SslContextFactory when there are no approved
|
||||
|
@ -28,26 +124,59 @@ jetty-9.4.6.v20170531 - 31 May 2017
|
|||
+ 1569 Allow setting of maxBinaryMessageSize to 0 in WebSocketPolicy
|
||||
+ 1579 NPE in Quoted Quality CSV
|
||||
|
||||
jetty-9.2.22.v20170606 - 06 June 2017
|
||||
+ 920 no main manifest attribute, in jetty-runner-9.2.19.v20160908.jar
|
||||
+ 1108 Please improve logging in SslContextFactory when there are no approved
|
||||
cipher suites
|
||||
jetty-9.3.21.v20170918 - 18 September 2017
|
||||
+ 487 JDK 9 build compatibility
|
||||
+ 1116 Support empty HTTP header values
|
||||
+ 1357 RolloverFileOutputStream: No rollout performed at midnight
|
||||
+ 1469 IllegalStateException in RolloverFileOutputStream
|
||||
+ 1507 Negative delay Timer.schedule exception due to mismatched local and
|
||||
_logTimeZone values
|
||||
+ 1532 RolloverFileOutputStream can't handle multiple instances
|
||||
+ 1523 Update ALPN support for Java 8u131
|
||||
+ 1556 A timing channel in Password.java
|
||||
+ 1590 RolloverFileOutputStream not functioning in Jetty 9.2.21+
|
||||
+ 1469 RolloverFileOutputStream: IllegalStateException Task already scheduled
|
||||
+ 1507 RolloverFileOutputStream: Negative delay Timer.schedule exception
|
||||
+ 1513 RolloverFileOutputStream: can't handle multiple instances
|
||||
+ 1515 Improved RollOverFileOutputStream removeOldFiles() behavior
|
||||
+ 1556 Remove a timing channel in Password matching
|
||||
+ 1590 Improve RolloverFileOutputStream functionality with multiple TimeZones
|
||||
+ 1655 Improve extensibility of ServerConnector
|
||||
+ 1661 AbstractProxyServlet onProxyResponseFailure Error
|
||||
+ 1664 IPAccessHandler CIDR IP range check is incorrect
|
||||
+ 1685 Update ALPN support for Java 8u141
|
||||
+ 1687 HTTP2: Correcting missing callback notification when channel not found
|
||||
+ 1702 Update ALPN support for Java 8u144
|
||||
+ 1703 Improve HttpInput failure logging
|
||||
+ 1719 HTTP/2: Improve handling of queued requests
|
||||
+ 1741 Java 9 javadoc failure in build
|
||||
+ 1749 Dump HttpDestination exchange queue
|
||||
+ 1750 PoolingHttpDestination creates ConnectionPool twice
|
||||
+ 1759 HTTP/2: producer can block in onReset
|
||||
+ 1790 HTTP/2: 100% CPU usage seen during close/shutdown of endpoint
|
||||
+ 475546 ClosedChannelException when connection to HTTPS over HTTP proxy
|
||||
with CONNECT
|
||||
|
||||
jetty-9.3.20.v20170531 - 31 May 2017
|
||||
jetty-9.4.6.v20170531 - 31 May 2017
|
||||
+ 523 TLS close behaviour breaking session resumption
|
||||
+ 1108 Please improve logging in SslContextFactory when there are no approved
|
||||
cipher suites
|
||||
+ 1505 Adding jetty.base.uri and jetty.home.uri
|
||||
+ 1514 websocket dump badly formatted
|
||||
+ 1516 Delay starting of WebSocketClient until an attempt to connect is made
|
||||
+ 1520 PropertyUserStore should extract packed config file
|
||||
+ 1526 MongoSessionDataStore old session scavenging is broken due to the
|
||||
missing $ sign in "and" operation
|
||||
+ 1527 Jetty BOM should not depend on jetty-parent
|
||||
+ 1528 Internal HttpClient usages should have common configurable technique
|
||||
+ 1536 Jetty BOM should include more artifacts
|
||||
+ 1538 NPE in Response.putHeaders
|
||||
+ 1539 JarFileResource mishandles paths with spaces
|
||||
+ 1544 Disabling JSR-356 doesn't indicate context it was disabled for
|
||||
+ 1546 Improve handling of quotes in cookies
|
||||
+ 1553 X509.isCertSign() can throw ArrayIndexOutOfBoundsException on
|
||||
non-standard implementations
|
||||
+ 1556 A timing channel in Password.java
|
||||
+ 1558 When creating WebAppContext without session-config and with NO_SESSIONS
|
||||
throws NPE
|
||||
+ 1567 XmlConfiguration will start the same object multiple times
|
||||
+ 1568 ServletUpgradeRequest mangles query strings containing percent-escapes
|
||||
by re-escaping them
|
||||
+ 1569 Allow setting of maxBinaryMessageSize to 0 in WebSocketPolicy
|
||||
+ 1579 NPE in Quoted Quality CSV
|
||||
|
||||
jetty-9.4.5.v20170502 - 02 May 2017
|
||||
+ 304 Review dead code - StringUtil.sidBytesToString
|
||||
|
@ -81,6 +210,27 @@ jetty-9.4.5.v20170502 - 02 May 2017
|
|||
+ 1521 Prevent copy of jetty jars to lib/gcloud
|
||||
+ 1523 Update ALPN support for Java 8u131
|
||||
|
||||
jetty-9.3.20.v20170531 - 31 May 2017
|
||||
+ 523 TLS close behaviour breaking session resumption
|
||||
+ 1108 Please improve logging in SslContextFactory when there are no approved
|
||||
cipher suites
|
||||
+ 1527 Jetty BOM should not depend on jetty-parent
|
||||
+ 1556 A timing channel in Password.java
|
||||
+ 1567 XmlConfiguration will start the same object multiple times
|
||||
|
||||
jetty-9.2.22.v20170606 - 06 June 2017
|
||||
+ 920 no main manifest attribute, in jetty-runner-9.2.19.v20160908.jar
|
||||
+ 1108 Please improve logging in SslContextFactory when there are no approved
|
||||
cipher suites
|
||||
+ 1357 RolloverFileOutputStream: No rollout performed at midnight
|
||||
+ 1469 IllegalStateException in RolloverFileOutputStream
|
||||
+ 1507 Negative delay Timer.schedule exception due to mismatched local and
|
||||
_logTimeZone values
|
||||
+ 1532 RolloverFileOutputStream can't handle multiple instances
|
||||
+ 1523 Update ALPN support for Java 8u131
|
||||
+ 1556 A timing channel in Password.java
|
||||
+ 1590 RolloverFileOutputStream not functioning in Jetty 9.2.21+
|
||||
|
||||
jetty-9.3.19.v20170502 - 02 May 2017
|
||||
+ 877 Programmatic servlet mappings cannot override mappings from
|
||||
webdefault.xml using quickstart
|
||||
|
|
|
@ -18,11 +18,6 @@
|
|||
|
||||
package org.eclipse.jetty.jstl;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -37,15 +32,22 @@ import org.eclipse.jetty.server.Server;
|
|||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.toolchain.test.FS;
|
||||
import org.eclipse.jetty.toolchain.test.JAR;
|
||||
import org.eclipse.jetty.toolchain.test.JDK;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.webapp.Configuration;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assume;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class JstlTest
|
||||
{
|
||||
private static Server server;
|
||||
|
@ -54,6 +56,8 @@ public class JstlTest
|
|||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
Assume.assumeFalse(JDK.IS_9);
|
||||
|
||||
// Setup Server
|
||||
server = new Server();
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
|
@ -98,7 +102,8 @@ public class JstlTest
|
|||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
if (server != null)
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -15,13 +15,6 @@
|
|||
<bundle-symbolic-name>${project.groupId}.embedded</bundle-symbolic-name>
|
||||
</properties>
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>18.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-util-ajax</artifactId>
|
||||
|
@ -82,6 +75,16 @@
|
|||
<artifactId>jetty-alpn-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-openjdk8-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-conscrypt-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-annotations</artifactId>
|
||||
|
@ -138,4 +141,20 @@
|
|||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>jdk9</id>
|
||||
<activation>
|
||||
<jdk>[1.9,)</jdk>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-java-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
|
|
@ -19,8 +19,10 @@
|
|||
|
||||
package org.eclipse.jetty.embedded;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
|
||||
|
@ -64,7 +66,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|||
public class Http2Server
|
||||
{
|
||||
public static void main(String... args) throws Exception
|
||||
{
|
||||
{
|
||||
Server server = new Server();
|
||||
|
||||
MBeanContainer mbContainer = new MBeanContainer(
|
||||
|
@ -72,7 +74,10 @@ public class Http2Server
|
|||
server.addBean(mbContainer);
|
||||
|
||||
ServletContextHandler context = new ServletContextHandler(server, "/",ServletContextHandler.SESSIONS);
|
||||
context.setResourceBase("src/main/resources/docroot");
|
||||
String docroot = "src/main/resources/docroot";
|
||||
if (!new File(docroot).exists())
|
||||
docroot = "examples/embedded/src/main/resources/docroot";
|
||||
context.setResourceBase(docroot);
|
||||
context.addFilter(PushCacheFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
// context.addFilter(PushSessionCacheFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
context.addFilter(PushedTilesFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
@ -94,11 +99,14 @@ public class Http2Server
|
|||
|
||||
// SSL Context Factory for HTTPS and HTTP/2
|
||||
String jetty_distro = System.getProperty("jetty.distro","../../jetty-distribution/target/distribution");
|
||||
if (!new File(jetty_distro).exists())
|
||||
jetty_distro = "jetty-distribution/target/distribution";
|
||||
SslContextFactory sslContextFactory = new SslContextFactory();
|
||||
sslContextFactory.setKeyStorePath(jetty_distro + "/demo-base/etc/keystore");
|
||||
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
|
||||
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
|
||||
sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
|
||||
// sslContextFactory.setProvider("Conscrypt");
|
||||
|
||||
// HTTPS Configuration
|
||||
HttpConfiguration https_config = new HttpConfiguration(http_config);
|
||||
|
@ -122,7 +130,6 @@ public class Http2Server
|
|||
ALPN.debug=false;
|
||||
|
||||
server.start();
|
||||
//server.dumpStdErr();
|
||||
server.join();
|
||||
}
|
||||
|
||||
|
|
|
@ -20,12 +20,8 @@ package org.eclipse.jetty.embedded;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.Provider;
|
||||
import java.security.Security;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
|
@ -43,7 +39,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|||
public class ManyConnectors
|
||||
{
|
||||
public static void main( String[] args ) throws Exception
|
||||
{
|
||||
{
|
||||
// Since this example shows off SSL configuration, we need a keystore
|
||||
// with the appropriate key. These lookup of jetty.home is purely a hack
|
||||
// to get access to a keystore that we use in many unit tests and should
|
||||
|
@ -97,7 +93,6 @@ public class ManyConnectors
|
|||
sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath());
|
||||
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
|
||||
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
|
||||
sslContextFactory.setProvider("Conscrypt");
|
||||
|
||||
// HTTPS Configuration
|
||||
// A new HttpConfiguration object is needed for the next connector and
|
||||
|
@ -137,18 +132,6 @@ public class ManyConnectors
|
|||
// Start the server
|
||||
server.start();
|
||||
|
||||
SSLEngine engine = sslContextFactory.newSSLEngine();
|
||||
System.err.println(engine);
|
||||
System.err.println(engine.getClass());
|
||||
for (Method m : engine.getClass().getMethods())
|
||||
{
|
||||
System.err.println(m);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
server.join();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,3 +8,4 @@
|
|||
#org.eclipse.jetty.io.ssl.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.server.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.servlets.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.alpn.LEVEL=DEBUG
|
||||
|
|
|
@ -50,12 +50,6 @@
|
|||
<artifactId>jetty-io</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.alpn</groupId>
|
||||
<artifactId>alpn-api</artifactId>
|
||||
<version>${alpn.api.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||
<artifactId>jetty-test-helper</artifactId>
|
||||
|
|
|
@ -24,14 +24,13 @@ import java.util.concurrent.Executor;
|
|||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import org.eclipse.jetty.alpn.ALPN;
|
||||
import org.eclipse.jetty.io.ClientConnectionFactory;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.io.NegotiatingClientConnection;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public class ALPNClientConnection extends NegotiatingClientConnection implements ALPN.ClientProvider
|
||||
public class ALPNClientConnection extends NegotiatingClientConnection
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(ALPNClientConnection.class);
|
||||
|
||||
|
@ -41,41 +40,18 @@ public class ALPNClientConnection extends NegotiatingClientConnection implements
|
|||
{
|
||||
super(endPoint, executor, sslEngine, connectionFactory, context);
|
||||
this.protocols = protocols;
|
||||
ALPN.put(sslEngine, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsupported()
|
||||
{
|
||||
ALPN.remove(getSSLEngine());
|
||||
completed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> protocols()
|
||||
public List<String> getProtocols()
|
||||
{
|
||||
return protocols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selected(String protocol)
|
||||
{
|
||||
if (protocols.contains(protocol))
|
||||
{
|
||||
ALPN.remove(getSSLEngine());
|
||||
completed();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.info("Could not negotiate protocol: server [{}] - client {}", protocol, protocols);
|
||||
if (protocol==null || !protocols.contains(protocol))
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
ALPN.remove(getSSLEngine());
|
||||
super.close();
|
||||
else
|
||||
super.completed();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.eclipse.jetty.alpn.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -31,17 +32,19 @@ import org.eclipse.jetty.io.ClientConnectionFactory;
|
|||
import org.eclipse.jetty.io.Connection;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.io.NegotiatingClientConnectionFactory;
|
||||
import org.eclipse.jetty.io.ssl.ALPNProcessor;
|
||||
import org.eclipse.jetty.io.ssl.ALPNProcessor.Client;
|
||||
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
|
||||
import org.eclipse.jetty.io.ssl.SslHandshakeListener;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFactory implements SslHandshakeListener
|
||||
{
|
||||
private final SslHandshakeListener alpnListener = new ALPNListener();
|
||||
private static final Logger LOG = Log.getLogger(ALPNClientConnectionFactory.class);
|
||||
|
||||
private final List<Client> processors = new ArrayList<>();
|
||||
private final Executor executor;
|
||||
private final List<String> protocols;
|
||||
private final ALPNProcessor.Client alpnProcessor;
|
||||
|
||||
public ALPNClientConnectionFactory(Executor executor, ClientConnectionFactory connectionFactory, List<String> protocols)
|
||||
{
|
||||
|
@ -50,39 +53,64 @@ public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFact
|
|||
throw new IllegalArgumentException("ALPN protocol list cannot be empty");
|
||||
this.executor = executor;
|
||||
this.protocols = protocols;
|
||||
Iterator<ALPNProcessor.Client> processors = ServiceLoader.load(ALPNProcessor.Client.class).iterator();
|
||||
alpnProcessor = processors.hasNext() ? processors.next() : ALPNProcessor.Client.NOOP;
|
||||
}
|
||||
|
||||
public ALPNProcessor.Client getALPNProcessor()
|
||||
{
|
||||
return alpnProcessor;
|
||||
IllegalStateException failure = new IllegalStateException("No Client ALPNProcessors!");
|
||||
|
||||
// Use a for loop on iterator so load exceptions can be caught and ignored
|
||||
for (Iterator<Client> i = ServiceLoader.load(Client.class).iterator(); i.hasNext();)
|
||||
{
|
||||
Client processor;
|
||||
try
|
||||
{
|
||||
processor = i.next();
|
||||
}
|
||||
catch(Throwable x)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug(x);
|
||||
failure.addSuppressed(x);
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
processor.init();
|
||||
processors.add(processor);
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Could not initialize " + processor, x);
|
||||
failure.addSuppressed(x);
|
||||
}
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("protocols: {}", protocols);
|
||||
LOG.debug("processors: {}", processors);
|
||||
}
|
||||
|
||||
if (processors.isEmpty())
|
||||
throw failure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
|
||||
{
|
||||
SSLEngine sslEngine = (SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY);
|
||||
getALPNProcessor().configure(sslEngine, protocols);
|
||||
ContainerLifeCycle connector = (ContainerLifeCycle)context.get(ClientConnectionFactory.CONNECTOR_CONTEXT_KEY);
|
||||
// Method addBean() has set semantic, so the listener is added only once.
|
||||
connector.addBean(alpnListener);
|
||||
ALPNClientConnection connection = new ALPNClientConnection(endPoint, executor, getClientConnectionFactory(),
|
||||
sslEngine, context, protocols);
|
||||
return customize(connection, context);
|
||||
}
|
||||
|
||||
private class ALPNListener implements SslHandshakeListener
|
||||
{
|
||||
@Override
|
||||
public void handshakeSucceeded(Event event)
|
||||
{
|
||||
getALPNProcessor().process(event.getSSLEngine());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handshakeFailed(Event event, Throwable failure)
|
||||
SSLEngine engine = (SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY);
|
||||
for (Client processor : processors)
|
||||
{
|
||||
if (processor.appliesTo(engine))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} for {} on {}", processor, engine, endPoint);
|
||||
ALPNClientConnection connection = new ALPNClientConnection(endPoint, executor, getClientConnectionFactory(),
|
||||
engine, context, protocols);
|
||||
processor.configure(engine, connection);
|
||||
return customize(connection, context);
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("No ALPNProcessor for " + engine);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-parent</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-alpn-conscrypt-client</artifactId>
|
||||
<name>Jetty :: ALPN :: Conscrypt Client Implementation</name>
|
||||
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.alpn.java.client</bundle-symbolic-name>
|
||||
<conscrypt.version>1.0.0.RC10</conscrypt.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.conscrypt</groupId>
|
||||
<artifactId>conscrypt-openjdk-uber</artifactId>
|
||||
<version>${conscrypt.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.http2</groupId>
|
||||
<artifactId>http2-client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,107 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.alpn.conscrypt.client;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Security;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import org.conscrypt.OpenSSLProvider;
|
||||
import org.eclipse.jetty.alpn.client.ALPNClientConnection;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
import org.eclipse.jetty.io.ssl.ALPNProcessor;
|
||||
import org.eclipse.jetty.io.ssl.SslConnection;
|
||||
import org.eclipse.jetty.io.ssl.SslHandshakeListener;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public class ConscryptClientALPNProcessor implements ALPNProcessor.Client
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(ConscryptClientALPNProcessor.class);
|
||||
|
||||
@Override
|
||||
public void init()
|
||||
{
|
||||
if (Security.getProvider("Conscrypt")==null)
|
||||
{
|
||||
Security.addProvider(new OpenSSLProvider());
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Added Conscrypt provider");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean appliesTo(SSLEngine sslEngine)
|
||||
{
|
||||
return sslEngine.getClass().getName().startsWith("org.conscrypt.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(SSLEngine sslEngine, Connection connection)
|
||||
{
|
||||
try
|
||||
{
|
||||
Method setAlpnProtocols = sslEngine.getClass().getDeclaredMethod("setAlpnProtocols", String[].class);
|
||||
setAlpnProtocols.setAccessible(true);
|
||||
ALPNClientConnection alpn = (ALPNClientConnection)connection;
|
||||
String[] protocols = alpn.getProtocols().toArray(new String[0]);
|
||||
setAlpnProtocols.invoke(sslEngine, (Object)protocols);
|
||||
((SslConnection.DecryptedEndPoint)connection.getEndPoint()).getSslConnection()
|
||||
.addHandshakeListener(new ALPNListener(alpn));
|
||||
}
|
||||
catch (RuntimeException x)
|
||||
{
|
||||
throw x;
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
|
||||
private final class ALPNListener implements SslHandshakeListener
|
||||
{
|
||||
private final ALPNClientConnection alpnConnection;
|
||||
|
||||
private ALPNListener(ALPNClientConnection connection)
|
||||
{
|
||||
alpnConnection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handshakeSucceeded(Event event)
|
||||
{
|
||||
try
|
||||
{
|
||||
SSLEngine sslEngine = alpnConnection.getSSLEngine();
|
||||
Method method = sslEngine.getClass().getDeclaredMethod("getAlpnSelectedProtocol");
|
||||
method.setAccessible(true);
|
||||
String protocol = new String((byte[])method.invoke(sslEngine), StandardCharsets.US_ASCII);
|
||||
alpnConnection.selected(protocol);
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
alpnConnection.selected(null);
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.eclipse.jetty.alpn.conscrypt.client.ConscryptClientALPNProcessor
|
|
@ -0,0 +1,89 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.alpn.java.client;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.Security;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.conscrypt.OpenSSLProvider;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http2.api.Session;
|
||||
import org.eclipse.jetty.http2.api.Stream;
|
||||
import org.eclipse.jetty.http2.client.HTTP2Client;
|
||||
import org.eclipse.jetty.http2.frames.DataFrame;
|
||||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.FuturePromise;
|
||||
import org.eclipse.jetty.util.Jetty;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
|
||||
public class ConscryptHTTP2Client
|
||||
{
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
Security.addProvider(new OpenSSLProvider());
|
||||
SslContextFactory sslContextFactory = new SslContextFactory();
|
||||
sslContextFactory.setProvider("Conscrypt");
|
||||
HTTP2Client client = new HTTP2Client();
|
||||
client.addBean(sslContextFactory);
|
||||
client.start();
|
||||
|
||||
String host = "webtide.com";
|
||||
int port = 443;
|
||||
|
||||
FuturePromise<Session> sessionPromise = new FuturePromise<>();
|
||||
client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener.Adapter(), sessionPromise);
|
||||
Session session = sessionPromise.get(5, TimeUnit.SECONDS);
|
||||
|
||||
HttpFields requestFields = new HttpFields();
|
||||
requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
|
||||
MetaData.Request metaData = new MetaData.Request("GET", new HttpURI("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
|
||||
HeadersFrame headersFrame = new HeadersFrame(metaData, null, true);
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
session.newStream(headersFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(Stream stream, HeadersFrame frame)
|
||||
{
|
||||
System.err.println(frame);
|
||||
if (frame.isEndStream())
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
System.err.println(frame);
|
||||
callback.succeeded();
|
||||
if (frame.isEndStream())
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
latch.await(5, TimeUnit.SECONDS);
|
||||
|
||||
client.stop();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
#org.eclipse.jetty.LEVEL=DEBUG
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-parent</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-alpn-conscrypt-server</artifactId>
|
||||
<name>Jetty :: ALPN :: Conscrypt Server Implementation</name>
|
||||
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.alpn.conscrypt.server</bundle-symbolic-name>
|
||||
<conscrypt.version>1.0.0.RC10</conscrypt.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.conscrypt</groupId>
|
||||
<artifactId>conscrypt-openjdk-uber</artifactId>
|
||||
<version>${conscrypt.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-io</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.http2</groupId>
|
||||
<artifactId>http2-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,115 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.alpn.conscrypt.server;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.Security;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import org.conscrypt.OpenSSLProvider;
|
||||
import org.eclipse.jetty.alpn.server.ALPNServerConnection;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
import org.eclipse.jetty.io.ssl.ALPNProcessor;
|
||||
import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint;
|
||||
import org.eclipse.jetty.io.ssl.SslHandshakeListener;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public class ConscryptServerALPNProcessor implements ALPNProcessor.Server
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(ConscryptServerALPNProcessor.class);
|
||||
|
||||
@Override
|
||||
public void init()
|
||||
{
|
||||
if (Security.getProvider("Conscrypt")==null)
|
||||
{
|
||||
Security.addProvider(new OpenSSLProvider());
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Added Conscrypt provider");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean appliesTo(SSLEngine sslEngine)
|
||||
{
|
||||
return sslEngine.getClass().getName().startsWith("org.conscrypt.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(SSLEngine sslEngine,Connection connection)
|
||||
{
|
||||
try
|
||||
{
|
||||
Method method = sslEngine.getClass().getMethod("setHandshakeApplicationProtocolSelector", BiFunction.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(sslEngine,new ALPNCallback((ALPNServerConnection)connection));
|
||||
}
|
||||
catch (RuntimeException x)
|
||||
{
|
||||
throw x;
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
|
||||
private final class ALPNCallback implements BiFunction<SSLEngine,List<String>,String>, SslHandshakeListener
|
||||
{
|
||||
private final ALPNServerConnection alpnConnection;
|
||||
|
||||
private ALPNCallback(ALPNServerConnection connection)
|
||||
{
|
||||
alpnConnection = connection;
|
||||
((DecryptedEndPoint)alpnConnection.getEndPoint()).getSslConnection().addHandshakeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(SSLEngine engine, List<String> protocols)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("apply {} {}", alpnConnection, protocols);
|
||||
alpnConnection.select(protocols);
|
||||
return alpnConnection.getProtocol();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handshakeSucceeded(Event event)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("handshakeSucceeded {} {}", alpnConnection, event);
|
||||
if (alpnConnection.getProtocol()==null)
|
||||
{
|
||||
LOG.warn("No ALPN callback! {} {}",alpnConnection, event);
|
||||
alpnConnection.unsupported();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handshakeFailed(Event event, Throwable failure)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("handshakeFailed {} {} {}", alpnConnection, event, failure);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.eclipse.jetty.alpn.conscrypt.server.ConscryptServerALPNProcessor
|
|
@ -0,0 +1,72 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.alpn.conscrypt.server;
|
||||
|
||||
import java.security.Security;
|
||||
|
||||
import org.conscrypt.OpenSSLProvider;
|
||||
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
|
||||
import org.eclipse.jetty.http2.HTTP2Cipher;
|
||||
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
|
||||
/**
|
||||
* Test server that verifies that the Conscrypt ALPN mechanism works.
|
||||
*/
|
||||
public class ConscryptHTTP2Server
|
||||
{
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
Security.addProvider(new OpenSSLProvider());
|
||||
|
||||
Server server = new Server();
|
||||
|
||||
HttpConfiguration httpsConfig = new HttpConfiguration();
|
||||
httpsConfig.setSecureScheme("https");
|
||||
httpsConfig.setSecurePort(8443);
|
||||
httpsConfig.setSendXPoweredBy(true);
|
||||
httpsConfig.setSendServerVersion(true);
|
||||
httpsConfig.addCustomizer(new SecureRequestCustomizer());
|
||||
|
||||
SslContextFactory sslContextFactory = new SslContextFactory();
|
||||
sslContextFactory.setProvider("Conscrypt");
|
||||
sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
|
||||
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
|
||||
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
|
||||
sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
|
||||
|
||||
HttpConnectionFactory http = new HttpConnectionFactory(httpsConfig);
|
||||
HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpsConfig);
|
||||
ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
|
||||
alpn.setDefaultProtocol(http.getProtocol());
|
||||
SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, alpn.getProtocol());
|
||||
|
||||
ServerConnector http2Connector = new ServerConnector(server, ssl, alpn, h2, http);
|
||||
http2Connector.setPort(8443);
|
||||
server.addConnector(http2Connector);
|
||||
|
||||
server.start();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
#org.eclipse.jetty.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.alpn.LEVEL=DEBUG
|
Binary file not shown.
|
@ -33,22 +33,15 @@
|
|||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-io</artifactId>
|
||||
<artifactId>jetty-alpn-client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.alpn</groupId>
|
||||
<artifactId>alpn-api</artifactId>
|
||||
<version>${alpn.api.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.http2</groupId>
|
||||
<artifactId>http2-client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -18,38 +18,66 @@
|
|||
|
||||
package org.eclipse.jetty.alpn.java.client;
|
||||
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
|
||||
import org.eclipse.jetty.alpn.ALPN;
|
||||
import org.eclipse.jetty.alpn.client.ALPNClientConnection;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
import org.eclipse.jetty.io.ssl.ALPNProcessor;
|
||||
import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint;
|
||||
import org.eclipse.jetty.io.ssl.SslHandshakeListener;
|
||||
import org.eclipse.jetty.util.JavaVersion;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public class JDK9ClientALPNProcessor implements ALPNProcessor.Client
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(JDK9ClientALPNProcessor.class);
|
||||
|
||||
@Override
|
||||
public void configure(SSLEngine sslEngine, List<String> protocols)
|
||||
public void init()
|
||||
{
|
||||
SSLParameters sslParameters = sslEngine.getSSLParameters();
|
||||
sslParameters.setApplicationProtocols(protocols.toArray(new String[0]));
|
||||
sslEngine.setSSLParameters(sslParameters);
|
||||
if (JavaVersion.VERSION.getPlatform()<9)
|
||||
throw new IllegalStateException(this + " not applicable for java "+JavaVersion.VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(SSLEngine sslEngine)
|
||||
public boolean appliesTo(SSLEngine sslEngine)
|
||||
{
|
||||
try
|
||||
Module module = sslEngine.getClass().getModule();
|
||||
return module!=null && "java.base".equals(module.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(SSLEngine sslEngine, Connection connection)
|
||||
{
|
||||
ALPNClientConnection alpn = (ALPNClientConnection)connection;
|
||||
SSLParameters sslParameters = sslEngine.getSSLParameters();
|
||||
List<String> protocols = alpn.getProtocols();
|
||||
sslParameters.setApplicationProtocols(protocols.toArray(new String[protocols.size()]));
|
||||
sslEngine.setSSLParameters(sslParameters);
|
||||
((DecryptedEndPoint)connection.getEndPoint()).getSslConnection()
|
||||
.addHandshakeListener(new ALPNListener(alpn));
|
||||
}
|
||||
|
||||
private final class ALPNListener implements SslHandshakeListener
|
||||
{
|
||||
private final ALPNClientConnection alpnConnection;
|
||||
|
||||
private ALPNListener(ALPNClientConnection connection)
|
||||
{
|
||||
ALPN.ClientProvider provider = (ALPN.ClientProvider)ALPN.get(sslEngine);
|
||||
if (provider != null)
|
||||
provider.selected(sslEngine.getApplicationProtocol());
|
||||
alpnConnection = connection;
|
||||
}
|
||||
catch (SSLException x)
|
||||
|
||||
@Override
|
||||
public void handshakeSucceeded(Event event)
|
||||
{
|
||||
throw new UncheckedIOException(x);
|
||||
String protocol = alpnConnection.getSSLEngine().getApplicationProtocol();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("selected protocol {}", protocol);
|
||||
alpnConnection.selected(protocol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,16 +42,16 @@ public class JDK9HTTP2Client
|
|||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
HTTP2Client client = new HTTP2Client();
|
||||
SslContextFactory sslContextFactory = new SslContextFactory(true);
|
||||
SslContextFactory sslContextFactory = new SslContextFactory();
|
||||
client.addBean(sslContextFactory);
|
||||
client.start();
|
||||
|
||||
String host = "localhost";
|
||||
int port = 8443;
|
||||
String host = "webtide.com";
|
||||
int port = 443;
|
||||
|
||||
FuturePromise<Session> sessionPromise = new FuturePromise<>();
|
||||
client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener.Adapter(), sessionPromise);
|
||||
Session session = sessionPromise.get(555, TimeUnit.SECONDS);
|
||||
Session session = sessionPromise.get(5, TimeUnit.SECONDS);
|
||||
|
||||
HttpFields requestFields = new HttpFields();
|
||||
requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
|
||||
|
|
|
@ -40,12 +40,10 @@
|
|||
<artifactId>alpn-api</artifactId>
|
||||
<version>${alpn.api.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
<artifactId>jetty-alpn-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.http2</groupId>
|
||||
|
@ -53,12 +51,6 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
|
|
|
@ -19,13 +19,16 @@
|
|||
package org.eclipse.jetty.alpn.java.server;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import org.eclipse.jetty.alpn.ALPN;
|
||||
import org.eclipse.jetty.alpn.server.ALPNServerConnection;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
import org.eclipse.jetty.io.ssl.ALPNProcessor;
|
||||
import org.eclipse.jetty.io.ssl.SslConnection;
|
||||
import org.eclipse.jetty.io.ssl.SslHandshakeListener;
|
||||
import org.eclipse.jetty.util.JavaVersion;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
@ -34,40 +37,61 @@ public class JDK9ServerALPNProcessor implements ALPNProcessor.Server, SslHandsha
|
|||
private static final Logger LOG = Log.getLogger(JDK9ServerALPNProcessor.class);
|
||||
|
||||
@Override
|
||||
public void configure(SSLEngine sslEngine)
|
||||
public void init()
|
||||
{
|
||||
sslEngine.setHandshakeApplicationProtocolSelector(this::process);
|
||||
}
|
||||
|
||||
private String process(SSLEngine sslEngine, List<String> protocols)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("ALPN selecting among client{}", protocols);
|
||||
ALPN.ServerProvider provider = (ALPN.ServerProvider)ALPN.remove(sslEngine);
|
||||
return provider == null ? "" : provider.select(protocols);
|
||||
}
|
||||
catch (SSLException x)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (JavaVersion.VERSION.getPlatform()<9)
|
||||
throw new IllegalStateException(this + " not applicable for java "+JavaVersion.VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handshakeSucceeded(Event event)
|
||||
public boolean appliesTo(SSLEngine sslEngine)
|
||||
{
|
||||
ALPN.ServerProvider provider = (ALPN.ServerProvider)ALPN.remove(event.getSSLEngine());
|
||||
if (provider != null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("ALPN unsupported by client");
|
||||
provider.unsupported();
|
||||
}
|
||||
Module module = sslEngine.getClass().getModule();
|
||||
return module!=null && "java.base".equals(module.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handshakeFailed(Event event, Throwable failure)
|
||||
public void configure(SSLEngine sslEngine, Connection connection)
|
||||
{
|
||||
sslEngine.setHandshakeApplicationProtocolSelector(new ALPNCallback((ALPNServerConnection)connection));
|
||||
}
|
||||
|
||||
private final class ALPNCallback implements BiFunction<SSLEngine,List<String>,String>, SslHandshakeListener
|
||||
{
|
||||
private final ALPNServerConnection alpnConnection;
|
||||
|
||||
private ALPNCallback(ALPNServerConnection connection)
|
||||
{
|
||||
alpnConnection = connection;
|
||||
((SslConnection.DecryptedEndPoint)alpnConnection.getEndPoint()).getSslConnection().addHandshakeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(SSLEngine engine, List<String> protocols)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("apply {} {}", alpnConnection, protocols);
|
||||
alpnConnection.select(protocols);
|
||||
return alpnConnection.getProtocol();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handshakeSucceeded(Event event)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("handshakeSucceeded {} {}", alpnConnection, event);
|
||||
if (alpnConnection.getProtocol()==null)
|
||||
{
|
||||
LOG.warn("No ALPN callback! {} {}",alpnConnection, event);
|
||||
alpnConnection.unsupported();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handshakeFailed(Event event, Throwable failure)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("handshakeFailed {} {} {}", alpnConnection, event, failure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-parent</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-alpn-openjdk8-client</artifactId>
|
||||
<name>Jetty :: ALPN :: OpenJDK8 Client Implementation</name>
|
||||
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.alpn.java.client</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.alpn</groupId>
|
||||
<artifactId>alpn-api</artifactId>
|
||||
<version>${alpn.api.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.http2</groupId>
|
||||
<artifactId>http2-client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,104 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.alpn.java.client;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import org.eclipse.jetty.alpn.ALPN;
|
||||
import org.eclipse.jetty.alpn.client.ALPNClientConnection;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
import org.eclipse.jetty.io.ssl.ALPNProcessor;
|
||||
import org.eclipse.jetty.util.JavaVersion;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public class OpenJDK8ClientALPNProcessor implements ALPNProcessor.Client
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(OpenJDK8ClientALPNProcessor.class);
|
||||
|
||||
@Override
|
||||
public void init()
|
||||
{
|
||||
if (JavaVersion.VERSION.getPlatform()!=8)
|
||||
throw new IllegalStateException(this + " not applicable for java "+JavaVersion.VERSION);
|
||||
if (ALPN.class.getClassLoader()!=null)
|
||||
throw new IllegalStateException(this + " must be on JVM boot classpath");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean appliesTo(SSLEngine sslEngine)
|
||||
{
|
||||
return sslEngine.getClass().getName().startsWith("sun.security.ssl.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(SSLEngine sslEngine, Connection connection)
|
||||
{
|
||||
connection.addListener(new ALPNListener((ALPNClientConnection)connection));
|
||||
}
|
||||
|
||||
private final class ALPNListener implements ALPN.ClientProvider, Connection.Listener
|
||||
{
|
||||
private final ALPNClientConnection alpnConnection;
|
||||
|
||||
private ALPNListener(ALPNClientConnection connection)
|
||||
{
|
||||
alpnConnection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpened(Connection connection)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("onOpened {}", alpnConnection);
|
||||
ALPN.put(alpnConnection.getSSLEngine(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosed(Connection connection)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("onClosed {}", alpnConnection);
|
||||
ALPN.remove(alpnConnection.getSSLEngine());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> protocols()
|
||||
{
|
||||
return alpnConnection.getProtocols();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsupported()
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("unsupported {}", alpnConnection);
|
||||
ALPN.remove(alpnConnection.getSSLEngine());
|
||||
alpnConnection.selected(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selected(String protocol)
|
||||
{
|
||||
alpnConnection.selected(protocol);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.eclipse.jetty.alpn.java.client.OpenJDK8ClientALPNProcessor
|
|
@ -0,0 +1,85 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.alpn.java.client;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http2.api.Session;
|
||||
import org.eclipse.jetty.http2.api.Stream;
|
||||
import org.eclipse.jetty.http2.client.HTTP2Client;
|
||||
import org.eclipse.jetty.http2.frames.DataFrame;
|
||||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.FuturePromise;
|
||||
import org.eclipse.jetty.util.Jetty;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
|
||||
public class OpenJDK8HTTP2Client
|
||||
{
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
HTTP2Client client = new HTTP2Client();
|
||||
SslContextFactory sslContextFactory = new SslContextFactory();
|
||||
client.addBean(sslContextFactory);
|
||||
client.start();
|
||||
|
||||
String host = "webtide.com";
|
||||
int port = 443;
|
||||
|
||||
FuturePromise<Session> sessionPromise = new FuturePromise<>();
|
||||
client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener.Adapter(), sessionPromise);
|
||||
Session session = sessionPromise.get(5, TimeUnit.SECONDS);
|
||||
|
||||
HttpFields requestFields = new HttpFields();
|
||||
requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
|
||||
MetaData.Request metaData = new MetaData.Request("GET", new HttpURI("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
|
||||
HeadersFrame headersFrame = new HeadersFrame(metaData, null, true);
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
session.newStream(headersFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(Stream stream, HeadersFrame frame)
|
||||
{
|
||||
System.err.println(frame);
|
||||
if (frame.isEndStream())
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
System.err.println(frame);
|
||||
callback.succeeded();
|
||||
if (frame.isEndStream())
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
latch.await(5, TimeUnit.SECONDS);
|
||||
|
||||
client.stop();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
#org.eclipse.jetty.LEVEL=DEBUG
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-parent</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-alpn-openjdk8-server</artifactId>
|
||||
<name>Jetty :: ALPN :: OpenJDK8 Server Implementation</name>
|
||||
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.alpn.conscrypt.server</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.alpn</groupId>
|
||||
<artifactId>alpn-api</artifactId>
|
||||
<version>${alpn.api.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-io</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.http2</groupId>
|
||||
<artifactId>http2-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,104 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.alpn.openjdk8.server;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import org.eclipse.jetty.alpn.ALPN;
|
||||
import org.eclipse.jetty.alpn.server.ALPNServerConnection;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
import org.eclipse.jetty.io.ssl.ALPNProcessor;
|
||||
import org.eclipse.jetty.util.JavaVersion;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public class OpenJDK8ServerALPNProcessor implements ALPNProcessor.Server
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(OpenJDK8ServerALPNProcessor.class);
|
||||
|
||||
@Override
|
||||
public void init()
|
||||
{
|
||||
if (JavaVersion.VERSION.getPlatform()!=8)
|
||||
throw new IllegalStateException(this + " not applicable for java "+JavaVersion.VERSION);
|
||||
if (ALPN.class.getClassLoader()!=null)
|
||||
throw new IllegalStateException(ALPN.class.getName() + " must be on JVM boot classpath");
|
||||
if (LOG.isDebugEnabled())
|
||||
ALPN.debug = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean appliesTo(SSLEngine sslEngine)
|
||||
{
|
||||
return sslEngine.getClass().getName().startsWith("sun.security.ssl.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(SSLEngine sslEngine, Connection connection)
|
||||
{
|
||||
connection.addListener(new ALPNListener((ALPNServerConnection)connection));
|
||||
}
|
||||
|
||||
private final class ALPNListener implements ALPN.ServerProvider, Connection.Listener
|
||||
{
|
||||
private final ALPNServerConnection alpnConnection;
|
||||
|
||||
private ALPNListener(ALPNServerConnection connection)
|
||||
{
|
||||
alpnConnection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpened(Connection connection)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("onOpened {}", alpnConnection);
|
||||
ALPN.put(alpnConnection.getSSLEngine(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosed(Connection connection)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("onClosed {}", alpnConnection);
|
||||
ALPN.remove(alpnConnection.getSSLEngine());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsupported()
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("unsupported {}", alpnConnection);
|
||||
alpnConnection.select(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String select(List<String> protocols) throws SSLException
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("select {} {}", alpnConnection, protocols);
|
||||
alpnConnection.select(protocols);
|
||||
return alpnConnection.getProtocol();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.eclipse.jetty.alpn.openjdk8.server.OpenJDK8ServerALPNProcessor
|
|
@ -0,0 +1,66 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.alpn.openjdk8.server;
|
||||
|
||||
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
|
||||
import org.eclipse.jetty.http2.HTTP2Cipher;
|
||||
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
|
||||
/**
|
||||
* Test server that verifies that the JDK 8 ALPN mechanism works.
|
||||
*/
|
||||
public class OpenJDK8HTTP2Server
|
||||
{
|
||||
public static void main(String... args) throws Exception
|
||||
{
|
||||
Server server = new Server();
|
||||
|
||||
HttpConfiguration httpsConfig = new HttpConfiguration();
|
||||
httpsConfig.setSecureScheme("https");
|
||||
httpsConfig.setSecurePort(8443);
|
||||
httpsConfig.setSendXPoweredBy(true);
|
||||
httpsConfig.setSendServerVersion(true);
|
||||
httpsConfig.addCustomizer(new SecureRequestCustomizer());
|
||||
|
||||
SslContextFactory sslContextFactory = new SslContextFactory();
|
||||
sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
|
||||
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
|
||||
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
|
||||
sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
|
||||
|
||||
HttpConnectionFactory http = new HttpConnectionFactory(httpsConfig);
|
||||
HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpsConfig);
|
||||
ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
|
||||
alpn.setDefaultProtocol(http.getProtocol());
|
||||
SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, alpn.getProtocol());
|
||||
|
||||
ServerConnector http2Connector = new ServerConnector(server, ssl, alpn, h2, http);
|
||||
http2Connector.setPort(8443);
|
||||
server.addConnector(http2Connector);
|
||||
|
||||
server.start();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
#org.eclipse.jetty.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.alpn.LEVEL=DEBUG
|
Binary file not shown.
|
@ -59,12 +59,6 @@
|
|||
<artifactId>jetty-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.alpn</groupId>
|
||||
<artifactId>alpn-api</artifactId>
|
||||
<version>${alpn.api.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||
<artifactId>jetty-test-helper</artifactId>
|
||||
|
|
|
@ -15,18 +15,9 @@
|
|||
<Call name="addConnectionFactory">
|
||||
<Arg>
|
||||
<New id="alpn" class="org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory">
|
||||
<Arg type="String">
|
||||
<Property name="jetty.alpn.protocols" default="" />
|
||||
</Arg>
|
||||
<Set name="defaultProtocol">
|
||||
<Property name="jetty.alpn.defaultProtocol" />
|
||||
</Set>
|
||||
<Arg name="protocols" type="String"><Property name="jetty.alpn.protocols" default="" /></Arg>
|
||||
<Set name="defaultProtocol"><Property name="jetty.alpn.defaultProtocol" /></Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
||||
<!-- ALPN debugging on System.err -->
|
||||
<Set class="org.eclipse.jetty.alpn.ALPN" name="debug" type="boolean"><Property name="jetty.alpn.debug" default="false" /></Set>
|
||||
|
||||
</Configure>
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
[description]
|
||||
Selects an ALPN (Application Layer Protocol Negotiation) implementation by java version.
|
||||
|
||||
[depend]
|
||||
alpn-impl/alpn-${java.version.platform}
|
|
@ -17,6 +17,9 @@ specific version of Java.
|
|||
[depend]
|
||||
alpn-impl/alpn-${java.version}
|
||||
|
||||
[lib]
|
||||
lib/jetty-alpn-openjdk8-server-${jetty.version}.jar
|
||||
|
||||
[files]
|
||||
lib/
|
||||
lib/alpn/
|
||||
|
|
|
@ -3,7 +3,7 @@ Enables the ALPN (Application Layer Protocol Negotiation) TLS extension.
|
|||
|
||||
[depend]
|
||||
ssl
|
||||
alpn-impl/alpn-${java.version.platform}
|
||||
alpn-impl
|
||||
|
||||
[lib]
|
||||
lib/jetty-alpn-client-${jetty.version}.jar
|
||||
|
@ -21,6 +21,3 @@ etc/jetty-alpn.xml
|
|||
## Specifies what protocol to use when negotiation fails.
|
||||
# jetty.alpn.defaultProtocol=http/1.1
|
||||
|
||||
## ALPN debug logging on System.err
|
||||
# jetty.alpn.debug=false
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ import java.util.List;
|
|||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
import org.eclipse.jetty.alpn.ALPN;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.server.ConnectionFactory;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
|
@ -32,24 +31,21 @@ import org.eclipse.jetty.server.NegotiatingServerConnection;
|
|||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public class ALPNServerConnection extends NegotiatingServerConnection implements ALPN.ServerProvider
|
||||
public class ALPNServerConnection extends NegotiatingServerConnection
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(ALPNServerConnection.class);
|
||||
|
||||
public ALPNServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List<String> protocols, String defaultProtocol)
|
||||
{
|
||||
super(connector, endPoint, engine, protocols, defaultProtocol);
|
||||
ALPN.put(engine, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsupported()
|
||||
{
|
||||
select(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String select(List<String> clientProtocols)
|
||||
|
||||
public void select(List<String> clientProtocols)
|
||||
{
|
||||
SSLEngine sslEngine = getSSLEngine();
|
||||
List<String> serverProtocols = getProtocols();
|
||||
|
@ -70,7 +66,7 @@ public class ALPNServerConnection extends NegotiatingServerConnection implements
|
|||
if (factory instanceof CipherDiscriminator && !((CipherDiscriminator)factory).isAcceptable(serverProtocol, tlsProtocol, tlsCipher))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} protocol {} not acceptable to {} for {}/{}", this, serverProtocol, factory, tlsProtocol, tlsCipher);
|
||||
LOG.debug("Protocol {} not acceptable to {} for {}/{} on {}", serverProtocol, factory, tlsProtocol, tlsCipher, getEndPoint());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -87,21 +83,12 @@ public class ALPNServerConnection extends NegotiatingServerConnection implements
|
|||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} could not negotiate protocol among client{} and server{}", this, clientProtocols, serverProtocols);
|
||||
LOG.debug("Could not negotiate protocol from client{} and server{} on {}", clientProtocols, serverProtocols, getEndPoint());
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} protocol selected {} among client{} and server{}", this, negotiated, clientProtocols, serverProtocols);
|
||||
LOG.debug("Protocol selected {} from client{} and server{} on {}", negotiated, clientProtocols, serverProtocols, getEndPoint());
|
||||
setProtocol(negotiated);
|
||||
ALPN.remove(sslEngine);
|
||||
return negotiated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
ALPN.remove(getSSLEngine());
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
package org.eclipse.jetty.alpn.server;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
|
@ -26,52 +28,82 @@ import javax.net.ssl.SSLEngine;
|
|||
|
||||
import org.eclipse.jetty.io.AbstractConnection;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.io.ssl.ALPNProcessor;
|
||||
import org.eclipse.jetty.io.ssl.SslHandshakeListener;
|
||||
import org.eclipse.jetty.io.ssl.ALPNProcessor.Server;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.NegotiatingServerConnectionFactory;
|
||||
import org.eclipse.jetty.util.annotation.Name;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public class ALPNServerConnectionFactory extends NegotiatingServerConnectionFactory implements SslHandshakeListener
|
||||
public class ALPNServerConnectionFactory extends NegotiatingServerConnectionFactory
|
||||
{
|
||||
private final ALPNProcessor.Server alpnProcessor;
|
||||
private static final Logger LOG = Log.getLogger(ALPNServerConnectionFactory.class);
|
||||
|
||||
public ALPNServerConnectionFactory(String protocols)
|
||||
private final List<Server> processors = new ArrayList<>();
|
||||
|
||||
public ALPNServerConnectionFactory(@Name("protocols") String protocols)
|
||||
{
|
||||
this(protocols.trim().split(",", 0));
|
||||
}
|
||||
|
||||
|
||||
public ALPNServerConnectionFactory(@Name("protocols") String... protocols)
|
||||
{
|
||||
super("alpn", protocols);
|
||||
checkProtocolNegotiationAvailable();
|
||||
Iterator<ALPNProcessor.Server> processors = ServiceLoader.load(ALPNProcessor.Server.class).iterator();
|
||||
alpnProcessor = processors.hasNext() ? processors.next() : ALPNProcessor.Server.NOOP;
|
||||
}
|
||||
|
||||
public ALPNProcessor.Server getALPNProcessor()
|
||||
{
|
||||
return alpnProcessor;
|
||||
IllegalStateException failure = new IllegalStateException("No Server ALPNProcessors!");
|
||||
// Use a for loop on iterator so load exceptions can be caught and ignored
|
||||
for (Iterator<Server> i = ServiceLoader.load(Server.class).iterator(); i.hasNext();)
|
||||
{
|
||||
Server processor;
|
||||
try
|
||||
{
|
||||
processor = i.next();
|
||||
}
|
||||
catch(Throwable x)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug(x);
|
||||
failure.addSuppressed(x);
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
processor.init();
|
||||
processors.add(processor);
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Could not initialize " + processor, x);
|
||||
failure.addSuppressed(x);
|
||||
}
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("protocols: {}", Arrays.asList(protocols));
|
||||
LOG.debug("processors: {}", processors);
|
||||
}
|
||||
|
||||
if (processors.isEmpty())
|
||||
throw failure;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractConnection newServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List<String> protocols, String defaultProtocol)
|
||||
{
|
||||
getALPNProcessor().configure(engine);
|
||||
return new ALPNServerConnection(connector, endPoint, engine, protocols, defaultProtocol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handshakeSucceeded(Event event)
|
||||
{
|
||||
if (alpnProcessor instanceof SslHandshakeListener)
|
||||
((SslHandshakeListener)alpnProcessor).handshakeSucceeded(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handshakeFailed(Event event, Throwable failure)
|
||||
{
|
||||
if (alpnProcessor instanceof SslHandshakeListener)
|
||||
((SslHandshakeListener)alpnProcessor).handshakeFailed(event, failure);
|
||||
for (Server processor : processors)
|
||||
{
|
||||
if (processor.appliesTo(engine))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} for {} on {}", processor, engine, endPoint);
|
||||
ALPNServerConnection connection = new ALPNServerConnection(connector, endPoint, engine, protocols, defaultProtocol);
|
||||
processor.configure(engine, connection);
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("No ALPNProcessor for " + engine);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,10 @@
|
|||
<modules>
|
||||
<module>jetty-alpn-server</module>
|
||||
<module>jetty-alpn-client</module>
|
||||
<module>jetty-alpn-openjdk8-server</module>
|
||||
<module>jetty-alpn-openjdk8-client</module>
|
||||
<module>jetty-alpn-conscrypt-server</module>
|
||||
<module>jetty-alpn-conscrypt-client</module>
|
||||
</modules>
|
||||
<profiles>
|
||||
<profile>
|
||||
|
|
|
@ -24,16 +24,23 @@ import java.io.InputStream;
|
|||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarInputStream;
|
||||
|
||||
import org.eclipse.jetty.util.Loader;
|
||||
import org.eclipse.jetty.util.MultiException;
|
||||
import org.eclipse.jetty.util.MultiReleaseJarFile;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
|
@ -45,7 +52,6 @@ import org.objectweb.asm.FieldVisitor;
|
|||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
|
||||
/**
|
||||
* AnnotationParser
|
||||
* <p>
|
||||
|
@ -68,10 +74,29 @@ public class AnnotationParser
|
|||
{
|
||||
private static final Logger LOG = Log.getLogger(AnnotationParser.class);
|
||||
|
||||
protected Set<String> _parsedClassNames = ConcurrentHashMap.newKeySet();
|
||||
|
||||
private static final int JVM_MAJOR_VER;
|
||||
protected static int ASM_OPCODE_VERSION = Opcodes.ASM5; //compatibility of api
|
||||
|
||||
|
||||
protected Map<String, List<String>> _parsedClassNames = new ConcurrentHashMap<>();
|
||||
|
||||
static
|
||||
{
|
||||
// Determine JVM spec version
|
||||
// Using guidance from http://openjdk.java.net/jeps/223
|
||||
String jvmSpecVer = System.getProperty("java.vm.specification.version");
|
||||
|
||||
if (jvmSpecVer.indexOf('.') >= 0)
|
||||
{
|
||||
// Old spec version (Java 1.8 and older)
|
||||
String parts[] = jvmSpecVer.split("\\.");
|
||||
JVM_MAJOR_VER = Integer.parseInt(parts[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Newer spec version (Java 9+)
|
||||
JVM_MAJOR_VER = Integer.parseInt(jvmSpecVer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert internal name to simple name
|
||||
|
@ -110,12 +135,8 @@ public class AnnotationParser
|
|||
return normalList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ClassInfo
|
||||
*
|
||||
* Immutable information gathered by parsing class header.
|
||||
*
|
||||
*/
|
||||
public class ClassInfo
|
||||
{
|
||||
|
@ -175,10 +196,7 @@ public class AnnotationParser
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MethodInfo
|
||||
*
|
||||
* Immutable information gathered by parsing a method on a class.
|
||||
*/
|
||||
public class MethodInfo
|
||||
|
@ -231,14 +249,9 @@ public class AnnotationParser
|
|||
return _exceptions;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* FieldInfo
|
||||
*
|
||||
* Immutable information gathered by parsing a field on a class.
|
||||
*
|
||||
*/
|
||||
public class FieldInfo
|
||||
{
|
||||
|
@ -292,8 +305,6 @@ public class AnnotationParser
|
|||
}
|
||||
|
||||
/**
|
||||
* Handler
|
||||
*
|
||||
* Signature for all handlers that respond to parsing class files.
|
||||
*/
|
||||
public static interface Handler
|
||||
|
@ -307,13 +318,10 @@ public class AnnotationParser
|
|||
}
|
||||
|
||||
/**
|
||||
* AbstractHandler
|
||||
*
|
||||
* Convenience base class to provide no-ops for all Handler methods.
|
||||
*/
|
||||
public static abstract class AbstractHandler implements Handler
|
||||
{
|
||||
|
||||
@Override
|
||||
public void handle(ClassInfo classInfo)
|
||||
{
|
||||
|
@ -347,15 +355,11 @@ public class AnnotationParser
|
|||
@Override
|
||||
public void handle(FieldInfo info, String annotationName)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* MyMethodVisitor
|
||||
*
|
||||
* ASM Visitor for parsing a method. We are only interested in the annotations on methods.
|
||||
*/
|
||||
public class MyMethodVisitor extends MethodVisitor
|
||||
|
@ -376,11 +380,8 @@ public class AnnotationParser
|
|||
_mi = new MethodInfo(classInfo, name, access, methodDesc,signature, exceptions);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* We are only interested in finding the annotations on methods.
|
||||
*
|
||||
* @see org.objectweb.asm.MethodVisitor#visitAnnotation(java.lang.String, boolean)
|
||||
*/
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
|
||||
|
@ -392,21 +393,15 @@ public class AnnotationParser
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* MyFieldVisitor
|
||||
*
|
||||
* An ASM visitor for parsing Fields.
|
||||
* An ASM visitor for parsing Fields.
|
||||
* We are only interested in visiting annotations on Fields.
|
||||
*
|
||||
*/
|
||||
public class MyFieldVisitor extends FieldVisitor
|
||||
{
|
||||
final FieldInfo _fieldInfo;
|
||||
final Set<? extends Handler> _handlers;
|
||||
|
||||
|
||||
|
||||
public MyFieldVisitor(final Set<? extends Handler> handlers,
|
||||
final ClassInfo classInfo,
|
||||
final int access,
|
||||
|
@ -420,11 +415,8 @@ public class AnnotationParser
|
|||
_fieldInfo = new FieldInfo(classInfo, fieldName, access, fieldType, signature, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse an annotation found on a Field.
|
||||
*
|
||||
* @see org.objectweb.asm.FieldVisitor#visitAnnotation(java.lang.String, boolean)
|
||||
*/
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
|
||||
|
@ -437,17 +429,11 @@ public class AnnotationParser
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* MyClassVisitor
|
||||
*
|
||||
* ASM visitor for a class.
|
||||
*/
|
||||
public class MyClassVisitor extends ClassVisitor
|
||||
{
|
||||
|
||||
final Resource _containingResource;
|
||||
final Set<? extends Handler> _handlers;
|
||||
ClassInfo _ci;
|
||||
|
@ -459,7 +445,6 @@ public class AnnotationParser
|
|||
_containingResource = containingResource;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void visit (final int version,
|
||||
final int access,
|
||||
|
@ -469,18 +454,12 @@ public class AnnotationParser
|
|||
final String[] interfaces)
|
||||
{
|
||||
_ci = new ClassInfo(_containingResource, normalize(name), version, access, signature, normalize(superName), normalize(interfaces));
|
||||
|
||||
_parsedClassNames.add(_ci.getClassName());
|
||||
|
||||
for (Handler h:_handlers)
|
||||
h.handle(_ci);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Visit an annotation on a Class
|
||||
*
|
||||
* @see org.objectweb.asm.ClassVisitor#visitAnnotation(java.lang.String, boolean)
|
||||
*/
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation (String desc, boolean visible)
|
||||
|
@ -488,15 +467,11 @@ public class AnnotationParser
|
|||
String annotationName = normalize(desc);
|
||||
for (Handler h : _handlers)
|
||||
h.handle(_ci, annotationName);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Visit a method to extract its annotations
|
||||
*
|
||||
* @see org.objectweb.asm.ClassVisitor#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
|
||||
*/
|
||||
@Override
|
||||
public MethodVisitor visitMethod (final int access,
|
||||
|
@ -505,14 +480,11 @@ public class AnnotationParser
|
|||
final String signature,
|
||||
final String[] exceptions)
|
||||
{
|
||||
|
||||
return new MyMethodVisitor(_handlers, _ci, access, name, methodDesc, signature, exceptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit a field to extract its annotations
|
||||
*
|
||||
* @see org.objectweb.asm.ClassVisitor#visitField(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public FieldVisitor visitField (final int access,
|
||||
|
@ -525,20 +497,41 @@ public class AnnotationParser
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* True if the class has already been processed, false otherwise
|
||||
* @param className the classname
|
||||
* @return true if class was parsed, false if not
|
||||
* Add a class as having been parsed.
|
||||
*
|
||||
* @param classname the name of the class
|
||||
* @param location the fully qualified location of the class
|
||||
*/
|
||||
public boolean isParsed (String className)
|
||||
public void addParsedClass (String classname, Resource location)
|
||||
{
|
||||
return _parsedClassNames.contains(className);
|
||||
List<String> list = new ArrayList<>(1);
|
||||
if (location != null)
|
||||
list.add(location.toString());
|
||||
|
||||
List<String> existing = _parsedClassNames.putIfAbsent(classname, list);
|
||||
if (existing != null)
|
||||
{
|
||||
existing.addAll(list);
|
||||
LOG.warn("{} scanned from multiple locations: {}", classname, existing);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the locations of the given classname. There may be more than one
|
||||
* location if there are duplicates of the same class.
|
||||
*
|
||||
* @param classname the name of the class
|
||||
* @return an immutable list of locations
|
||||
*/
|
||||
public List<String> getParsedLocations (String classname)
|
||||
{
|
||||
List<String> list = _parsedClassNames.get(classname);
|
||||
if (list == null)
|
||||
return Collections.emptyList();
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Parse a given class
|
||||
*
|
||||
|
@ -546,29 +539,25 @@ public class AnnotationParser
|
|||
* @param className the class name to parse
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
public void parse (Set<? extends Handler> handlers, String className)
|
||||
throws Exception
|
||||
public void parse (Set<? extends Handler> handlers, String className) throws Exception
|
||||
{
|
||||
if (className == null)
|
||||
return;
|
||||
|
||||
if (!isParsed(className))
|
||||
String tmp = className;
|
||||
className = className.replace('.', '/')+".class";
|
||||
URL resource = Loader.getResource(className);
|
||||
if (resource!= null)
|
||||
{
|
||||
className = className.replace('.', '/')+".class";
|
||||
URL resource = Loader.getResource(className);
|
||||
if (resource!= null)
|
||||
Resource r = Resource.newResource(resource);
|
||||
addParsedClass(tmp, r);
|
||||
try (InputStream is = r.getInputStream())
|
||||
{
|
||||
Resource r = Resource.newResource(resource);
|
||||
try (InputStream is = r.getInputStream())
|
||||
{
|
||||
scanClass(handlers, null, is);
|
||||
}
|
||||
scanClass(handlers, null, is);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Parse the given class, optionally walking its inheritance hierarchy
|
||||
*
|
||||
|
@ -577,27 +566,23 @@ public class AnnotationParser
|
|||
* @param visitSuperClasses if true, also visit super classes for parse
|
||||
* @throws Exception if unable to parse class
|
||||
*/
|
||||
public void parse (Set<? extends Handler> handlers, Class<?> clazz, boolean visitSuperClasses)
|
||||
throws Exception
|
||||
public void parse (Set<? extends Handler> handlers, Class<?> clazz, boolean visitSuperClasses) throws Exception
|
||||
{
|
||||
Class<?> cz = clazz;
|
||||
while (cz != Object.class)
|
||||
{
|
||||
if (!isParsed(cz.getName()))
|
||||
String nameAsResource = cz.getName().replace('.', '/')+".class";
|
||||
URL resource = Loader.getResource(nameAsResource);
|
||||
if (resource!= null)
|
||||
{
|
||||
String nameAsResource = cz.getName().replace('.', '/')+".class";
|
||||
URL resource = Loader.getResource(nameAsResource);
|
||||
if (resource!= null)
|
||||
Resource r = Resource.newResource(resource);
|
||||
addParsedClass(clazz.getName(), r);
|
||||
try (InputStream is = r.getInputStream())
|
||||
{
|
||||
Resource r = Resource.newResource(resource);
|
||||
try (InputStream is = r.getInputStream())
|
||||
{
|
||||
scanClass(handlers, null, is);
|
||||
}
|
||||
scanClass(handlers, null, is);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (visitSuperClasses)
|
||||
cz = cz.getSuperclass();
|
||||
else
|
||||
|
@ -605,8 +590,6 @@ public class AnnotationParser
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Parse the given classes
|
||||
*
|
||||
|
@ -614,8 +597,7 @@ public class AnnotationParser
|
|||
* @param classNames the class name
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
public void parse (Set<? extends Handler> handlers, String[] classNames)
|
||||
throws Exception
|
||||
public void parse (Set<? extends Handler> handlers, String[] classNames) throws Exception
|
||||
{
|
||||
if (classNames == null)
|
||||
return;
|
||||
|
@ -623,7 +605,6 @@ public class AnnotationParser
|
|||
parse(handlers, Arrays.asList(classNames));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse the given classes
|
||||
*
|
||||
|
@ -631,26 +612,24 @@ public class AnnotationParser
|
|||
* @param classNames the class names
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
public void parse (Set<? extends Handler> handlers, List<String> classNames)
|
||||
throws Exception
|
||||
public void parse (Set<? extends Handler> handlers, List<String> classNames) throws Exception
|
||||
{
|
||||
MultiException me = new MultiException();
|
||||
|
||||
|
||||
for (String s:classNames)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!isParsed(s))
|
||||
String name = s;
|
||||
s = s.replace('.', '/')+".class";
|
||||
URL resource = Loader.getResource(s);
|
||||
if (resource!= null)
|
||||
{
|
||||
s = s.replace('.', '/')+".class";
|
||||
URL resource = Loader.getResource(s);
|
||||
if (resource!= null)
|
||||
Resource r = Resource.newResource(resource);
|
||||
addParsedClass(name, r);
|
||||
try (InputStream is = r.getInputStream())
|
||||
{
|
||||
Resource r = Resource.newResource(resource);
|
||||
try (InputStream is = r.getInputStream())
|
||||
{
|
||||
scanClass(handlers, null, is);
|
||||
}
|
||||
scanClass(handlers, null, is);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -662,67 +641,65 @@ public class AnnotationParser
|
|||
me.ifExceptionThrow();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse all classes in a directory
|
||||
*
|
||||
* @param handlers the set of handlers to look for classes in
|
||||
* @param dir the resource directory to look for classes
|
||||
* @param root the resource directory to look for classes
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
protected void parseDir (Set<? extends Handler> handlers, Resource dir)
|
||||
throws Exception
|
||||
protected void parseDir (Set<? extends Handler> handlers, Resource root) throws Exception
|
||||
{
|
||||
// skip dirs whose name start with . (ie hidden)
|
||||
if (!dir.isDirectory() || !dir.exists() || dir.getName().startsWith("."))
|
||||
if (!root.isDirectory() || !root.exists() || root.getName().startsWith("."))
|
||||
return;
|
||||
|
||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning dir {}", dir);};
|
||||
|
||||
MultiException me = new MultiException();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Scanning dir {}", root);
|
||||
|
||||
String[] files=dir.list();
|
||||
for (int f=0;files!=null && f<files.length;f++)
|
||||
File rootFile = root.getFile();
|
||||
|
||||
MultiException me = new MultiException();
|
||||
Collection<Resource> resources = root.getAllResources();
|
||||
if (resources != null)
|
||||
{
|
||||
Resource res = dir.addPath(files[f]);
|
||||
if (res.isDirectory())
|
||||
parseDir(handlers, res);
|
||||
else
|
||||
for (Resource r:resources)
|
||||
{
|
||||
//we've already verified the directories, so just verify the class file name
|
||||
File file = res.getFile();
|
||||
if (r.isDirectory())
|
||||
continue;
|
||||
|
||||
File file = r.getFile();
|
||||
if (isValidClassFileName((file==null?null:file.getName())))
|
||||
{
|
||||
Path classpath = rootFile.toPath().relativize(file.toPath());
|
||||
String str = classpath.toString();
|
||||
str = str.substring(0, str.lastIndexOf(".class")).replace('/', '.').replace('\\', '.');
|
||||
|
||||
try
|
||||
{
|
||||
String name = res.getName();
|
||||
if (!isParsed(name))
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Scanning class {}", r);
|
||||
addParsedClass(str, r);
|
||||
try (InputStream is=r.getInputStream())
|
||||
{
|
||||
Resource r = Resource.newResource(res.getURL());
|
||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning class {}", r);};
|
||||
try (InputStream is=r.getInputStream())
|
||||
{
|
||||
scanClass(handlers, dir, is);
|
||||
}
|
||||
scanClass(handlers, Resource.newResource(file.getParentFile()), is);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Error scanning file "+files[f], ex);
|
||||
me.add(new RuntimeException("Error scanning file "+files[f],ex));
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Error scanning file "+file, ex);
|
||||
me.add(new RuntimeException("Error scanning file "+file,ex));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Skipping scan on invalid file {}", res);
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Skipping scan on invalid file {}", file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
me.ifExceptionThrow();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse classes in the supplied classloader.
|
||||
* Only class files in jar files will be scanned.
|
||||
|
@ -733,39 +710,12 @@ public class AnnotationParser
|
|||
* @param nullInclusive if true, an empty pattern means all names match, if false, none match
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
public void parse (final Set<? extends Handler> handlers, ClassLoader loader, boolean visitParents, boolean nullInclusive)
|
||||
throws Exception
|
||||
@Deprecated
|
||||
public void parse (final Set<? extends Handler> handlers, ClassLoader loader, boolean visitParents, boolean nullInclusive) throws Exception
|
||||
{
|
||||
if (loader==null)
|
||||
return;
|
||||
|
||||
if (!(loader instanceof URLClassLoader))
|
||||
return; //can't extract classes?
|
||||
|
||||
final MultiException me = new MultiException();
|
||||
|
||||
JarScanner scanner = new JarScanner()
|
||||
{
|
||||
@Override
|
||||
public void processEntry(URI jarUri, JarEntry entry)
|
||||
{
|
||||
try
|
||||
{
|
||||
parseJarEntry(handlers, Resource.newResource(jarUri), entry);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
me.add(new RuntimeException("Error parsing entry "+entry.getName()+" from jar "+ jarUri, e));
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
scanner.scan(null, loader, nullInclusive, visitParents);
|
||||
me.ifExceptionThrow();
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse classes in the supplied uris.
|
||||
*
|
||||
|
@ -773,8 +723,7 @@ public class AnnotationParser
|
|||
* @param uris the uris for the jars
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
public void parse (final Set<? extends Handler> handlers, final URI[] uris)
|
||||
throws Exception
|
||||
public void parse (final Set<? extends Handler> handlers, final URI[] uris) throws Exception
|
||||
{
|
||||
if (uris==null)
|
||||
return;
|
||||
|
@ -802,8 +751,7 @@ public class AnnotationParser
|
|||
* @param uri the uri for the jar
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
public void parse (final Set<? extends Handler> handlers, URI uri)
|
||||
throws Exception
|
||||
public void parse (final Set<? extends Handler> handlers, URI uri) throws Exception
|
||||
{
|
||||
if (uri == null)
|
||||
return;
|
||||
|
@ -811,7 +759,6 @@ public class AnnotationParser
|
|||
parse (handlers, Resource.newResource(uri));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse a resource
|
||||
*
|
||||
|
@ -819,8 +766,7 @@ public class AnnotationParser
|
|||
* @param r the resource to parse
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
public void parse (final Set<? extends Handler> handlers, Resource r)
|
||||
throws Exception
|
||||
public void parse (final Set<? extends Handler> handlers, Resource r) throws Exception
|
||||
{
|
||||
if (r == null)
|
||||
return;
|
||||
|
@ -850,9 +796,6 @@ public class AnnotationParser
|
|||
if (LOG.isDebugEnabled()) LOG.warn("Resource not scannable for classes: {}", r);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Parse a resource that is a jar file.
|
||||
*
|
||||
|
@ -860,47 +803,31 @@ public class AnnotationParser
|
|||
* @param jarResource the jar resource to parse
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
protected void parseJar (Set<? extends Handler> handlers, Resource jarResource)
|
||||
throws Exception
|
||||
protected void parseJar (Set<? extends Handler> handlers, Resource jarResource) throws Exception
|
||||
{
|
||||
if (jarResource == null)
|
||||
return;
|
||||
|
||||
if (jarResource.toString().endsWith(".jar"))
|
||||
{
|
||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning jar {}", jarResource);};
|
||||
|
||||
//treat it as a jar that we need to open and scan all entries from
|
||||
InputStream in = jarResource.getInputStream();
|
||||
if (in==null)
|
||||
return;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Scanning jar {}", jarResource);
|
||||
|
||||
MultiException me = new MultiException();
|
||||
JarInputStream jar_in = new JarInputStream(in);
|
||||
try
|
||||
{
|
||||
JarEntry entry = jar_in.getNextJarEntry();
|
||||
while (entry!=null)
|
||||
{
|
||||
try
|
||||
{
|
||||
parseJarEntry(handlers, jarResource, entry);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
me.add(new RuntimeException("Error scanning entry "+entry.getName()+" from jar "+jarResource, e));
|
||||
}
|
||||
entry = jar_in.getNextJarEntry();
|
||||
// TODO do not force version 8 once ASM can scan 9
|
||||
MultiReleaseJarFile jarFile = new MultiReleaseJarFile(jarResource.getFile(),8,false);
|
||||
jarFile.stream().forEach(e->
|
||||
{
|
||||
try
|
||||
{
|
||||
parseJarEntry(handlers, jarResource, e);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
me.add(new RuntimeException("Error scanning jar "+jarResource, e));
|
||||
}
|
||||
finally
|
||||
{
|
||||
jar_in.close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
me.add(new RuntimeException("Error scanning entry " + e.getName() + " from jar " + jarResource, ex));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
me.ifExceptionThrow();
|
||||
}
|
||||
|
@ -910,12 +837,10 @@ public class AnnotationParser
|
|||
* Parse a single entry in a jar file
|
||||
*
|
||||
* @param handlers the handlers to look for classes in
|
||||
* @param jar the jar resource to parse
|
||||
* @param entry the entry in the jar resource to parse
|
||||
* @param entry the entry in the potentially MultiRelease jar resource to parse
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
protected void parseJarEntry (Set<? extends Handler> handlers, Resource jar, JarEntry entry)
|
||||
throws Exception
|
||||
protected void parseJarEntry (Set<? extends Handler> handlers, Resource jar, MultiReleaseJarFile.VersionedJarEntry entry) throws Exception
|
||||
{
|
||||
if (jar == null || entry == null)
|
||||
return;
|
||||
|
@ -930,20 +855,15 @@ public class AnnotationParser
|
|||
if (isValidClassFileName(name) && isValidClassFilePath(name))
|
||||
{
|
||||
String shortName = name.replace('/', '.').substring(0,name.length()-6);
|
||||
|
||||
if (!isParsed(shortName))
|
||||
addParsedClass(shortName, Resource.newResource("jar:"+jar.getURI()+"!/"+entry.getNameInJar()));
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Scanning class from jar {}!/{}", jar, entry);
|
||||
try (InputStream is = entry.getInputStream())
|
||||
{
|
||||
Resource clazz = Resource.newResource("jar:"+jar.getURI()+"!/"+name);
|
||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", clazz);};
|
||||
try (InputStream is = clazz.getInputStream())
|
||||
{
|
||||
scanClass(handlers, jar, is);
|
||||
}
|
||||
scanClass(handlers, jar, is);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Use ASM on a class
|
||||
|
@ -953,13 +873,20 @@ public class AnnotationParser
|
|||
* @param is the input stream to parse
|
||||
* @throws IOException if unable to parse
|
||||
*/
|
||||
protected void scanClass (Set<? extends Handler> handlers, Resource containingResource, InputStream is)
|
||||
throws IOException
|
||||
protected void scanClass (Set<? extends Handler> handlers, Resource containingResource, InputStream is) throws IOException
|
||||
{
|
||||
ClassReader reader = new ClassReader(is);
|
||||
reader.accept(new MyClassVisitor(handlers, containingResource), ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any parsed class names.
|
||||
*/
|
||||
public void resetParsedClasses ()
|
||||
{
|
||||
_parsedClassNames.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the given path represents a valid class file name.
|
||||
* The check is fairly cursory, checking that:
|
||||
|
@ -968,8 +895,8 @@ public class AnnotationParser
|
|||
* <li> it isn't a dot file or in a hidden directory </li>
|
||||
* <li> the name of the class at least begins with a valid identifier for a class name </li>
|
||||
* </ul>
|
||||
* @param name
|
||||
* @return
|
||||
* @param name the class file name
|
||||
* @return whether the class file name is valid
|
||||
*/
|
||||
private boolean isValidClassFileName (String name)
|
||||
{
|
||||
|
@ -1003,13 +930,12 @@ public class AnnotationParser
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check that the given path does not contain hidden directories
|
||||
*
|
||||
* @param path
|
||||
* @return
|
||||
* @param path the class file path
|
||||
* @return whether the class file path is valid
|
||||
*/
|
||||
private boolean isValidClassFilePath (String path)
|
||||
{
|
||||
|
@ -1017,14 +943,13 @@ public class AnnotationParser
|
|||
if (path == null || path.length()==0)
|
||||
return false;
|
||||
|
||||
//skip any classfiles that are in a hidden directory
|
||||
// skip any classfiles that are in a hidden directory
|
||||
if (path.startsWith(".") || path.contains("/."))
|
||||
{
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Contains hidden dirs: {}"+path);
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Contains hidden dirs: " + path);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,11 +18,15 @@
|
|||
|
||||
package org.eclipse.jetty.annotations;
|
||||
|
||||
|
||||
import javax.annotation.security.DeclareRoles;
|
||||
import javax.servlet.Servlet;
|
||||
|
||||
import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
|
||||
import org.eclipse.jetty.security.ConstraintAware;
|
||||
import org.eclipse.jetty.security.ConstraintSecurityHandler;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
|
||||
/**
|
||||
|
@ -30,6 +34,7 @@ import org.eclipse.jetty.webapp.WebAppContext;
|
|||
*/
|
||||
public class DeclareRolesAnnotationHandler extends AbstractIntrospectableAnnotationHandler
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(DeclareRolesAnnotationHandler.class);
|
||||
|
||||
protected WebAppContext _context;
|
||||
|
||||
|
@ -48,6 +53,12 @@ public class DeclareRolesAnnotationHandler extends AbstractIntrospectableAnnotat
|
|||
if (!Servlet.class.isAssignableFrom(clazz))
|
||||
return; //only applicable on javax.servlet.Servlet derivatives
|
||||
|
||||
if (!(_context.getSecurityHandler() instanceof ConstraintAware))
|
||||
{
|
||||
LOG.warn("SecurityHandler not ConstraintAware, skipping security annotation processing");
|
||||
return;
|
||||
}
|
||||
|
||||
DeclareRoles declareRoles = (DeclareRoles) clazz.getAnnotation(DeclareRoles.class);
|
||||
if (declareRoles == null)
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package org.acme;
|
||||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* ClassOne
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class ClassOne
|
||||
{
|
||||
|
||||
public void one()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
|
@ -29,6 +29,7 @@ import java.io.FileOutputStream;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -44,6 +45,7 @@ import org.eclipse.jetty.toolchain.test.FS;
|
|||
import org.eclipse.jetty.toolchain.test.IO;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.toolchain.test.TestingDir;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
@ -163,6 +165,26 @@ public class TestAnnotationParser
|
|||
// only the valid classes inside bad-classes.jar should be parsed. If any invalid classes are parsed and exception would be thrown here
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModuleInfoClassInJar() throws Exception
|
||||
{
|
||||
File badClassesJar = MavenTestingUtils.getTestResourceFile("jdk9/slf4j-api-1.8.0-alpha2.jar");
|
||||
AnnotationParser parser = new AnnotationParser();
|
||||
Set<Handler> emptySet = Collections.emptySet();
|
||||
parser.parse(emptySet, badClassesJar.toURI());
|
||||
// Should throw no exceptions, and happily skip the module-info.class files
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJep238MultiReleaseInJar() throws Exception
|
||||
{
|
||||
File badClassesJar = MavenTestingUtils.getTestResourceFile("jdk9/log4j-api-2.9.0.jar");
|
||||
AnnotationParser parser = new AnnotationParser();
|
||||
Set<Handler> emptySet = Collections.emptySet();
|
||||
parser.parse(emptySet, badClassesJar.toURI());
|
||||
// Should throw no exceptions, and skip the META-INF/versions/9/* files
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasedirExclusion() throws Exception
|
||||
{
|
||||
|
@ -189,6 +211,40 @@ public class TestAnnotationParser
|
|||
// Validate
|
||||
Assert.assertThat("Found Class", tracker.foundClasses, contains(ClassA.class.getName()));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testScanDuplicateClassesInJars() throws Exception
|
||||
{
|
||||
Resource testJar = Resource.newResource(MavenTestingUtils.getTestResourceFile("tinytest.jar"));
|
||||
Resource testJar2 = Resource.newResource(MavenTestingUtils.getTestResourceFile("tinytest_copy.jar"));
|
||||
AnnotationParser parser = new AnnotationParser();
|
||||
Set<Handler> emptySet = Collections.emptySet();
|
||||
parser.parse(emptySet, testJar);
|
||||
parser.parse(emptySet, testJar2);
|
||||
List<String> locations = parser.getParsedLocations("org.acme.ClassOne");
|
||||
Assert.assertNotNull(locations);
|
||||
Assert.assertEquals(2, locations.size());
|
||||
Assert.assertTrue(!(locations.get(0).equals(locations.get(1))));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testScanDuplicateClasses() throws Exception
|
||||
{
|
||||
Resource testJar = Resource.newResource(MavenTestingUtils.getTestResourceFile("tinytest.jar"));
|
||||
File testClasses = new File(MavenTestingUtils.getTargetDir(), "test-classes");
|
||||
AnnotationParser parser = new AnnotationParser();
|
||||
Set<Handler> emptySet = Collections.emptySet();
|
||||
parser.parse(emptySet, testJar);
|
||||
parser.parse(emptySet, Resource.newResource(testClasses));
|
||||
List<String> locations = parser.getParsedLocations("org.acme.ClassOne");
|
||||
Assert.assertNotNull(locations);
|
||||
Assert.assertEquals(2, locations.size());
|
||||
Assert.assertTrue(!(locations.get(0).equals(locations.get(1))));
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void copyClass(Class<?> clazz, File basedir) throws IOException
|
||||
{
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -169,12 +169,12 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.fcgi</groupId>
|
||||
<artifactId>jetty-fcgi-client</artifactId>
|
||||
<artifactId>fcgi-client</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.fcgi</groupId>
|
||||
<artifactId>jetty-fcgi-server</artifactId>
|
||||
<artifactId>fcgi-server</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -262,11 +262,6 @@
|
|||
<artifactId>jetty-memcached-sessions</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-monitor</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-nosql</artifactId>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty.cdi</groupId>
|
||||
<artifactId>jetty-cdi-parent</artifactId>
|
||||
<version>9.4.7-SNAPSHOT</version>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>cdi-webapp-it</artifactId>
|
||||
|
|
|
@ -73,6 +73,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.eclipse.jetty.util.thread.ThreadPool;
|
||||
|
||||
/**
|
||||
* <p>{@link HttpClient} provides an efficient, asynchronous, non-blocking implementation
|
||||
|
@ -118,33 +119,33 @@ public class HttpClient extends ContainerLifeCycle
|
|||
private final ConcurrentMap<Origin, HttpDestination> destinations = new ConcurrentHashMap<>();
|
||||
private final ProtocolHandlers handlers = new ProtocolHandlers();
|
||||
private final List<Request.Listener> requestListeners = new ArrayList<>();
|
||||
private final AuthenticationStore authenticationStore = new HttpAuthenticationStore();
|
||||
private final Set<ContentDecoder.Factory> decoderFactories = new ContentDecoderFactorySet();
|
||||
private final ProxyConfiguration proxyConfig = new ProxyConfiguration();
|
||||
private final HttpClientTransport transport;
|
||||
private final SslContextFactory sslContextFactory;
|
||||
private volatile CookieManager cookieManager;
|
||||
private volatile CookieStore cookieStore;
|
||||
private volatile Executor executor;
|
||||
private volatile ByteBufferPool byteBufferPool;
|
||||
private volatile Scheduler scheduler;
|
||||
private volatile SocketAddressResolver resolver;
|
||||
private volatile HttpField agentField = new HttpField(HttpHeader.USER_AGENT, "Jetty/" + Jetty.VERSION);
|
||||
private volatile boolean followRedirects = true;
|
||||
private volatile int maxConnectionsPerDestination = 64;
|
||||
private volatile int maxRequestsQueuedPerDestination = 1024;
|
||||
private volatile int requestBufferSize = 4096;
|
||||
private volatile int responseBufferSize = 16384;
|
||||
private volatile int maxRedirects = 8;
|
||||
private volatile SocketAddress bindAddress;
|
||||
private volatile long connectTimeout = 15000;
|
||||
private volatile long addressResolutionTimeout = 15000;
|
||||
private volatile long idleTimeout;
|
||||
private volatile boolean tcpNoDelay = true;
|
||||
private volatile boolean strictEventOrdering = false;
|
||||
private volatile HttpField encodingField;
|
||||
private volatile boolean removeIdleDestinations = false;
|
||||
private volatile boolean connectBlocking = false;
|
||||
private AuthenticationStore authenticationStore = new HttpAuthenticationStore();
|
||||
private CookieManager cookieManager;
|
||||
private CookieStore cookieStore;
|
||||
private Executor executor;
|
||||
private ByteBufferPool byteBufferPool;
|
||||
private Scheduler scheduler;
|
||||
private SocketAddressResolver resolver;
|
||||
private HttpField agentField = new HttpField(HttpHeader.USER_AGENT, "Jetty/" + Jetty.VERSION);
|
||||
private boolean followRedirects = true;
|
||||
private int maxConnectionsPerDestination = 64;
|
||||
private int maxRequestsQueuedPerDestination = 1024;
|
||||
private int requestBufferSize = 4096;
|
||||
private int responseBufferSize = 16384;
|
||||
private int maxRedirects = 8;
|
||||
private SocketAddress bindAddress;
|
||||
private long connectTimeout = 15000;
|
||||
private long addressResolutionTimeout = 15000;
|
||||
private long idleTimeout;
|
||||
private boolean tcpNoDelay = true;
|
||||
private boolean strictEventOrdering = false;
|
||||
private HttpField encodingField;
|
||||
private boolean removeIdleDestinations = false;
|
||||
private boolean connectBlocking = false;
|
||||
|
||||
/**
|
||||
* Creates a {@link HttpClient} instance that can perform requests to non-TLS destinations only
|
||||
|
@ -204,9 +205,12 @@ public class HttpClient extends ContainerLifeCycle
|
|||
executor = threadPool;
|
||||
}
|
||||
addBean(executor);
|
||||
|
||||
|
||||
if (byteBufferPool == null)
|
||||
byteBufferPool = new MappedByteBufferPool();
|
||||
byteBufferPool = new MappedByteBufferPool(2048,
|
||||
executor instanceof ThreadPool.SizedThreadPool
|
||||
? ((ThreadPool.SizedThreadPool)executor).getMaxThreads()/2
|
||||
: Runtime.getRuntime().availableProcessors()*2);
|
||||
addBean(byteBufferPool);
|
||||
|
||||
if (scheduler == null)
|
||||
|
@ -302,6 +306,14 @@ public class HttpClient extends ContainerLifeCycle
|
|||
return authenticationStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param authenticationStore the authentication store associated with this instance
|
||||
*/
|
||||
public void setAuthenticationStore(AuthenticationStore authenticationStore)
|
||||
{
|
||||
this.authenticationStore = authenticationStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a <em>non</em> thread-safe set of {@link ContentDecoder.Factory}s that can be modified before
|
||||
* performing requests.
|
||||
|
|
|
@ -29,9 +29,7 @@ import javax.servlet.ServletException;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.client.util.DeferredContentProvider;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
|
@ -53,22 +51,11 @@ public class HttpResponseAbortTest extends AbstractHttpClientServerTest
|
|||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.onResponseBegin(new Response.BeginListener()
|
||||
.onResponseBegin(response -> response.abort(new Exception()))
|
||||
.send(result ->
|
||||
{
|
||||
@Override
|
||||
public void onBegin(Response response)
|
||||
{
|
||||
response.abort(new Exception());
|
||||
}
|
||||
})
|
||||
.send(new Response.CompleteListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertTrue(result.isFailed());
|
||||
latch.countDown();
|
||||
}
|
||||
Assert.assertTrue(result.isFailed());
|
||||
latch.countDown();
|
||||
});
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
@ -81,23 +68,15 @@ public class HttpResponseAbortTest extends AbstractHttpClientServerTest
|
|||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.onResponseHeader(new Response.HeaderListener()
|
||||
.onResponseHeader((response, field) ->
|
||||
{
|
||||
@Override
|
||||
public boolean onHeader(Response response, HttpField field)
|
||||
{
|
||||
response.abort(new Exception());
|
||||
return true;
|
||||
}
|
||||
response.abort(new Exception());
|
||||
return true;
|
||||
})
|
||||
.send(new Response.CompleteListener()
|
||||
.send(result ->
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertTrue(result.isFailed());
|
||||
latch.countDown();
|
||||
}
|
||||
Assert.assertTrue(result.isFailed());
|
||||
latch.countDown();
|
||||
});
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
@ -110,23 +89,11 @@ public class HttpResponseAbortTest extends AbstractHttpClientServerTest
|
|||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.onResponseHeaders(new Response.HeadersListener()
|
||||
.onResponseHeaders(response -> response.abort(new Exception()))
|
||||
.send(result ->
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(Response response)
|
||||
{
|
||||
response.abort(new Exception());
|
||||
}
|
||||
})
|
||||
.send(new Response.CompleteListener()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertTrue(result.isFailed());
|
||||
latch.countDown();
|
||||
}
|
||||
Assert.assertTrue(result.isFailed());
|
||||
latch.countDown();
|
||||
});
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
@ -158,22 +125,11 @@ public class HttpResponseAbortTest extends AbstractHttpClientServerTest
|
|||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.onResponseContent(new Response.ContentListener()
|
||||
.onResponseContent((response, content) -> response.abort(new Exception()))
|
||||
.send(result ->
|
||||
{
|
||||
@Override
|
||||
public void onContent(Response response, ByteBuffer content)
|
||||
{
|
||||
response.abort(new Exception());
|
||||
}
|
||||
})
|
||||
.send(new Response.CompleteListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertTrue(result.isFailed());
|
||||
latch.countDown();
|
||||
}
|
||||
Assert.assertTrue(result.isFailed());
|
||||
latch.countDown();
|
||||
});
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
@ -202,53 +158,31 @@ public class HttpResponseAbortTest extends AbstractHttpClientServerTest
|
|||
}
|
||||
});
|
||||
|
||||
final CountDownLatch abortLatch = new CountDownLatch(1);
|
||||
final DeferredContentProvider contentProvider = new DeferredContentProvider(ByteBuffer.allocate(1));
|
||||
final AtomicInteger completes = new AtomicInteger();
|
||||
final CountDownLatch completeLatch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.onRequestSuccess(new org.eclipse.jetty.client.api.Request.SuccessListener()
|
||||
.content(contentProvider)
|
||||
.onResponseContent((response, content) ->
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(org.eclipse.jetty.client.api.Request request)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
abortLatch.await(5, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
x.printStackTrace();
|
||||
}
|
||||
response.abort(new Exception());
|
||||
contentProvider.close();
|
||||
// Delay to let the request side to finish its processing.
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
x.printStackTrace();
|
||||
}
|
||||
})
|
||||
.onResponseContent(new Response.ContentListener()
|
||||
.send(result ->
|
||||
{
|
||||
@Override
|
||||
public void onContent(Response response, ByteBuffer content)
|
||||
{
|
||||
try
|
||||
{
|
||||
response.abort(new Exception());
|
||||
abortLatch.countDown();
|
||||
// Delay to let the request side to finish its processing.
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
x.printStackTrace();
|
||||
}
|
||||
}
|
||||
})
|
||||
.send(new Response.CompleteListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
completes.incrementAndGet();
|
||||
Assert.assertTrue(result.isFailed());
|
||||
completeLatch.countDown();
|
||||
}
|
||||
completes.incrementAndGet();
|
||||
Assert.assertTrue(result.isFailed());
|
||||
completeLatch.countDown();
|
||||
});
|
||||
Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
|
|
|
@ -207,7 +207,7 @@ INFO : logging-log4j initialized in ${jetty.base}/start.d/logging-log4j.ini
|
|||
MKDIR : ${jetty.base}/lib/slf4j
|
||||
DOWNLD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-api-1.7.21.jar
|
||||
MKDIR : ${jetty.base}/lib/log4j
|
||||
COPY : /Users/chris/.m2/repository/log4j/log4j/1.2.17/log4j-1.2.17.jar to ${jetty.base}/lib/log4j/log4j-1.2.17.jar
|
||||
COPY : /Users/admin/.m2/repository/log4j/log4j/1.2.17/log4j-1.2.17.jar to ${jetty.base}/lib/log4j/log4j-1.2.17.jar
|
||||
MKDIR : ${jetty.base}/resources
|
||||
COPY : ${jetty.home}/modules/log4j-impl/resources/log4j.xml to ${jetty.base}/resources/log4j.xml
|
||||
DOWNLD: http://central.maven.org/maven2/org/slf4j/slf4j-log4j12/1.7.21/slf4j-log4j12-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-log4j12-1.7.21.jar
|
||||
|
|
|
@ -25,6 +25,8 @@ Jetty also offers more niche session managers that leverage backends such as Mon
|
|||
|
||||
include::session-hierarchy.adoc[]
|
||||
include::sessions-details.adoc[]
|
||||
include::session-configuration-housekeeper.adoc[]
|
||||
include::session-configuration-sessioncache.adoc[]
|
||||
include::session-configuration-memory.adoc[]
|
||||
include::session-configuration-file-system.adoc[]
|
||||
include::session-configuration-jdbc.adoc[]
|
||||
|
@ -32,6 +34,8 @@ include::session-configuration-mongodb.adoc[]
|
|||
include::session-configuration-infinispan.adoc[]
|
||||
include::session-configuration-hazelcast.adoc[]
|
||||
include::session-configuration-gcloud.adoc[]
|
||||
include::session-configuration-memcachedsessiondatastore.adoc[]
|
||||
include::sessions-usecases.adoc[]
|
||||
//include::setting-session-characteristics.adoc[]
|
||||
//include::using-persistent-sessions.adoc[]
|
||||
//include::session-clustering-jdbc.adoc[]
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
|
||||
[[configuring-sessions-file-system]]
|
||||
|
||||
=== Non-Clustered Session Management: File System
|
||||
=== Persistent Sessions: File System
|
||||
|
||||
Note: Persisting sessions to the local file system should *not* be used in a clustered environment.
|
||||
|
||||
==== Enabling File System Sessions
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
// ========================================================================
|
||||
|
||||
[[configuring-sessions-gcloud]]
|
||||
=== Clustered Session Management: Google Cloud DataStore
|
||||
|
||||
=== Persistent Sessions: Google Cloud DataStore
|
||||
|
||||
==== Preparation
|
||||
|
||||
|
@ -122,49 +123,49 @@ COPY : ${jetty.home}/modules/jul-impl/etc/java-util-logging.properties to ${jet
|
|||
MKDIR : ${jetty.base}/lib/slf4j
|
||||
DOWNLD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-api-1.7.21.jar
|
||||
MKDIR : ${jetty.base}/lib/gcloud
|
||||
COPY : /Users/chris/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar to ${jetty.base}/lib/gcloud/aopalliance-1.0.jar
|
||||
COPY : /Users/chris/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.1.3/jackson-core-2.1.3.jar to ${jetty.base}/lib/gcloud/jackson-core-2.1.3.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/api-client/google-api-client-appengine/1.21.0/google-api-client-appengine-1.21.0.jar to ${jetty.base}/lib/gcloud/google-api-client-appengine-1.21.0.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/api-client/google-api-client/1.20.0/google-api-client-1.20.0.jar to ${jetty.base}/lib/gcloud/google-api-client-1.20.0.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/api-client/google-api-client-servlet/1.21.0/google-api-client-servlet-1.21.0.jar to ${jetty.base}/lib/gcloud/google-api-client-servlet-1.21.0.jar
|
||||
COPY : /Users/admin/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar to ${jetty.base}/lib/gcloud/aopalliance-1.0.jar
|
||||
COPY : /Users/admin/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.1.3/jackson-core-2.1.3.jar to ${jetty.base}/lib/gcloud/jackson-core-2.1.3.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/api-client/google-api-client-appengine/1.21.0/google-api-client-appengine-1.21.0.jar to ${jetty.base}/lib/gcloud/google-api-client-appengine-1.21.0.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/api-client/google-api-client/1.20.0/google-api-client-1.20.0.jar to ${jetty.base}/lib/gcloud/google-api-client-1.20.0.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/api-client/google-api-client-servlet/1.21.0/google-api-client-servlet-1.21.0.jar to ${jetty.base}/lib/gcloud/google-api-client-servlet-1.21.0.jar
|
||||
DOWNLD: http://central.maven.org/maven2/com/google/api/gax/0.0.21/gax-0.0.21.jar to ${jetty.base}/lib/gcloud/gax-0.0.21.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/api/grpc/grpc-google-common-protos/0.1.0/grpc-google-common-protos-0.1.0.jar to ${jetty.base}/lib/gcloud/grpc-google-common-protos-0.1.0.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/api/grpc/grpc-google-iam-v1/0.1.0/grpc-google-iam-v1-0.1.0.jar to ${jetty.base}/lib/gcloud/grpc-google-iam-v1-0.1.0.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/auth/google-auth-library-credentials/0.3.1/google-auth-library-credentials-0.3.1.jar to ${jetty.base}/lib/gcloud/google-auth-library-credentials-0.3.1.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/auth/google-auth-library-oauth2-http/0.3.1/google-auth-library-oauth2-http-0.3.1.jar to ${jetty.base}/lib/gcloud/google-auth-library-oauth2-http-0.3.1.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/api/grpc/grpc-google-common-protos/0.1.0/grpc-google-common-protos-0.1.0.jar to ${jetty.base}/lib/gcloud/grpc-google-common-protos-0.1.0.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/api/grpc/grpc-google-iam-v1/0.1.0/grpc-google-iam-v1-0.1.0.jar to ${jetty.base}/lib/gcloud/grpc-google-iam-v1-0.1.0.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/auth/google-auth-library-credentials/0.3.1/google-auth-library-credentials-0.3.1.jar to ${jetty.base}/lib/gcloud/google-auth-library-credentials-0.3.1.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/auth/google-auth-library-oauth2-http/0.3.1/google-auth-library-oauth2-http-0.3.1.jar to ${jetty.base}/lib/gcloud/google-auth-library-oauth2-http-0.3.1.jar
|
||||
DOWNLD: http://central.maven.org/maven2/com/google/auto/value/auto-value/1.2/auto-value-1.2.jar to ${jetty.base}/lib/gcloud/auto-value-1.2.jar
|
||||
DOWNLD: http://central.maven.org/maven2/com/google/cloud/datastore/datastore-v1-proto-client/1.3.0/datastore-v1-proto-client-1.3.0.jar to ${jetty.base}/lib/gcloud/datastore-v1-proto-client-1.3.0.jar
|
||||
DOWNLD: http://central.maven.org/maven2/com/google/cloud/datastore/datastore-v1-protos/1.3.0/datastore-v1-protos-1.3.0.jar to ${jetty.base}/lib/gcloud/datastore-v1-protos-1.3.0.jar
|
||||
DOWNLD: http://central.maven.org/maven2/com/google/cloud/google-cloud-core/0.5.1/google-cloud-core-0.5.1.jar to ${jetty.base}/lib/gcloud/google-cloud-core-0.5.0.jar
|
||||
DOWNLD: http://central.maven.org/maven2/com/google/cloud/google-cloud-datastore/0.5.1/google-cloud-datastore-0.5.1.jar to ${jetty.base}/lib/gcloud/google-cloud-datastore-0.5.1.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar to ${jetty.base}/lib/gcloud/jsr305-1.3.9.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/code/gson/gson/2.3/gson-2.3.jar to ${jetty.base}/lib/gcloud/gson-2.3.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/guava/guava/19.0/guava-19.0.jar to ${jetty.base}/lib/gcloud/guava-19.0.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/http-client/google-http-client-appengine/1.21.0/google-http-client-appengine-1.21.0.jar to ${jetty.base}/lib/gcloud/google-http-client-appengine-1.21.0.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/http-client/google-http-client-jackson2/1.19.0/google-http-client-jackson2-1.19.0.jar to ${jetty.base}/lib/gcloud/google-http-client-jackson2-1.19.0.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/http-client/google-http-client-jackson/1.21.0/google-http-client-jackson-1.21.0.jar to ${jetty.base}/lib/gcloud/google-http-client-jackson-1.21.0.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/http-client/google-http-client/1.21.0/google-http-client-1.21.0.jar to ${jetty.base}/lib/gcloud/google-http-client-1.21.0.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/http-client/google-http-client-jdo/1.21.0/google-http-client-jdo-1.21.0.jar to ${jetty.base}/lib/gcloud/google-http-client-jdo-1.21.0.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/http-client/google-http-client-protobuf/1.20.0/google-http-client-protobuf-1.20.0.jar to ${jetty.base}/lib/gcloud/google-http-client-protobuf-1.20.0.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/inject/guice/4.0/guice-4.0.jar to ${jetty.base}/lib/gcloud/guice-4.0.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/oauth-client/google-oauth-client-appengine/1.21.0/google-oauth-client-appengine-1.21.0.jar to ${jetty.base}/lib/gcloud/google-oauth-client-appengine-1.21.0.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/oauth-client/google-oauth-client/1.21.0/google-oauth-client-1.21.0.jar to ${jetty.base}/lib/gcloud/google-oauth-client-1.21.0.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/oauth-client/google-oauth-client-servlet/1.21.0/google-oauth-client-servlet-1.21.0.jar to ${jetty.base}/lib/gcloud/google-oauth-client-servlet-1.21.0.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/protobuf/protobuf-java/3.0.0/protobuf-java-3.0.0.jar to ${jetty.base}/lib/gcloud/protobuf-java-3.0.0.jar
|
||||
COPY : /Users/chris/.m2/repository/com/google/protobuf/protobuf-java-util/3.0.0/protobuf-java-util-3.0.0.jar to ${jetty.base}/lib/gcloud/protobuf-java-util-3.0.0.jar
|
||||
COPY : /Users/chris/.m2/repository/commons-codec/commons-codec/1.3/commons-codec-1.3.jar to ${jetty.base}/lib/gcloud/commons-codec-1.3.jar
|
||||
COPY : /Users/chris/.m2/repository/io/grpc/grpc-context/1.0.1/grpc-context-1.0.1.jar to ${jetty.base}/lib/gcloud/grpc-context-1.0.1.jar
|
||||
COPY : /Users/chris/.m2/repository/io/grpc/grpc-core/1.0.1/grpc-core-1.0.1.jar to ${jetty.base}/lib/gcloud/grpc-core-1.0.1.jar
|
||||
COPY : /Users/chris/.m2/repository/io/grpc/grpc-protobuf/1.0.1/grpc-protobuf-1.0.1.jar to ${jetty.base}/lib/gcloud/grpc-protobuf-1.0.1.jar
|
||||
COPY : /Users/chris/.m2/repository/io/grpc/grpc-protobuf-lite/1.0.1/grpc-protobuf-lite-1.0.1.jar to ${jetty.base}/lib/gcloud/grpc-protobuf-lite-1.0.1.jar
|
||||
COPY : /Users/chris/.m2/repository/javax/inject/javax.inject/1/javax.inject-1.jar to ${jetty.base}/lib/gcloud/javax.inject-1.jar
|
||||
COPY : /Users/chris/.m2/repository/javax/jdo/jdo2-api/2.3-eb/jdo2-api-2.3-eb.jar to ${jetty.base}/lib/gcloud/jdo2-api-2.3-eb.jar
|
||||
COPY : /Users/chris/.m2/repository/javax/transaction/transaction-api/1.1/transaction-api-1.1.jar to ${jetty.base}/lib/gcloud/transaction-api-1.1.jar
|
||||
COPY : /Users/chris/.m2/repository/joda-time/joda-time/2.9.2/joda-time-2.9.2.jar to ${jetty.base}/lib/gcloud/joda-time-2.9.2.jar
|
||||
COPY : /Users/chris/.m2/repository/org/apache/httpcomponents/httpclient/4.0.1/httpclient-4.0.1.jar to ${jetty.base}/lib/gcloud/httpclient-4.0.1.jar
|
||||
COPY : /Users/chris/.m2/repository/org/apache/httpcomponents/httpcore/4.0.1/httpcore-4.0.1.jar to ${jetty.base}/lib/gcloud/httpcore-4.0.1.jar
|
||||
COPY : /Users/chris/.m2/repository/org/codehaus/jackson/jackson-core-asl/1.9.11/jackson-core-asl-1.9.11.jar to ${jetty.base}/lib/gcloud/jackson-core-asl-1.9.11.jar
|
||||
COPY : /Users/chris/.m2/repository/org/json/json/20151123/json-20151123.jar to ${jetty.base}/lib/gcloud/json-20151123.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar to ${jetty.base}/lib/gcloud/jsr305-1.3.9.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/code/gson/gson/2.3/gson-2.3.jar to ${jetty.base}/lib/gcloud/gson-2.3.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/guava/guava/19.0/guava-19.0.jar to ${jetty.base}/lib/gcloud/guava-19.0.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/http-client/google-http-client-appengine/1.21.0/google-http-client-appengine-1.21.0.jar to ${jetty.base}/lib/gcloud/google-http-client-appengine-1.21.0.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/http-client/google-http-client-jackson2/1.19.0/google-http-client-jackson2-1.19.0.jar to ${jetty.base}/lib/gcloud/google-http-client-jackson2-1.19.0.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/http-client/google-http-client-jackson/1.21.0/google-http-client-jackson-1.21.0.jar to ${jetty.base}/lib/gcloud/google-http-client-jackson-1.21.0.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/http-client/google-http-client/1.21.0/google-http-client-1.21.0.jar to ${jetty.base}/lib/gcloud/google-http-client-1.21.0.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/http-client/google-http-client-jdo/1.21.0/google-http-client-jdo-1.21.0.jar to ${jetty.base}/lib/gcloud/google-http-client-jdo-1.21.0.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/http-client/google-http-client-protobuf/1.20.0/google-http-client-protobuf-1.20.0.jar to ${jetty.base}/lib/gcloud/google-http-client-protobuf-1.20.0.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/inject/guice/4.0/guice-4.0.jar to ${jetty.base}/lib/gcloud/guice-4.0.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/oauth-client/google-oauth-client-appengine/1.21.0/google-oauth-client-appengine-1.21.0.jar to ${jetty.base}/lib/gcloud/google-oauth-client-appengine-1.21.0.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/oauth-client/google-oauth-client/1.21.0/google-oauth-client-1.21.0.jar to ${jetty.base}/lib/gcloud/google-oauth-client-1.21.0.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/oauth-client/google-oauth-client-servlet/1.21.0/google-oauth-client-servlet-1.21.0.jar to ${jetty.base}/lib/gcloud/google-oauth-client-servlet-1.21.0.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/protobuf/protobuf-java/3.0.0/protobuf-java-3.0.0.jar to ${jetty.base}/lib/gcloud/protobuf-java-3.0.0.jar
|
||||
COPY : /Users/admin/.m2/repository/com/google/protobuf/protobuf-java-util/3.0.0/protobuf-java-util-3.0.0.jar to ${jetty.base}/lib/gcloud/protobuf-java-util-3.0.0.jar
|
||||
COPY : /Users/admin/.m2/repository/commons-codec/commons-codec/1.3/commons-codec-1.3.jar to ${jetty.base}/lib/gcloud/commons-codec-1.3.jar
|
||||
COPY : /Users/admin/.m2/repository/io/grpc/grpc-context/1.0.1/grpc-context-1.0.1.jar to ${jetty.base}/lib/gcloud/grpc-context-1.0.1.jar
|
||||
COPY : /Users/admin/.m2/repository/io/grpc/grpc-core/1.0.1/grpc-core-1.0.1.jar to ${jetty.base}/lib/gcloud/grpc-core-1.0.1.jar
|
||||
COPY : /Users/admin/.m2/repository/io/grpc/grpc-protobuf/1.0.1/grpc-protobuf-1.0.1.jar to ${jetty.base}/lib/gcloud/grpc-protobuf-1.0.1.jar
|
||||
COPY : /Users/admin/.m2/repository/io/grpc/grpc-protobuf-lite/1.0.1/grpc-protobuf-lite-1.0.1.jar to ${jetty.base}/lib/gcloud/grpc-protobuf-lite-1.0.1.jar
|
||||
COPY : /Users/admin/.m2/repository/javax/inject/javax.inject/1/javax.inject-1.jar to ${jetty.base}/lib/gcloud/javax.inject-1.jar
|
||||
COPY : /Users/admin/.m2/repository/javax/jdo/jdo2-api/2.3-eb/jdo2-api-2.3-eb.jar to ${jetty.base}/lib/gcloud/jdo2-api-2.3-eb.jar
|
||||
COPY : /Users/admin/.m2/repository/javax/transaction/transaction-api/1.1/transaction-api-1.1.jar to ${jetty.base}/lib/gcloud/transaction-api-1.1.jar
|
||||
COPY : /Users/admin/.m2/repository/joda-time/joda-time/2.9.2/joda-time-2.9.2.jar to ${jetty.base}/lib/gcloud/joda-time-2.9.2.jar
|
||||
COPY : /Users/admin/.m2/repository/org/apache/httpcomponents/httpclient/4.0.1/httpclient-4.0.1.jar to ${jetty.base}/lib/gcloud/httpclient-4.0.1.jar
|
||||
COPY : /Users/admin/.m2/repository/org/apache/httpcomponents/httpcore/4.0.1/httpcore-4.0.1.jar to ${jetty.base}/lib/gcloud/httpcore-4.0.1.jar
|
||||
COPY : /Users/admin/.m2/repository/org/codehaus/jackson/jackson-core-asl/1.9.11/jackson-core-asl-1.9.11.jar to ${jetty.base}/lib/gcloud/jackson-core-asl-1.9.11.jar
|
||||
COPY : /Users/admin/.m2/repository/org/json/json/20151123/json-20151123.jar to ${jetty.base}/lib/gcloud/json-20151123.jar
|
||||
DOWNLD: http://central.maven.org/maven2/org/slf4j/jcl-over-slf4j/1.7.21/jcl-over-slf4j-1.7.21.jar to ${jetty.base}/lib/slf4j/jcl-over-slf4j-1.7.21.jar
|
||||
COPY : ${jetty.home}/modules/gcloud/index.yaml to ${jetty.base}/etc/index.yaml
|
||||
INFO : Base directory was modified
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
[[configuring-sessions-hazelcast]]
|
||||
|
||||
=== Clustered Session Management: Hazelcast
|
||||
=== Persistent Sessions: Hazelcast
|
||||
|
||||
==== Enabling Hazelcast Sessions
|
||||
|
||||
|
@ -24,10 +24,11 @@ When using the Jetty distribution, you will first need to enable the `session-st
|
|||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
mb-olamy:tmp-base olamy$ java -jar ../start.jar --create-startd
|
||||
$ java -jar ../start.jar --create-startd
|
||||
MKDIR : ${jetty.base}/start.d
|
||||
INFO : Base directory was modified
|
||||
mb-olamy:tmp-base olamy$ java -jar ../start.jar --add-to-start=session-store-hazelcast-remote
|
||||
|
||||
$ java -jar ../start.jar --add-to-start=session-store-hazelcast-remote
|
||||
|
||||
ALERT: There are enabled module(s) with licenses.
|
||||
The following 1 module(s):
|
||||
|
@ -44,11 +45,11 @@ Proceed (y/N)? y
|
|||
INFO : server transitively enabled, ini template available with --add-to-start=server
|
||||
INFO : sessions transitively enabled, ini template available with --add-to-start=sessions
|
||||
INFO : session-store-hazelcast-remote initialized in ${jetty.base}/start.d/session-store-hazelcast-remote.ini
|
||||
MKDIR : /Users/olamy/mvn-repo/com/hazelcast/hazelcast/3.8.2
|
||||
DOWNLD: http://central.maven.org/maven2/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar to /Users/olamy/mvn-repo/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar
|
||||
MKDIR : /Users/admin/mvn-repo/com/hazelcast/hazelcast/3.8.2
|
||||
DOWNLD: http://central.maven.org/maven2/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar to /Users/admin/mvn-repo/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar
|
||||
MKDIR : ${jetty.base}/lib/hazelcast
|
||||
COPY : /Users/olamy/mvn-repo/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar to ${jetty.base}/lib/hazelcast/hazelcast-3.8.2.jar
|
||||
COPY : /Users/olamy/mvn-repo/com/hazelcast/hazelcast-client/3.8.2/hazelcast-client-3.8.2.jar to ${jetty.base}/lib/hazelcast/hazelcast-client-3.8.2.jar
|
||||
COPY : /Users/admin/mvn-repo/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar to ${jetty.base}/lib/hazelcast/hazelcast-3.8.2.jar
|
||||
COPY : /Users/admin/mvn-repo/com/hazelcast/hazelcast-client/3.8.2/hazelcast-client-3.8.2.jar to ${jetty.base}/lib/hazelcast/hazelcast-client-3.8.2.jar
|
||||
INFO : Base directory was modified
|
||||
----
|
||||
|
||||
|
@ -112,10 +113,10 @@ To enable this you will first need to enable the `session-store-hazelcast-embedd
|
|||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
mb-olamy:tmp-base olamy$ java -jar ../start.jar --create-startd
|
||||
$ java -jar ../start.jar --create-startd
|
||||
MKDIR : ${jetty.base}/start.d
|
||||
INFO : Base directory was modified
|
||||
mb-olamy:tmp-base olamy$ java -jar ../start.jar --add-to-start=session-store-hazelcast-embedded
|
||||
$ java -jar ../start.jar --add-to-start=session-store-hazelcast-embedded
|
||||
|
||||
ALERT: There are enabled module(s) with licenses.
|
||||
The following 1 module(s):
|
||||
|
@ -132,11 +133,11 @@ Proceed (y/N)? y
|
|||
INFO : server transitively enabled, ini template available with --add-to-start=server
|
||||
INFO : sessions transitively enabled, ini template available with --add-to-start=sessions
|
||||
INFO : session-store-hazelcast-embedded initialized in ${jetty.base}/start.d/session-store-hazelcast-embedded.ini
|
||||
MKDIR : /Users/olamy/mvn-repo/com/hazelcast/hazelcast/3.8.2
|
||||
DOWNLD: http://central.maven.org/maven2/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar to /Users/olamy/mvn-repo/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar
|
||||
MKDIR : /Users/admin/mvn-repo/com/hazelcast/hazelcast/3.8.2
|
||||
DOWNLD: http://central.maven.org/maven2/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar to /Users/admin/mvn-repo/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar
|
||||
MKDIR : ${jetty.base}/lib/hazelcast
|
||||
COPY : /Users/olamy/mvn-repo/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar to ${jetty.base}/lib/hazelcast/hazelcast-3.8.2.jar
|
||||
COPY : /Users/olamy/mvn-repo/com/hazelcast/hazelcast-client/3.8.2/hazelcast-client-3.8.2.jar to ${jetty.base}/lib/hazelcast/hazelcast-client-3.8.2.jar
|
||||
COPY : /Users/admin/mvn-repo/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar to ${jetty.base}/lib/hazelcast/hazelcast-3.8.2.jar
|
||||
COPY : /Users/admin/mvn-repo/com/hazelcast/hazelcast-client/3.8.2/hazelcast-client-3.8.2.jar to ${jetty.base}/lib/hazelcast/hazelcast-client-3.8.2.jar
|
||||
----
|
||||
|
||||
Doing this enables the embedded Hazelcast Session module and any dependent modules or files needed for it to run on the server.
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.
|
||||
// ========================================================================
|
||||
|
||||
[[session-configuration-housekeeper]]
|
||||
=== The SessionIdManager and the Housekeeper
|
||||
|
||||
==== Default Settings
|
||||
By default, Jetty will instantiate a single instance of the `DefaultSessionIdManager` and `HouseKeeper` at startup with default settings.
|
||||
|
||||
The default settings are:
|
||||
|
||||
DefaultSessionIdManager: worker name::
|
||||
This uniquely identifies the jetty server instance within a cluster.
|
||||
It is set from the value of the `JETTY_WORKER_INSTANCE` environment variable, or `node0` if the environment value is not set.
|
||||
If you have more than one Jetty instance, it is *crucial* that you explicitly configure the worker name on each Jetty instance (see link:#session-idmanager-housekeeper-config[below] for how to configure).
|
||||
|
||||
HouseKeeper: scavenge interval::
|
||||
This is the period in seconds between runs of the session scavenger, and by default is set to the equivalent of 10 minutes.
|
||||
As a rule of thumb, you should ensure that the scavenge interval is shorter than the `maxInactiveInterval` of your sessions to ensure that they are promptly scavenged.
|
||||
See below for instructions on how to configure this.
|
||||
|
||||
[[session-idmanager-housekeeper-config]]
|
||||
==== Configuration
|
||||
To change the default values, use the link:#startup-modules[module system] to link:#startup-modules[enable] the `sessions` module.
|
||||
|
||||
This will enable the `$jetty.home/etc/sessions/id-manager.xml` file and generate a `$jetty.base/start.d/sessions.ini` file.
|
||||
|
||||
The `id-manager.xml` file instantiates a single `DefaultSessionIdManager` and `HouseKeeper` and configures them using the properties from the `sessions.ini` file.
|
||||
|
||||
Edit the ini file to change the properties to easily customize the `DefaultSessionIdManager` and `HouseKeeper`:
|
||||
|
||||
jetty.sessionIdManager.workerName::
|
||||
By default it is `node1`.
|
||||
This uniquely identifies the Jetty server instance within a cluster.
|
||||
If you have more than one Jetty instance, it is crucial that you configure the worker name differently on each jetty instance.
|
||||
|
||||
|
||||
jetty.sessionScavengeInterval.seconds::
|
||||
This is the period in seconds between runs of the session scavenger.
|
||||
By default it will run every 600 secs (ie 10 mins).
|
||||
As a rule of thumb, you should ensure that the scavenge interval is shorter than the maxInactiveInterval of your sessions to ensure that they are promptly scavenged.
|
|
@ -16,12 +16,17 @@
|
|||
|
||||
[[configuring-sessions-infinispan]]
|
||||
|
||||
=== Clustered Session Management: Inifinspan
|
||||
=== Persistent Sessions: Inifinspan
|
||||
|
||||
==== Enabling Infinispan Sessions
|
||||
|
||||
When using the Jetty distribution, you will first need to enable the `session-store-infinispan-remote` link:#startup-modules[module] for your link:#startup-base-and-home[Jetty base] using the `--add-to-start` argument on the command line.
|
||||
|
||||
____
|
||||
[IMPORTANT]
|
||||
If you are running Jetty with JDK 9 or greater, enable `session-store-infinispan-remote-910.mod` instead.
|
||||
____
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
$ java -jar ../start.jar --create-startd
|
||||
|
@ -106,6 +111,11 @@ ____
|
|||
During testing, it can be helpful to run an in-process instance of Infinispan.
|
||||
To enable this you will first need to enable the `session-store-infinispan-embedded` link:#startup-modules[module] for your link:#startup-base-and-home[Jetty base] using the `--add-to-start` argument on the command line.
|
||||
|
||||
____
|
||||
[IMPORTANT]
|
||||
If you are running Jetty with JDK 9 or greater, enable `session-store-infinispan-embedded-910.mod` instead.
|
||||
____
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
java -jar ../start.jar --add-to-start=session-store-infinispan-embedded
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
[[configuring-sessions-jdbc]]
|
||||
|
||||
=== Clustered Session Management: JDBC
|
||||
=== Persistent Sessions: JDBC
|
||||
|
||||
==== Enabling JDBC Sessions
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.
|
||||
// ========================================================================
|
||||
|
||||
[[session-configuration-memcachedsessiondatastore]]
|
||||
|
||||
=== Persistent Sessions: The L2 Session Data Cache
|
||||
|
||||
If your chosen persistence technology is slow, it can be helpful to locally cache the session data.
|
||||
The `CachingSessionDataStore` is a special type of `SessionDataStore` that locally caches session data, which makes reads faster. It writes-through to your chosen type of `SessionDataStore` when session data changes.
|
||||
|
||||
==== MemcachedSessionDataMap
|
||||
|
||||
The `MemcachedSessionDataMap` uses `memcached` to perform caching.
|
||||
|
||||
To enable it with the Jetty distribution, enable the `session-store-cache` link:#startup-modules[module], along with your chosen `session-store-xxxx` module, and optionally the `session-cache-hash` or `session-cache-null` modules.
|
||||
|
||||
After enabling, the `$jetty.base/start.d/session-store-cache.ini` file will be generated:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
--module=session-store-cache
|
||||
|
||||
|
||||
## Session Data Cache type: xmemcached
|
||||
session-data-cache=xmemcached
|
||||
#jetty.session.memcached.host=localhost
|
||||
#jetty.session.memcached.port=11211
|
||||
#jetty.session.memcached.expirySec=
|
||||
#jetty.session.memcached.heartbeats=true
|
||||
----
|
||||
|
||||
|
||||
The configuration properties are:
|
||||
|
||||
jetty.session.memcached.host::
|
||||
Default value is `localhost`.
|
||||
This is the host on which the memcached server resides.
|
||||
|
||||
jetty.session.memcached.port::
|
||||
Default value is `11211`.
|
||||
This is the port on which the memcached server is listening.
|
||||
|
||||
jetty.session.memcached.expirySec::
|
||||
Default value `0`.
|
||||
This is the length of time in seconds that an item can remain in the memcached cache, where 0 indicates indefinitely.
|
||||
|
||||
jetty.session.memcached.heartbeats::
|
||||
Default value `true`.
|
||||
Whether or not the memcached system should generate heartbeats.
|
|
@ -16,21 +16,16 @@
|
|||
|
||||
[[configuring-sessions-memory]]
|
||||
|
||||
=== Non-Clustered Session Management: Memory
|
||||
=== Non-Persistent Sessions
|
||||
|
||||
Non-clustered, in-memory-only is the default style of Session Management.
|
||||
In previous versions of Jetty this was referred to as "hash" sessions, as they were stored in a `HashMap` in memory.
|
||||
When using the Jetty distribution, if you do not configure any session module, this will be enabled by default.
|
||||
Non-clustered, non-persistent, in-memory-only is the default style of session management.
|
||||
In previous versions of Jetty this was referred to as "hash" sessions, as they were stored in a `HashMap` in memory.
|
||||
|
||||
Specifically, Jetty will hook up:
|
||||
This is delivered by a combination of the `DefaultSessionCache` (to keep sessions in memory) and a `NullSessionDataStore` (to avoid session persistence).
|
||||
|
||||
A `DefaultSessionIdManager`::
|
||||
* Produces unique session ids and supports cross-context dispatch re-use of session ids
|
||||
A `HouseKeeper`::
|
||||
* Scavenges for expired sessions every 10 mins
|
||||
A `DefaultSessionCache` per context::
|
||||
* Keeps session objects in memory
|
||||
A `NullSessionDataStore` per context::
|
||||
* No persistence of sessions
|
||||
If you do nothing, Jetty will instantiate one of each of these objects for each context at startup time using hard-coded defaults.
|
||||
|
||||
If you wish to change any of the default configuration, enable the `session-cache-hash` module.
|
||||
To explicitly set up non-persisted sessions using modules, use both the `session-cache-hash` and the `session-store-null` modules.
|
||||
|
||||
Enabling the modules allows you to configure behavior - see link:#session-configuration-sessioncache[the L1 Session Cache] for detailed information on configuration options for the `DefaultSessionCache`.
|
||||
The `NullSessionDataStore` has no customizable options.
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
[[configuring-sessions-mongo]]
|
||||
|
||||
=== Clustered Session Management: MongoDB
|
||||
=== Persistent Sessions: MongoDB
|
||||
|
||||
==== Enabling MongoDB Sessions
|
||||
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.
|
||||
// ========================================================================
|
||||
|
||||
[[session-configuration-sessioncache]]
|
||||
=== The L1 Session Cache
|
||||
|
||||
==== The DefaultSessionCache
|
||||
|
||||
In the absence of any explicit configuration, Jetty will instantiate an instance of the `DefaultSessionCache` per context.
|
||||
If you wish to change any of the default values, you need to enable the `session-cache-hash` link:#startup-modules[module].
|
||||
|
||||
Once the `session-cache-hash` module has been enabled, you can view a list of all the configurable values by opening `start.d/session-cache-hash.ini`:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
--module=session-cache-hash
|
||||
|
||||
#jetty.session.evictionPolicy=-1
|
||||
#jetty.session.saveOnInactiveEvict=false
|
||||
#jetty.session.saveOnCreate=false
|
||||
#jetty.session.removeUnloadableSessions=false
|
||||
----
|
||||
|
||||
jetty.session.evictionPolicy::
|
||||
Integer.
|
||||
Controls whether session objects that are held in memory are subject to eviction from the memory cache.
|
||||
Evicting sessions can reduce the memory footprint of the cache.
|
||||
Eviction is usually used in conjunction with a `SessionDataStore` that persists sessions.
|
||||
Values are:
|
||||
* -1 : sessions are never evicted from the cache
|
||||
* 0 : sessions are evicted from the cache as soon as the last active request for it finishes
|
||||
* >= 1 : any positive number is the time in seconds after which a session that is in the cache but has not experienced any activity will be evicted
|
||||
|
||||
____
|
||||
[NOTE]
|
||||
If you are not using a `SessionDataStore` that persists sessions, be aware that evicted sessions will be lost.
|
||||
____
|
||||
|
||||
jetty.session.saveOnInactiveEvict::
|
||||
Boolean, default `false`.
|
||||
Controls whether a session will be saved to the `SessionDataStore` just prior to its eviction.
|
||||
|
||||
jetty.session.saveOnCreate::
|
||||
Boolean, default `false`.
|
||||
Controls whether a session that is newly created will be immediately saved to the `SessionDataStore` or lazily saved as the last request for the session exits.
|
||||
|
||||
jetty.session.removeUnloadableSessions::
|
||||
Boolean, default `false`.
|
||||
Controls whether a session that cannot be restored - for example because it is corrupted - from the `SessionDataStore` is deleted by the `SessionDataStore`.
|
||||
|
||||
For more general information on the uses of these configuration properties, see link:#sessions-details[Session Components].
|
||||
|
||||
|
||||
==== The NullSessionCache
|
||||
|
||||
The `NullSessionCache` is a trivial implementation of the `SessionCache` that does not cache any session information.
|
||||
You may need to use it if your clustering setup does not have a sticky load balancer, or if you want absolutely minimal support for sessions.
|
||||
If you use this in conjunction with the `NullSessionDataStore`, then sessions will neither be retained in memory nor persisted.
|
||||
|
||||
To enable the `NullSessionCache`, enable the `sesssion-cache-null` link:#startup-modules[module].
|
|
@ -49,8 +49,11 @@ image::images/SessionsHierarchy.png[]
|
|||
|
||||
==== Configuring Sessions in the Jetty Distribution
|
||||
|
||||
Jetty provides support for several different Session Management technologies.
|
||||
Both link:#configuring-sessions-file-system[local file storage] and in-memory session management can be implemented for standard implementations.
|
||||
For implementations using clustered technologies, link:#configuring-sessions-jdbc[JDBC], link:#configuring-sessions-mongo[MongoDB], link:#configuring-sessions-infinispan[Inifinispan] and link:#configuring-sessions-gcloud[Google Cloud Datastore] are all supported.
|
||||
Setting up these technologies is as easy as enabling it's link:#startup-modules[module] and editing it's associated ini file with any usernames, passwords or changes you need to make for your instance.
|
||||
The following sections will cover how exactly to enable the required modules as well as an overview of what options are available for customization.
|
||||
Configuring session management involves selecting a link:#startup-modules[module] for the desired type of link:#session-configuration-sessioncache[session caching] behavior, and a module for the type of session persistence.
|
||||
|
||||
Jetty provides two different session caches: the `DefaultSessionCache` which holds sessions in memory, and the `NullSessionCache` which does not.
|
||||
There is more information on both of these types of session caching and the circumstances which would lead you to select one or the other in the link:#sessions-details[Session Components] section, and more information on the configuration options of each in link:#session-configuration-sessioncache[the L1 Session Cache] section.
|
||||
|
||||
For session persistence, Jetty provides a number of different implementations from which to choose including link:#configuring-sessions-memory[non-persistence], link:#configuring-sessions-file-system[local file storage], clustered technologies such as link:#configuring-sessions-jdbc[JDBC], link:#configuring-sessions-mongo[MongoDB], link:#configuring-sessions-infinispan[Inifinispan], link:#configuring-sessions-gcloud[Google Cloud Datastore], and link:#configuring-sessions-hazelcast[Hazelcast].
|
||||
|
||||
Depending on your persistence technology, to enhance performance, you may want to use an L2 cache for session data, in which case Jetty provides the link:#session-configuration-memcachedsessiondatastore[memcached L2 session data cache].
|
||||
|
|
|
@ -15,40 +15,43 @@
|
|||
// ========================================================================
|
||||
|
||||
[[sessions-details]]
|
||||
=== Session Configuration and Use Cases
|
||||
=== Session Components
|
||||
|
||||
==== Configuration
|
||||
==== SessionIdManager
|
||||
|
||||
===== SessionIdManager
|
||||
|
||||
There is a maximum of 1 `SessionIdManager` per jetty Server instance.
|
||||
There is a maximum of one (1) `SessionIdManager` per Jetty Server instance.
|
||||
Its purpose is to generate fresh, unique session ids and to coordinate the re-use of session ids amongst co-operating contexts.
|
||||
|
||||
Unlike in previous versions of Jetty, the `SessionIdManager` is agnostic with respect to the type of clustering technology chosen.
|
||||
|
||||
Jetty provides a default implementation - the `DefaultSessionIdManager` - which should meet the needs of most users.
|
||||
If you do not explicitly enable one of the session modules, or otherwise configure a `SessionIdManager`, the `DefaultSessionIdManager` will be used.
|
||||
If you do not explicitly enable one of the session modules or otherwise configure a `SessionIdManager`, the `DefaultSessionIdManager` will be used.
|
||||
|
||||
If the `DefaultSessionIdManager` does not meet your needs, you can extend the `org.eclipse.jetty.server.session.AbstractSessionIdManager` or do a fresh implementation of the `org.eclipse.jetty.server.session.SessionIdManager` interface.
|
||||
|
||||
===== HouseKeeper
|
||||
See link:#session-configuration-housekeeper[Configuring the SessionIdManager and HouseKeeper] for details on configuration.
|
||||
|
||||
There is a maximum of 1 `HouseKeeper` per `SessionIdManager`.
|
||||
==== HouseKeeper
|
||||
|
||||
There is a maximum of one (1) `HouseKeeper` per `SessionIdManager`.
|
||||
Its purpose is to periodically poll the `SessionHandlers` to clean out expired sessions.
|
||||
|
||||
By default the `HouseKeeper` will poll the `SessionHandlers` every 10 mins to find and delete expired sessions, although this interval is configurable.
|
||||
|
||||
See link:#session-configuration-housekeeper[Configuring the SessionIdManager and HouseKeeper] for details on configuration.
|
||||
|
||||
===== SessionCache
|
||||
|
||||
There is 1 `SessionCache` per context.
|
||||
==== SessionCache
|
||||
|
||||
There is one (1) `SessionCache` *per context.*
|
||||
Its purpose is to provide an L1 cache of Session objects.
|
||||
Having a working set of Session objects in memory allows multiple simultaneous requests for the same session to share the same Session object.
|
||||
|
||||
Jetty provides 2 `SessionCache` implementations: the `DefaultSessionCache` and the `NullSessionCache`.
|
||||
The `DefaultSessionCache` retains Session objects in memory in a cache and has a number of configuration options to control cache behavior.
|
||||
Jetty provides two (2) `SessionCache` implementations: the `DefaultSessionCache` and the `NullSessionCache`.
|
||||
The `DefaultSessionCache` retains Session objects in memory in a cache and has a number of link:#session-configuration-sessioncache[configuration options] to control cache behavior.
|
||||
It is the default that is used if no other `SessionCache` has been configured.
|
||||
It is suitable for non-clustered and clustered deployments with a sticky load balancer, as well as clustered deployments with a non-sticky load balancer, with some caveats.
|
||||
|
||||
The `NullSessionCache` does not actually cache any objects: each request uses a fresh Session object.
|
||||
It is suitable for clustered deployments without a sticky load balancer and non-clustered deployments when purely minimal support for sessions is needed.
|
||||
|
||||
|
@ -58,16 +61,19 @@ They can also be configured to do an immediate, eager write of a freshly created
|
|||
This can be useful if you are likely to experience multiple, near simultaneous requests referencing the same session, e.g. with HTTP/2 and you don't have a sticky load balancer.
|
||||
Alternatively, if the eager write is not done, application paths which create and then invalidate a session within a single request never incur the cost of writing to persistent storage.
|
||||
|
||||
Additionally, if the `EVICT_ON_INACTIVITY` eviction policy is in use, you can configure the `DefaultSessionCache` to force a write of the Session to the SessionDataStore just before the Session is evicted.
|
||||
Additionally, if the `EVICT_ON_INACTIVITY` eviction policy is in use, you can link:#session-configuration-sessioncache[configure] the `DefaultSessionCache` to force a write of the Session to the `SessionDataStore` just before the Session is evicted.
|
||||
|
||||
===== SessionDataStore
|
||||
See link:#session-configuration-sessioncache[the L1 Session Cache] for more information.
|
||||
|
||||
There is 1 `SessionDataStore` per context. Its purpose is to handle all persistence related operations on sessions.
|
||||
==== SessionDataStore
|
||||
|
||||
There is one (1) `SessionDataStore` per context.
|
||||
Its purpose is to handle all persistence related operations on sessions.
|
||||
|
||||
The common characteristics for all `SessionDataStores` are whether or not they support passivation, and the length of the grace period.
|
||||
|
||||
Supporting passivation means that session data is serialized.
|
||||
Some persistence mechanisms serialize, such as JDBC, GCloud Datastore etc, whereas others may store an object in shared memory eg Infinispan when configured with a local cache.
|
||||
Some persistence mechanisms serialize, such as JDBC, GCloud Datastore etc, whereas others may store an object in shared memory, e.g. Infinispan, when configured with a local cache.
|
||||
|
||||
Whether or not a clustering technology entails passivation controls whether or not the session passivation/activation listeners will be called.
|
||||
|
||||
|
@ -79,56 +85,17 @@ When `SessionDataStores` search their persistent store to find sessions that hav
|
|||
* The second finds sessions in the store that have expired which were last live on the current node
|
||||
* The third finds sessions that expired a "while" ago, irrespective of on which node they were last used: the definition of "a while" is based on the grace period.
|
||||
|
||||
Jetty instantiates the trivial `NullSessionDataStore` - which does not persist sessions - as the default.
|
||||
|
||||
===== CachingSessionDataStore
|
||||
The distribution provides a number of alternative `SessionDataStore` implementations such as link:#configuring-sessions-file-system[FileSessionDataStore], link:#configuring-sessions-gcloud[GCloudSessionDataStore], link:#configuring-sessions-jdbc[JDBCSessionDataStore], link:#configuring-sessions-mongodb[MongoSessionDataStore], link:#configuring-sessions-infinispan[InfinispanSessionDataStore], link:#configuring-sessions-hazelcast[HazelcastSessionDataStore].
|
||||
|
||||
The `CachingSessionDataStore` is a special type of `SessionDataStore` that inserts an L2 cache of SessionData - the `SessionDataMap` - in front of a delegate `SessionDataStore`.
|
||||
|
||||
==== CachingSessionDataStore
|
||||
|
||||
The `CachingSessionDataStore` is a special type of `SessionDataStore` that inserts an L2 cache of Session data - the `SessionDataMap` - in front of a delegate `SessionDataStore`.
|
||||
The `SessionDataMap` is preferentially consulted before the actual SessionDataStore on reads.
|
||||
This can improve the performance of slow stores.
|
||||
|
||||
Jetty provides one implementation of the this L2 cache based on `Memcached`, the `MemcachedSessionDataMap`.
|
||||
|
||||
|
||||
==== Use Cases
|
||||
|
||||
===== Clustering with a Sticky Load Balancer
|
||||
|
||||
Preferably, your cluster will utilize a sticky load balancer.
|
||||
This will route requests for the same session to the same Jetty instance.
|
||||
In this case, the `DefaultSessionCache` can be used to keep in-use Session objects in memory.
|
||||
You can fine-tune the cache by controlling how long Session objects remain in memory with the eviction policy settings.
|
||||
|
||||
If you have a large number of Sessions or very large Session objects, then you might want to manage your memory allocation by controlling the amount of time Session objects spend in the cache.
|
||||
The `EVICT_ON_SESSION_EXIT` eviction policy will remove a Session object from the cache as soon as the last simultaneous request referencing it exits.
|
||||
Alternatively, the `EVICT_ON_INACTIVITY` policy will remove a Session object from the cache after a configurable amount of time has passed without a request referencing it.
|
||||
|
||||
If your Sessions are very long lived and infrequently referenced, you might use the `EVICT_ON_INACTIVITY_POLICY` to control the size of the cache.
|
||||
|
||||
If your Sessions are small, or relatively few or stable in number or they are read-mostly, then you might select the `NEVER_EVICT` policy.
|
||||
With this policy, Session objects will remain in the cache until they either expire or are explicitly invalidated.
|
||||
|
||||
If you have a high likelihood of simultaneous requests for the same session object, then the `EVICT_ON_SESSION_EXIT` policy will ensure the Session object stays in the cache as long as it is needed.
|
||||
|
||||
|
||||
===== Clustering without a Sticky Load Balancer
|
||||
|
||||
Without a sticky load balancer requests for the same session may arrive on any node in the cluster.
|
||||
This means it is likely that the copy of the Session object in any `SessionCache` is likely to be out-of-date, as the Session was probably last accessed on a different node.
|
||||
In this case, your `choices` are to use either the `NullSessionCache` or to de-tuned the `DefaultSessionCache`.
|
||||
If you use the NullSessionCache all Session object caching is avoided.
|
||||
This means that every time a request references a session it must be brought in from persistent storage.
|
||||
It also means that there can be no sharing of Session objects for multiple requests for the same session: each will have their own Session object.
|
||||
Furthermore, the outcome of session writes are indeterminate because the Servlet Specification does not mandate ACID transactions for sessions.
|
||||
|
||||
If you use the `DefaultSessionCache`, there is a risk that the caches on some nodes will contain out-of-date session information as simultaneous requests for the same session are scattered over the cluster.
|
||||
To mitigate this somewhat you can use the `EVICT_ON_SESSION_EXIT` eviction policy: this will ensure that the Session is removed from the cache as soon as the last simultaneous request for it exits.
|
||||
Again, due to the lack of session transactionality, the ordering outcome of write operations cannot be guaranteed.
|
||||
As the Session is cached while at least one request is accessing it, it is possible for multiple simultaneous requests to share the same Session object.
|
||||
|
||||
|
||||
===== Handling corrupted or unloadable session data
|
||||
|
||||
For various reasons it might not be possible for the SessionDataStore to re-read a stored session.
|
||||
One scenario is that the session stores a serialized object in it's attributes, and after a redeployment there in an incompatible class change.
|
||||
Using the setter `SessionCache.setRemoveUnloadableSessions(true)` will allow the `SessionDataStore` to delete the unreadable session from persistent storage.
|
||||
This can be useful from preventing the scavenger from continually generating errors on the same expired, but un-restorable, session.
|
||||
See link:#session-configuration-memcachedsessiondatastore[the L2 SessionData Cache]for additional information.
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.
|
||||
// ========================================================================
|
||||
|
||||
[[sessions-usecases]]
|
||||
=== Use Cases
|
||||
|
||||
==== Clustering with a Sticky Load Balancer
|
||||
|
||||
Preferably, your cluster will utilize a sticky load balancer.
|
||||
This will route requests for the same Session to the same Jetty instance.
|
||||
In this case, the `DefaultSessionCache` can be used to keep in-use Session objects in memory.
|
||||
You can fine-tune the cache by controlling how long Session objects remain in memory with the eviction policy settings.
|
||||
|
||||
If you have a large number of Sessions or very large Session objects, then you may want to manage your memory allocation by controlling the amount of time Session objects spend in the cache.
|
||||
The `EVICT_ON_SESSION_EXIT` eviction policy will remove a Session object from the cache as soon as the last simultaneous request referencing it exits.
|
||||
Alternatively, the `EVICT_ON_INACTIVITY` policy will remove a Session object from the cache after a configurable amount of time has passed without a request referencing it.
|
||||
|
||||
If your Sessions are very long lived and infrequently referenced, you might use the `EVICT_ON_INACTIVITY_POLICY` to control the size of the cache.
|
||||
|
||||
If your Sessions are small, or relatively few or stable in number or they are read-mostly, then you might select the `NEVER_EVICT` policy.
|
||||
With this policy, Session objects will remain in the cache until they either expire or are explicitly invalidated.
|
||||
|
||||
If you have a high likelihood of simultaneous requests for the same session object, then the `EVICT_ON_SESSION_EXIT` policy will ensure the Session object stays in the cache as long as it is needed.
|
||||
|
||||
|
||||
==== Clustering Without a Sticky Load Balancer
|
||||
|
||||
Without a sticky load balancer requests for the same session may arrive on any node in the cluster.
|
||||
This means it is likely that the copy of the Session object in any `SessionCache` is likely to be out-of-date, as the Session was probably last accessed on a different node.
|
||||
In this case, your `choices` are to use either the `NullSessionCache` or to de-tune the `DefaultSessionCache`.
|
||||
If you use the NullSessionCache all Session object caching is avoided.
|
||||
This means that every time a request references a session it must be brought in from persistent storage.
|
||||
It also means that there can be no sharing of Session objects for multiple requests for the same session: each will have their own Session object.
|
||||
Furthermore, the outcome of session writes are indeterminate because the Servlet Specification does not mandate ACID transactions for sessions.
|
||||
|
||||
If you use the `DefaultSessionCache`, there is a risk that the caches on some nodes will contain out-of-date Session information as simultaneous requests for the same session are scattered over the cluster.
|
||||
To mitigate this somewhat you can use the `EVICT_ON_SESSION_EXIT` eviction policy: this will ensure that the Session is removed from the cache as soon as the last simultaneous request for it exits.
|
||||
Again, due to the lack of Session transactionality, the ordering outcome of write operations cannot be guaranteed.
|
||||
As the Session is cached while at least one request is accessing it, it is possible for multiple simultaneous requests to share the same Session object.
|
||||
|
||||
|
||||
==== Handling corrupted or unloadable session data
|
||||
|
||||
For various reasons it might not be possible for the `SessionDataStore` to re-read a stored session.
|
||||
One scenario is that the session stores a serialized object in it's attributes, and after a redeployment there in an incompatible class change.
|
||||
Using the setter `SessionCache.setRemoveUnloadableSessions(true)` will allow the `SessionDataStore` to delete the unreadable session from persistent storage.
|
||||
This can be useful from preventing the scavenger from continually generating errors on the same expired, but un-restorable, session.
|
|
@ -115,8 +115,8 @@ Jetty Environment:
|
|||
Config Search Order:
|
||||
--------------------
|
||||
<command-line>
|
||||
${jetty.base} -> /home/user/jetty-distribution-9.4.1.v20170120/demo-base
|
||||
${jetty.home} -> /home/user/Desktop/jetty-distribution-9.4.1.v20170120
|
||||
${jetty.base} -> /home/user/jetty-distribution-{VERSION}/demo-base
|
||||
${jetty.home} -> /home/user/Desktop/jetty-distribution-{VERSION}
|
||||
|
||||
JVM Arguments:
|
||||
--------------
|
||||
|
@ -152,26 +152,26 @@ Jetty Server Classpath:
|
|||
Version Information on 42 entries in the classpath.
|
||||
Note: order presented here is how they would appear on the classpath.
|
||||
changes to the --module=name command line options will be reflected here.
|
||||
0: {VERSION} | ${jetty.home}/lib/jetty-client-{VERSION}.jar
|
||||
0: {VERSION} | ${jetty.home}/lib/jetty-client-{VERSION}.jar
|
||||
1: 1.4.1.v201005082020 | ${jetty.base}/lib/ext/javax.mail.glassfish-1.4.1.v201005082020.jar
|
||||
2: {VERSION} | ${jetty.base}/lib/ext/test-mock-resources-{VERSION}.jar
|
||||
2: {VERSION} | ${jetty.base}/lib/ext/test-mock-resources-{VERSION}.jar
|
||||
3: (dir) | ${jetty.home}/resources
|
||||
4: 3.1.0 | ${jetty.home}/lib/servlet-api-3.1.jar
|
||||
5: 3.1.RC0 | ${jetty.home}/lib/jetty-schemas-3.1.jar
|
||||
6: {VERSION} | ${jetty.home}/lib/jetty-http-{VERSION}.jar
|
||||
7: {VERSION} | ${jetty.home}/lib/jetty-continuation-{VERSION}.jar
|
||||
8: {VERSION} | ${jetty.home}/lib/jetty-server-{VERSION}.jar
|
||||
9: {VERSION} | ${jetty.home}/lib/jetty-xml-{VERSION}.jar
|
||||
10: {VERSION} | ${jetty.home}/lib/jetty-util-{VERSION}.jar
|
||||
11: {VERSION} | ${jetty.home}/lib/jetty-io-{VERSION}.jar
|
||||
12: {VERSION} | ${jetty.home}/lib/jetty-jaas-{VERSION}.jar
|
||||
13: {VERSION} | ${jetty.home}/lib/jetty-jndi-{VERSION}.jar
|
||||
6: {VERSION} | ${jetty.home}/lib/jetty-http-{VERSION}.jar
|
||||
7: {VERSION} | ${jetty.home}/lib/jetty-continuation-{VERSION}.jar
|
||||
8: {VERSION} | ${jetty.home}/lib/jetty-server-{VERSION}.jar
|
||||
9: {VERSION} | ${jetty.home}/lib/jetty-xml-{VERSION}.jar
|
||||
10: {VERSION} | ${jetty.home}/lib/jetty-util-{VERSION}.jar
|
||||
11: {VERSION} | ${jetty.home}/lib/jetty-io-{VERSION}.jar
|
||||
12: {VERSION} | ${jetty.home}/lib/jetty-jaas-{VERSION}.jar
|
||||
13: {VERSION} | ${jetty.home}/lib/jetty-jndi-{VERSION}.jar
|
||||
14: 1.1.0.v201105071233 | ${jetty.home}/lib/jndi/javax.activation-1.1.0.v201105071233.jar
|
||||
15: 1.4.1.v201005082020 | ${jetty.home}/lib/jndi/javax.mail.glassfish-1.4.1.v201005082020.jar
|
||||
16: 1.2 | ${jetty.home}/lib/jndi/javax.transaction-api-1.2.jar
|
||||
17: {VERSION} | ${jetty.home}/lib/jetty-rewrite-{VERSION}.jar
|
||||
18: {VERSION} | ${jetty.home}/lib/jetty-security-{VERSION}.jar
|
||||
19: {VERSION} | ${jetty.home}/lib/jetty-servlet-{VERSION}.jar
|
||||
17: {VERSION} | ${jetty.home}/lib/jetty-rewrite-{VERSION}.jar
|
||||
18: {VERSION} | ${jetty.home}/lib/jetty-security-{VERSION}.jar
|
||||
19: {VERSION} | ${jetty.home}/lib/jetty-servlet-{VERSION}.jar
|
||||
20: 3.0.0 | ${jetty.home}/lib/jsp/javax.el-3.0.0.jar
|
||||
21: 1.2.0.v201105211821 | ${jetty.home}/lib/jsp/javax.servlet.jsp.jstl-1.2.0.v201105211821.jar
|
||||
22: 2.3.2 | ${jetty.home}/lib/jsp/javax.servlet.jsp-2.3.2.jar
|
||||
|
@ -179,21 +179,21 @@ Note: order presented here is how they would appear on the classpath.
|
|||
24: 2.3.3 | ${jetty.home}/lib/jsp/jetty-jsp-jdt-2.3.3.jar
|
||||
25: 1.2.0.v201112081803 | ${jetty.home}/lib/jsp/org.apache.taglibs.standard.glassfish-1.2.0.v201112081803.jar
|
||||
26: 3.8.2.v20130121-145325 | ${jetty.home}/lib/jsp/org.eclipse.jdt.core-3.8.2.v20130121.jar
|
||||
27: {VERSION} | ${jetty.home}/lib/jetty-plus-{VERSION}.jar
|
||||
28: {VERSION} | ${jetty.home}/lib/jetty-webapp-{VERSION}.jar
|
||||
29: {VERSION} | ${jetty.home}/lib/jetty-annotations-{VERSION}.jar
|
||||
27: {VERSION} | ${jetty.home}/lib/jetty-plus-{VERSION}.jar
|
||||
28: {VERSION} | ${jetty.home}/lib/jetty-webapp-{VERSION}.jar
|
||||
29: {VERSION} | ${jetty.home}/lib/jetty-annotations-{VERSION}.jar
|
||||
30: 4.1 | ${jetty.home}/lib/annotations/asm-4.1.jar
|
||||
31: 4.1 | ${jetty.home}/lib/annotations/asm-commons-4.1.jar
|
||||
32: 1.2 | ${jetty.home}/lib/annotations/javax.annotation-api-1.2.jar
|
||||
33: {VERSION} | ${jetty.home}/lib/jetty-deploy-{VERSION}.jar
|
||||
33: {VERSION} | ${jetty.home}/lib/jetty-deploy-{VERSION}.jar
|
||||
34: 1.0 | ${jetty.home}/lib/websocket/javax.websocket-api-1.0.jar
|
||||
35: {VERSION} | ${jetty.home}/lib/websocket/javax-websocket-client-impl-{VERSION}.jar
|
||||
36: {VERSION} | ${jetty.home}/lib/websocket/javax-websocket-server-impl-{VERSION}.jar
|
||||
37: {VERSION} | ${jetty.home}/lib/websocket/websocket-api-{VERSION}.jar
|
||||
38: {VERSION} | ${jetty.home}/lib/websocket/websocket-client-{VERSION}.jar
|
||||
39: {VERSION} | ${jetty.home}/lib/websocket/websocket-common-{VERSION}.jar
|
||||
40: {VERSION} | ${jetty.home}/lib/websocket/websocket-server-{VERSION}.jar
|
||||
41: {VERSION} | ${jetty.home}/lib/websocket/websocket-servlet-{VERSION}.jar
|
||||
35: {VERSION} | ${jetty.home}/lib/websocket/javax-websocket-client-impl-{VERSION}.jar
|
||||
36: {VERSION} | ${jetty.home}/lib/websocket/javax-websocket-server-impl-{VERSION}.jar
|
||||
37: {VERSION} | ${jetty.home}/lib/websocket/websocket-api-{VERSION}.jar
|
||||
38: {VERSION} | ${jetty.home}/lib/websocket/websocket-client-{VERSION}.jar
|
||||
39: {VERSION} | ${jetty.home}/lib/websocket/websocket-common-{VERSION}.jar
|
||||
40: {VERSION} | ${jetty.home}/lib/websocket/websocket-server-{VERSION}.jar
|
||||
41: {VERSION} | ${jetty.home}/lib/websocket/websocket-servlet-{VERSION}.jar
|
||||
|
||||
Jetty Active XMLs:
|
||||
------------------
|
||||
|
|
|
@ -17,57 +17,39 @@
|
|||
[[garbage-collection]]
|
||||
=== Garbage Collection
|
||||
|
||||
Tuning the JVM garbage collection (GC) can greatly improve Jetty performance.
|
||||
Specifically, you can avoid pauses while the system performs full garbage collections.
|
||||
Optimal tuning of the GC depends on the behavior of the application and requires detailed analysis, however there are general recommendations.
|
||||
Tuning the JVM garbage collection (GC) can greatly improve the performance of the JVM where Jetty and your application are running.
|
||||
Optimal tuning of the GC depends on the behavior of the application(s) and requires detailed analysis, but there are general recommendations to follow to at least obtain comprehensive GC logs that can be later analyzed.
|
||||
|
||||
See official https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/[Java 8 Garbage Collection documentation] for further assistance.
|
||||
See official https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/[Java 8] and https://docs.oracle.com/javase/9/gctuning/introduction-garbage-collection-tuning.htm[Java 9] Garbage Collection documentation for further assistance.
|
||||
|
||||
[[tuning-examples]]
|
||||
==== Tuning Examples
|
||||
[[garbage-collection-logging-configuration]]
|
||||
==== Garbage Collection Logging Configuration
|
||||
|
||||
These options are general to the Oracle JVM, and work in a Java 8 installation.
|
||||
They provide good information about how your JVM is running; based on that initial information, you can then tune more finely.
|
||||
|
||||
The most important thing you can do for yourself when considering GC is to capture the GC activity so that you can analyze what is happening and how often it happens.
|
||||
These options are general to OpenJDK (and therefore also for the Oracle JVM).
|
||||
They provide good information about the GC activity of your JVM, producing logs that can later be analyzed to perform finer tuning.
|
||||
|
||||
.JDK 8 Garbage Collection Logging Configuration
|
||||
[source,screen, subs="{sub-order}"]
|
||||
....
|
||||
-verbose:gc
|
||||
-Xloggc:/path/to/myjettybase/logs/gc.log
|
||||
-XX:+PrintGCDateStamps
|
||||
-XX:+PrintGCTimeStamps
|
||||
-XX:+PrintGCDetails
|
||||
-XX:+PrintTenuringDistribution
|
||||
-XX:+PrintCommandLineFlags
|
||||
-XX:+ParallelRefProcEnabled
|
||||
-XX:+PrintReferenceGC
|
||||
-XX:+PrintTenuringDistribution
|
||||
-XX:+PrintAdaptiveSizePolicy
|
||||
....
|
||||
|
||||
There are not many recommended options for GC that can apply to nearly all users.
|
||||
.JDK 9 Garbage Collection Logging Configuration
|
||||
[source,screen, subs="{sub-order}"]
|
||||
....
|
||||
Xlog:gc*,ergo*=trace,ref*=debug,age*=trace:file=/path/to/myjettybase/logs/gc.log:time,level,tags
|
||||
....
|
||||
|
||||
There are not many recommended options for GC that can apply to all users.
|
||||
However, the most obvious one is to disable explicit GC (this is performed regularly by RMI and can introduce an abnormal amount of GC pauses).
|
||||
|
||||
[source,screen, subs="{sub-order}"]
|
||||
....
|
||||
-XX:+DisableExplicitGC
|
||||
....
|
||||
|
||||
Before you apply any other GC tuning options, monitor your GC logs to see if https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html[tuning the CMS] makes sense for your environment.
|
||||
|
||||
A common setup for those just starting out with GC tuning is included below for reference.
|
||||
|
||||
____
|
||||
[CAUTION]
|
||||
This example configuration below could have a negative effect on your application performance, so continued monitoring of your GC log before and after is highly recommended to know if the configuration was beneficial or not.
|
||||
____
|
||||
|
||||
[source,screen, subs="{sub-order}"]
|
||||
....
|
||||
-XX:MaxGCPauseMillis=250
|
||||
-XX:+UseConcMarkSweepGC
|
||||
-XX:ParallelCMSThreads=2
|
||||
-XX:+CMSClassUnloadingEnabled
|
||||
-XX:+UseCMSCompactAtFullCollection
|
||||
-XX:CMSInitiatingOccupancyFraction=80
|
||||
....
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
This document provides an overview of how to configure SSL and TLS for Jetty.
|
||||
|
||||
[[configuring-jetty-for-ssl]]
|
||||
===== Configuring Jetty for SSL
|
||||
==== Configuring Jetty for SSL
|
||||
|
||||
To configure Jetty for SSL, complete the tasks in the following sections:
|
||||
|
||||
|
|
|
@ -60,8 +60,8 @@ Unfortunately this approach denies all aliases, including symbolic links, which
|
|||
==== Serving Aliases and Symbolic Links
|
||||
|
||||
Not all aliases are bad nor should be seen as attempts to subvert security constraints.
|
||||
Specifically symbolic links can be very useful when assembling complex web applications, yet by default Jetty will not serve them.
|
||||
Thus Jetty contexts support an extensible `AliasCheck` mechanism to allow aliases resources to be inspected an conditionally served.
|
||||
Specifically, symbolic links can be very useful when assembling complex web applications.
|
||||
As such, Jetty contexts support an extensible `AliasCheck` mechanism to allow aliases resources to be inspected and conditionally served.
|
||||
In this way, "good" aliases can be detected and served.
|
||||
Jetty provides several utility implementations of the `AliasCheck` interface as nested classes with `ContextHandler`:
|
||||
|
||||
|
@ -70,6 +70,11 @@ ApproveAliases::
|
|||
AllowSymLinkAliasChecker::
|
||||
Approve Aliases using the java-7 `Files.readSymbolicLink(path)` and `Path.toRealPath(...)` APIs to check that aliases are valid symbolic links.
|
||||
|
||||
____
|
||||
[NOTE]
|
||||
By default, Jetty serves aliased files for implementations running on UNIX as Contexts are created with both the {JDURL}/org/eclipse/jetty/server/handler/AllowSymLinkAliasChecker.html[`AllowSymLinkAliasChecker`] and {JDURL}/org/eclipse/jetty/server/handler/ContextHandler.ApproveNonExistentDirectoryAliases.html[`ApproveNonExistentDirectoryAliases`] alias checkers.
|
||||
____
|
||||
|
||||
An application is free to implement its own Alias checking.
|
||||
Alias Checkers can be installed in a context via the following XML used in a context deployer file or `WEB-INF/jetty-web.xml`:
|
||||
|
||||
|
|
|
@ -26,8 +26,8 @@ To start Jetty on the default port of 8080, run the following command:
|
|||
|
||||
2015-06-04 10:50:44.806:INFO::main: Logging initialized @334ms
|
||||
2015-06-04 10:50:44.858:WARN:oejs.HomeBaseWarning:main: This instance of Jetty is not running from a separate {jetty.base} directory, this is not recommended. See documentation at http://www.eclipse.org/jetty/documentation/current/startup.html
|
||||
2015-06-04 10:50:44.995:INFO:oejs.Server:main: jetty-9.3.0.v20150601
|
||||
2015-06-04 10:50:45.012:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///opt/jetty-distribution-9.3.0.v20150601/webapps/] at interval 1
|
||||
2015-06-04 10:50:44.995:INFO:oejs.Server:main: jetty-{VERSION}
|
||||
2015-06-04 10:50:45.012:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///opt/jetty-distribution-{VERSION}/webapps/] at interval 1
|
||||
2015-06-04 10:50:45.030:INFO:oejs.ServerConnector:main: Started ServerConnector@19dfb72a{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
|
||||
2015-06-04 10:50:45.030:INFO:oejs.Server:main: Started @558ms
|
||||
----
|
||||
|
@ -57,13 +57,13 @@ Within the standard Jetty distribution there is the `demo-base` directory, which
|
|||
|
||||
2017-08-16 16:55:15.571:INFO::main: Logging initialized @521ms to org.eclipse.jetty.util.log.StdErrLog
|
||||
2017-08-16 16:55:15.907:WARN::main: demo test-realm is deployed. DO NOT USE IN PRODUCTION!
|
||||
2017-08-16 16:55:15.910:INFO:oejs.Server:main: jetty-9.4.7-SNAPSHOT
|
||||
2017-08-16 16:55:15.931:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///tmp/jetty-distribution-9.4.7-SNAPSHOT/demo-base/webapps/] at interval 1
|
||||
2017-08-16 16:55:15.910:INFO:oejs.Server:main: jetty-{VERSION}
|
||||
2017-08-16 16:55:15.931:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///tmp/jetty-distribution-{VERSION}/demo-base/webapps/] at interval 1
|
||||
2017-08-16 16:55:16.151:INFO:oeja.AnnotationConfiguration:main: Scanning elapsed time=50ms
|
||||
2017-08-16 16:55:16.369:INFO:oejs.session:main: DefaultSessionIdManager workerName=node0
|
||||
2017-08-16 16:55:16.369:INFO:oejs.session:main: No SessionScavenger set, using defaults
|
||||
2017-08-16 16:55:16.370:INFO:oejs.session:main: Scavenging every 660000ms
|
||||
2017-08-16 16:55:16.416:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@7113b13f{/,file:///tmp/jetty-distribution-9.4.7-SNAPSHOT/demo-base/webapps/ROOT/,AVAILABLE}{/ROOT}
|
||||
2017-08-16 16:55:16.416:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@7113b13f{/,file:///tmp/jetty-distribution-{VERSION}/demo-base/webapps/ROOT/,AVAILABLE}{/ROOT}
|
||||
2017-08-16 16:55:16.625:INFO:oeja.AnnotationConfiguration:main: Scanning elapsed time=82ms
|
||||
2017-08-16 16:55:16.631:WARN::main: test webapp is deployed. DO NOT USE IN PRODUCTION!
|
||||
2017-08-16 16:55:16.751:INFO:oejsh.ManagedAttributeListener:main: update PushFilter null->org.eclipse.jetty.servlets.PushCacheFilter@1a677343 on o.e.j.w.WebAppContext@2d7275fc{/test,file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-test.war-_test-any-7157753932050220016.dir/webapp/,STARTING}{/test.war}
|
||||
|
@ -72,7 +72,7 @@ Within the standard Jetty distribution there is the `demo-base` directory, which
|
|||
2017-08-16 16:55:16.809:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@2d7275fc{/test,file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-test.war-_test-any-7157753932050220016.dir/webapp/,AVAILABLE}{/test.war}
|
||||
2017-08-16 16:55:16.816:INFO:oejsh.ContextHandler:main: Started o.e.j.s.h.MovedContextHandler@7c9d8e2{/oldContextPath,null,AVAILABLE}
|
||||
2017-08-16 16:55:16.854:INFO:oeja.AnnotationConfiguration:main: Scanning elapsed time=23ms
|
||||
2017-08-16 16:55:16.891:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@69453e37{/doc,file:///tmp/jetty-distribution-9.4.7-SNAPSHOT/demo-base/webapps/doc/,AVAILABLE}{/doc}
|
||||
2017-08-16 16:55:16.891:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@69453e37{/doc,file:///tmp/jetty-distribution-{VERSION}/demo-base/webapps/doc/,AVAILABLE}{/doc}
|
||||
2017-08-16 16:55:16.942:INFO:oeja.AnnotationConfiguration:main: Scanning elapsed time=25ms
|
||||
2017-08-16 16:55:16.945:WARN::main: test-jaas webapp is deployed. DO NOT USE IN PRODUCTION!
|
||||
2017-08-16 16:55:16.983:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@4e3958e7{/test-jaas,file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-test-jaas.war-_test-jaas-any-6953571893682159674.dir/webapp/,AVAILABLE}{/test-jaas.war}
|
||||
|
@ -81,15 +81,15 @@ Within the standard Jetty distribution there is the `demo-base` directory, which
|
|||
2017-08-16 16:55:17.192:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@1d8bd0de{/test-jndi,file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-test-jndi.war-_test-jndi-any-1246461885510956986.dir/webapp/,AVAILABLE}{/test-jndi.war}
|
||||
2017-08-16 16:55:17.307:INFO:oeja.AnnotationConfiguration:main: Scanning elapsed time=53ms
|
||||
2017-08-16 16:55:17.310:WARN::main: test-spec webapp is deployed. DO NOT USE IN PRODUCTION!
|
||||
2017-08-16 16:55:17.388:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@51dcb805{/test-spec,[file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-test-spec.war-_test-spec-any-3750193079644252256.dir/webapp/, jar:file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-test-spec.war-_test-spec-any-3750193079644252256.dir/webapp/WEB-INF/lib/test-web-fragment-9.4.7-SNAPSHOT.jar!/META-INF/resources],AVAILABLE}{/test-spec.war}
|
||||
2017-08-16 16:55:17.388:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@51dcb805{/test-spec,[file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-test-spec.war-_test-spec-any-3750193079644252256.dir/webapp/, jar:file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-test-spec.war-_test-spec-any-3750193079644252256.dir/webapp/WEB-INF/lib/test-web-fragment-{VERSION}.jar!/META-INF/resources],AVAILABLE}{/test-spec.war}
|
||||
2017-08-16 16:55:17.490:INFO:oeja.AnnotationConfiguration:main: Scanning elapsed time=53ms
|
||||
2017-08-16 16:55:17.493:WARN::main: async-rest webapp is deployed. DO NOT USE IN PRODUCTION!
|
||||
2017-08-16 16:55:17.516:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@1de76cc7{/async-rest,[file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-async-rest.war-_async-rest-any-8972552397332323832.dir/webapp/, jar:file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-async-rest.war-_async-rest-any-8972552397332323832.dir/webapp/WEB-INF/lib/example-async-rest-jar-9.4.7-SNAPSHOT.jar!/META-INF/resources],AVAILABLE}{/async-rest.war}
|
||||
2017-08-16 16:55:17.516:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@1de76cc7{/async-rest,[file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-async-rest.war-_async-rest-any-8972552397332323832.dir/webapp/, jar:file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-async-rest.war-_async-rest-any-8972552397332323832.dir/webapp/WEB-INF/lib/example-async-rest-jar-{VERSION}.jar!/META-INF/resources],AVAILABLE}{/async-rest.war}
|
||||
2017-08-16 16:55:17.643:INFO:oeja.AnnotationConfiguration:main: Scanning elapsed time=83ms
|
||||
2017-08-16 16:55:17.921:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@242b836{/proxy,file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-javadoc-proxy.war-_javadoc-proxy-any-4521643038409884891.dir/webapp/,AVAILABLE}{/javadoc-proxy.war}
|
||||
2017-08-16 16:55:17.936:INFO:oejs.AbstractConnector:main: Started ServerConnector@6f15d60e{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
|
||||
2017-08-16 16:55:17.944:INFO:oejus.SslContextFactory:main: x509=X509@58e1d9d(jetty,h=[jetty.eclipse.org],w=[]) for SslContextFactory@446a1e84(file:///tmp/jetty-distribution-9.4.7-SNAPSHOT/demo-base/etc/keystore,file:///tmp/jetty-distribution-9.4.7-SNAPSHOT/demo-base/etc/keystore)
|
||||
2017-08-16 16:55:17.944:INFO:oejus.SslContextFactory:main: x509=X509@4f0f2942(mykey,h=[],w=[]) for SslContextFactory@446a1e84(file:///tmp/jetty-distribution-9.4.7-SNAPSHOT/demo-base/etc/keystore,file:///tmp/jetty-distribution-9.4.7-SNAPSHOT/demo-base/etc/keystore)
|
||||
2017-08-16 16:55:17.944:INFO:oejus.SslContextFactory:main: x509=X509@58e1d9d(jetty,h=[jetty.eclipse.org],w=[]) for SslContextFactory@446a1e84(file:///tmp/jetty-distribution-{VERSION}/demo-base/etc/keystore,file:///tmp/jetty-distribution-{VERSION}/demo-base/etc/keystore)
|
||||
2017-08-16 16:55:17.944:INFO:oejus.SslContextFactory:main: x509=X509@4f0f2942(mykey,h=[],w=[]) for SslContextFactory@446a1e84(file:///tmp/jetty-distribution-{VERSION}/demo-base/etc/keystore,file:///tmp/jetty-distribution-{VERSION}/demo-base/etc/keystore)
|
||||
2017-08-16 16:55:18.071:INFO:oejs.AbstractConnector:main: Started ServerConnector@41488b16{SSL,[ssl, http/1.1]}{0.0.0.0:8443}
|
||||
2017-08-16 16:55:18.072:INFO:oejs.Server:main: Started @3022ms
|
||||
----
|
||||
|
@ -179,7 +179,7 @@ INFO: Base directory was modified
|
|||
2015-06-04 11:10:16.460:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///tmp/mybase/webapps/] at interval 1
|
||||
2015-06-04 11:10:16.581:WARN::main: async-rest webapp is deployed. DO NOT USE IN PRODUCTION!
|
||||
2015-06-04 11:10:16.589:INFO:oejw.StandardDescriptorProcessor:main: NO JSP Support for /, did not find org.eclipse.jetty.jsp.JettyJspServlet
|
||||
2015-06-04 11:10:16.628:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@1a407d53{/,[file:///tmp/jetty-0.0.0.0-8080-ROOT.war-_-any-4510228025526425427.dir/webapp/, jar:file:///tmp/jetty-0.0.0.0-8080-ROOT.war-_-any-4510228025526425427.dir/webapp/WEB-INF/lib/example-async-rest-jar-9.3.0.v20150601.jar!/META-INF/resources],AVAILABLE}{/ROOT.war}
|
||||
2015-06-04 11:10:16.628:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@1a407d53{/,[file:///tmp/jetty-0.0.0.0-8080-ROOT.war-_-any-4510228025526425427.dir/webapp/, jar:file:///tmp/jetty-0.0.0.0-8080-ROOT.war-_-any-4510228025526425427.dir/webapp/WEB-INF/lib/example-async-rest-jar-{VERSION}.jar!/META-INF/resources],AVAILABLE}{/ROOT.war}
|
||||
2015-06-04 11:10:16.645:INFO:oejs.ServerConnector:main: Started ServerConnector@3abbfa04{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
|
||||
2015-06-04 11:10:16.646:INFO:oejs.Server:main: Started @634ms
|
||||
----
|
||||
|
|
|
@ -86,7 +86,7 @@ The default system classes are:
|
|||
.Default System Classes
|
||||
[width="100%",cols="8%,92%",options="header",]
|
||||
|=======================================================================
|
||||
|System Classes
|
||||
|System Classes | Note
|
||||
|java. |Java SE classes (per servlet spec v2.5 / SRV.9.7.2).
|
||||
|javax. |Java SE classes (per servlet spec v2.5 / SRV.9.7.2).
|
||||
|org.xml. |Needed by javax.xml.
|
||||
|
@ -98,7 +98,7 @@ The default system classes are:
|
|||
|org.eclipse.jetty.servlet.DefaultServlet |Webapp can see and not change default servlet.
|
||||
|=======================================================================
|
||||
|
||||
Absolute classname can be passed, names ending with . are treated as packages names, and names starting with - are treated as negative matches and must be listed before any enclosing packages.
|
||||
Absolute classname can be passed, names ending with `.` are treated as packages names, and names starting with `-` are treated as negative matches and must be listed before any enclosing packages.
|
||||
|
||||
[[setting-server-classes]]
|
||||
===== Setting Server Classes
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.gcloud</groupId>
|
||||
<artifactId>gcloud-parent</artifactId>
|
||||
|
@ -22,12 +24,12 @@
|
|||
<version>${gcloud.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
|
||||
|
@ -57,50 +59,51 @@
|
|||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.session</bundle-symbolic-name>
|
||||
</properties>
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.session</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>manifest</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Export-Package>org.eclipse.jetty.gcloud.session.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}";</Export-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>manifest</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Export-Package>
|
||||
org.eclipse.jetty.gcloud.session.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}";
|
||||
</Export-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>build-deps-file</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>list</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<appendOutput>false</appendOutput>
|
||||
<outputFile>${project.build.directory}/deps.txt</outputFile>
|
||||
<sort>true</sort>
|
||||
<excludeGroupIds>org.eclipse.jetty</excludeGroupIds>
|
||||
<prependGroupId>true</prependGroupId>
|
||||
<includeScope>runtime</includeScope>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>build-deps-file</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>list</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<appendOutput>false</appendOutput>
|
||||
<outputFile>${project.build.directory}/deps.txt</outputFile>
|
||||
<sort>true</sort>
|
||||
<excludeGroupIds>org.eclipse.jetty</excludeGroupIds>
|
||||
<prependGroupId>true</prependGroupId>
|
||||
<includeScope>runtime</includeScope>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
|
@ -118,15 +121,15 @@
|
|||
match=" *(.*):(.*):jar:(.*):[a-z]*"
|
||||
replace="maven://\1/\2/\3|lib/gcloud/\2-\3.jar"
|
||||
byline="true"
|
||||
/>
|
||||
/>
|
||||
<replaceregexp file="${project.build.directory}/deps.txt"
|
||||
match="The following files have been resolved:"
|
||||
replace="[files]"
|
||||
/>
|
||||
</tasks>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>process-mod</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
|
@ -141,8 +144,7 @@
|
|||
</tasks>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
</executions>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
|
@ -161,7 +163,7 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -486,6 +486,16 @@
|
|||
<artifactId>jetty-alpn-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-openjdk8-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-conscrypt-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-jaspi</artifactId>
|
||||
|
|
|
@ -7,21 +7,26 @@ Installs the Conscrypt JSSE provider
|
|||
[depend]
|
||||
ssl
|
||||
|
||||
[provides]
|
||||
alpn-impl
|
||||
|
||||
[files]
|
||||
maven://org.conscrypt/conscrypt-openjdk-uber/${conscrypt.version}|lib/conscrypt/conscrypt-uber-${conscrypt.version}.jar
|
||||
#maven://org.conscrypt/conscrypt-openjdk/${conscrypt.version}/jar/linux-x86_64|lib/conscrypt/conscrypt-${conscrypt.version}-linux-x86_64.jar
|
||||
basehome:modules/conscrypt/conscrypt.xml|etc/conscrypt.xml
|
||||
|
||||
[lib]
|
||||
lib/conscrypt/**.jar
|
||||
|
||||
[xml]
|
||||
etc/conscrypt.xml
|
||||
|
||||
[lib]
|
||||
lib/conscrypt/**.jar
|
||||
lib/jetty-alpn-conscrypt-server-${jetty.version}.jar
|
||||
|
||||
[license]
|
||||
Conscrypt is distributed under the Apache Licence 2.0
|
||||
https://github.com/google/conscrypt/blob/master/LICENSE
|
||||
|
||||
[ini]
|
||||
conscrypt.version?=1.0.0.RC9
|
||||
jetty.sslContext.provider?=AndroidOpenSSL
|
||||
conscrypt.version?=1.0.0.RC10
|
||||
jetty.sslContext.provider?=Conscrypt
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.util.HashMap;
|
|||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
|
@ -98,6 +99,12 @@ public class HttpFields implements Iterable<HttpField>
|
|||
{
|
||||
return new Itr();
|
||||
}
|
||||
|
||||
public ListIterator<HttpField> listIterator()
|
||||
{
|
||||
return new Itr();
|
||||
}
|
||||
|
||||
|
||||
public Stream<HttpField> stream()
|
||||
{
|
||||
|
@ -935,7 +942,7 @@ public class HttpFields implements Iterable<HttpField>
|
|||
return value.substring(0, i).trim();
|
||||
}
|
||||
|
||||
private class Itr implements Iterator<HttpField>
|
||||
private class Itr implements ListIterator<HttpField>
|
||||
{
|
||||
int _cursor; // index of next element to return
|
||||
int _last=-1;
|
||||
|
@ -963,6 +970,49 @@ public class HttpFields implements Iterable<HttpField>
|
|||
_cursor=_last;
|
||||
_last=-1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPrevious()
|
||||
{
|
||||
return _cursor>0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField previous()
|
||||
{
|
||||
if (_cursor == 0)
|
||||
throw new NoSuchElementException();
|
||||
return _fields[_last=--_cursor];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextIndex()
|
||||
{
|
||||
return _cursor+1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int previousIndex()
|
||||
{
|
||||
return _cursor-1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(HttpField field)
|
||||
{
|
||||
if (_last<0)
|
||||
throw new IllegalStateException();
|
||||
_fields[_last] = field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(HttpField field)
|
||||
{
|
||||
_fields = Arrays.copyOf(_fields,_fields.length+1);
|
||||
System.arraycopy(_fields,_cursor,_fields,_cursor+1,_size++);
|
||||
_fields[_cursor++] = field;
|
||||
_last=-1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ 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.StringUtil;
|
||||
import org.eclipse.jetty.util.Trie;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.Utf8StringBuilder;
|
||||
|
@ -601,8 +602,8 @@ public class HttpParser
|
|||
while (_state.ordinal()<State.HEADER.ordinal() && buffer.hasRemaining() && !handle)
|
||||
{
|
||||
// process each character
|
||||
byte ch=next(buffer);
|
||||
if (ch==0)
|
||||
byte b=next(buffer);
|
||||
if (b==0)
|
||||
break;
|
||||
|
||||
if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
|
||||
|
@ -625,7 +626,7 @@ public class HttpParser
|
|||
switch (_state)
|
||||
{
|
||||
case METHOD:
|
||||
if (ch == SPACE)
|
||||
if (b == SPACE)
|
||||
{
|
||||
_length=_string.length();
|
||||
_methodString=takeString();
|
||||
|
@ -634,19 +635,19 @@ public class HttpParser
|
|||
_methodString=legacyString(_methodString,method.asString());
|
||||
setState(State.SPACE1);
|
||||
}
|
||||
else if (ch < SPACE)
|
||||
else if (b < SPACE)
|
||||
{
|
||||
if (ch==LINE_FEED)
|
||||
if (b==LINE_FEED)
|
||||
throw new BadMessageException("No URI");
|
||||
else
|
||||
throw new IllegalCharacterException(_state,ch,buffer);
|
||||
throw new IllegalCharacterException(_state,b,buffer);
|
||||
}
|
||||
else
|
||||
_string.append((char)ch);
|
||||
_string.append((char)b);
|
||||
break;
|
||||
|
||||
case RESPONSE_VERSION:
|
||||
if (ch == HttpTokens.SPACE)
|
||||
if (b == HttpTokens.SPACE)
|
||||
{
|
||||
_length=_string.length();
|
||||
String version=takeString();
|
||||
|
@ -655,19 +656,19 @@ public class HttpParser
|
|||
throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Unknown Version");
|
||||
setState(State.SPACE1);
|
||||
}
|
||||
else if (ch < HttpTokens.SPACE)
|
||||
throw new IllegalCharacterException(_state,ch,buffer);
|
||||
else if (b < HttpTokens.SPACE)
|
||||
throw new IllegalCharacterException(_state,b,buffer);
|
||||
else
|
||||
_string.append((char)ch);
|
||||
_string.append((char)b);
|
||||
break;
|
||||
|
||||
case SPACE1:
|
||||
if (ch > HttpTokens.SPACE || ch<0)
|
||||
if (b > HttpTokens.SPACE || b<0)
|
||||
{
|
||||
if (_responseHandler!=null)
|
||||
{
|
||||
setState(State.STATUS);
|
||||
setResponseStatus(ch-'0');
|
||||
setResponseStatus(b-'0');
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -695,25 +696,25 @@ public class HttpParser
|
|||
buffer.position(i-buffer.arrayOffset());
|
||||
}
|
||||
else
|
||||
_uri.append(ch);
|
||||
_uri.append(b);
|
||||
}
|
||||
}
|
||||
else if (ch < HttpTokens.SPACE)
|
||||
else if (b < HttpTokens.SPACE)
|
||||
{
|
||||
throw new BadMessageException(HttpStatus.BAD_REQUEST_400,_requestHandler!=null?"No URI":"No Status");
|
||||
}
|
||||
break;
|
||||
|
||||
case STATUS:
|
||||
if (ch == HttpTokens.SPACE)
|
||||
if (b == HttpTokens.SPACE)
|
||||
{
|
||||
setState(State.SPACE2);
|
||||
}
|
||||
else if (ch>='0' && ch<='9')
|
||||
else if (b>='0' && b<='9')
|
||||
{
|
||||
_responseStatus=_responseStatus*10+(ch-'0');
|
||||
_responseStatus=_responseStatus*10+(b-'0');
|
||||
}
|
||||
else if (ch < HttpTokens.SPACE && ch>=0)
|
||||
else if (b < HttpTokens.SPACE && b>=0)
|
||||
{
|
||||
setState(State.HEADER);
|
||||
handle=_responseHandler.startResponse(_version, _responseStatus, null)||handle;
|
||||
|
@ -725,11 +726,11 @@ public class HttpParser
|
|||
break;
|
||||
|
||||
case URI:
|
||||
if (ch == HttpTokens.SPACE)
|
||||
if (b == HttpTokens.SPACE)
|
||||
{
|
||||
setState(State.SPACE2);
|
||||
}
|
||||
else if (ch < HttpTokens.SPACE && ch>=0)
|
||||
else if (b < HttpTokens.SPACE && b>=0)
|
||||
{
|
||||
// HTTP/0.9
|
||||
if (complianceViolation(RFC7230,"HTTP/0.9"))
|
||||
|
@ -741,15 +742,15 @@ public class HttpParser
|
|||
}
|
||||
else
|
||||
{
|
||||
_uri.append(ch);
|
||||
_uri.append(b);
|
||||
}
|
||||
break;
|
||||
|
||||
case SPACE2:
|
||||
if (ch > HttpTokens.SPACE)
|
||||
if (b > HttpTokens.SPACE)
|
||||
{
|
||||
_string.setLength(0);
|
||||
_string.append((char)ch);
|
||||
_string.append((char)b);
|
||||
if (_responseHandler!=null)
|
||||
{
|
||||
_length=1;
|
||||
|
@ -789,7 +790,7 @@ public class HttpParser
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (ch == HttpTokens.LINE_FEED)
|
||||
else if (b == HttpTokens.LINE_FEED)
|
||||
{
|
||||
if (_responseHandler!=null)
|
||||
{
|
||||
|
@ -808,12 +809,12 @@ public class HttpParser
|
|||
handle= handleHeaderContentMessage() || handle;
|
||||
}
|
||||
}
|
||||
else if (ch<0)
|
||||
else if (b<0)
|
||||
throw new BadMessageException();
|
||||
break;
|
||||
|
||||
case REQUEST_VERSION:
|
||||
if (ch == HttpTokens.LINE_FEED)
|
||||
if (b == HttpTokens.LINE_FEED)
|
||||
{
|
||||
if (_version==null)
|
||||
{
|
||||
|
@ -835,25 +836,25 @@ public class HttpParser
|
|||
handle=_requestHandler.startRequest(_methodString,_uri.toString(), _version)||handle;
|
||||
continue;
|
||||
}
|
||||
else if (ch>=HttpTokens.SPACE)
|
||||
_string.append((char)ch);
|
||||
else if (b>=HttpTokens.SPACE)
|
||||
_string.append((char)b);
|
||||
else
|
||||
throw new BadMessageException();
|
||||
|
||||
break;
|
||||
|
||||
case REASON:
|
||||
if (ch == HttpTokens.LINE_FEED)
|
||||
if (b == HttpTokens.LINE_FEED)
|
||||
{
|
||||
String reason=takeString();
|
||||
setState(State.HEADER);
|
||||
handle=_responseHandler.startResponse(_version, _responseStatus, reason)||handle;
|
||||
continue;
|
||||
}
|
||||
else if (ch>=HttpTokens.SPACE)
|
||||
else if (b>=HttpTokens.SPACE || ((b<0) && (b>=-96)))
|
||||
{
|
||||
_string.append((char)ch);
|
||||
if (ch!=' '&&ch!='\t')
|
||||
_string.append((char)(0xff&b));
|
||||
if (b!=' '&&b!='\t')
|
||||
_length=_string.length();
|
||||
}
|
||||
else
|
||||
|
@ -991,8 +992,8 @@ public class HttpParser
|
|||
while ((_state==State.HEADER || _state==State.TRAILER) && buffer.hasRemaining())
|
||||
{
|
||||
// process each character
|
||||
byte ch=next(buffer);
|
||||
if (ch==0)
|
||||
byte b=next(buffer);
|
||||
if (b==0)
|
||||
break;
|
||||
|
||||
if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
|
||||
|
@ -1007,7 +1008,7 @@ public class HttpParser
|
|||
switch (_fieldState)
|
||||
{
|
||||
case FIELD:
|
||||
switch(ch)
|
||||
switch(b)
|
||||
{
|
||||
case HttpTokens.COLON:
|
||||
case HttpTokens.SPACE:
|
||||
|
@ -1110,7 +1111,7 @@ public class HttpParser
|
|||
default:
|
||||
{
|
||||
// now handle the ch
|
||||
if (ch<HttpTokens.SPACE)
|
||||
if (b<HttpTokens.SPACE)
|
||||
throw new BadMessageException();
|
||||
|
||||
// process previous header
|
||||
|
@ -1168,15 +1169,15 @@ public class HttpParser
|
|||
{
|
||||
// Header and value
|
||||
int pos=buffer.position()+n.length()+v.length()+1;
|
||||
byte b=buffer.get(pos);
|
||||
byte peek=buffer.get(pos);
|
||||
|
||||
if (b==HttpTokens.CARRIAGE_RETURN || b==HttpTokens.LINE_FEED)
|
||||
if (peek==HttpTokens.CARRIAGE_RETURN || peek==HttpTokens.LINE_FEED)
|
||||
{
|
||||
_field=field;
|
||||
_valueString=v;
|
||||
setState(FieldState.IN_VALUE);
|
||||
|
||||
if (b==HttpTokens.CARRIAGE_RETURN)
|
||||
if (peek==HttpTokens.CARRIAGE_RETURN)
|
||||
{
|
||||
_cr=true;
|
||||
buffer.position(pos+1);
|
||||
|
@ -1199,7 +1200,7 @@ public class HttpParser
|
|||
// New header
|
||||
setState(FieldState.IN_NAME);
|
||||
_string.setLength(0);
|
||||
_string.append((char)ch);
|
||||
_string.append((char)b);
|
||||
_length=1;
|
||||
|
||||
}
|
||||
|
@ -1207,7 +1208,7 @@ public class HttpParser
|
|||
break;
|
||||
|
||||
case IN_NAME:
|
||||
if (ch==HttpTokens.COLON)
|
||||
if (b==HttpTokens.COLON)
|
||||
{
|
||||
if (_headerString==null)
|
||||
{
|
||||
|
@ -1220,7 +1221,7 @@ public class HttpParser
|
|||
break;
|
||||
}
|
||||
|
||||
if (ch>HttpTokens.SPACE)
|
||||
if (b>HttpTokens.SPACE)
|
||||
{
|
||||
if (_header!=null)
|
||||
{
|
||||
|
@ -1229,13 +1230,13 @@ public class HttpParser
|
|||
_headerString=null;
|
||||
}
|
||||
|
||||
_string.append((char)ch);
|
||||
if (ch>HttpTokens.SPACE)
|
||||
_string.append((char)b);
|
||||
if (b>HttpTokens.SPACE)
|
||||
_length=_string.length();
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch==HttpTokens.LINE_FEED && !complianceViolation(RFC7230,"name only header"))
|
||||
if (b==HttpTokens.LINE_FEED && !complianceViolation(RFC7230,"name only header"))
|
||||
{
|
||||
if (_headerString==null)
|
||||
{
|
||||
|
@ -1251,21 +1252,21 @@ public class HttpParser
|
|||
break;
|
||||
}
|
||||
|
||||
throw new IllegalCharacterException(_state,ch,buffer);
|
||||
throw new IllegalCharacterException(_state,b,buffer);
|
||||
|
||||
case VALUE:
|
||||
if (ch>HttpTokens.SPACE || ch<0)
|
||||
if (b>HttpTokens.SPACE || b<0)
|
||||
{
|
||||
_string.append((char)(0xff&ch));
|
||||
_string.append((char)(0xff&b));
|
||||
_length=_string.length();
|
||||
setState(FieldState.IN_VALUE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch==HttpTokens.SPACE || ch==HttpTokens.TAB)
|
||||
if (b==HttpTokens.SPACE || b==HttpTokens.TAB)
|
||||
break;
|
||||
|
||||
if (ch==HttpTokens.LINE_FEED)
|
||||
if (b==HttpTokens.LINE_FEED)
|
||||
{
|
||||
_value=null;
|
||||
_string.setLength(0);
|
||||
|
@ -1275,10 +1276,10 @@ public class HttpParser
|
|||
setState(FieldState.FIELD);
|
||||
break;
|
||||
}
|
||||
throw new IllegalCharacterException(_state,ch,buffer);
|
||||
throw new IllegalCharacterException(_state,b,buffer);
|
||||
|
||||
case IN_VALUE:
|
||||
if (ch>=HttpTokens.SPACE || ch<0 || ch==HttpTokens.TAB)
|
||||
if (b>=HttpTokens.SPACE || b<0 || b==HttpTokens.TAB)
|
||||
{
|
||||
if (_valueString!=null)
|
||||
{
|
||||
|
@ -1286,13 +1287,13 @@ public class HttpParser
|
|||
_valueString=null;
|
||||
_field=null;
|
||||
}
|
||||
_string.append((char)(0xff&ch));
|
||||
if (ch>HttpTokens.SPACE || ch<0)
|
||||
_string.append((char)(0xff&b));
|
||||
if (b>HttpTokens.SPACE || b<0)
|
||||
_length=_string.length();
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch==HttpTokens.LINE_FEED)
|
||||
if (b==HttpTokens.LINE_FEED)
|
||||
{
|
||||
if (_length > 0)
|
||||
{
|
||||
|
@ -1304,7 +1305,7 @@ public class HttpParser
|
|||
break;
|
||||
}
|
||||
|
||||
throw new IllegalCharacterException(_state,ch,buffer);
|
||||
throw new IllegalCharacterException(_state,b,buffer);
|
||||
|
||||
default:
|
||||
throw new IllegalStateException(_state.toString());
|
||||
|
|
|
@ -76,6 +76,7 @@ mov=video/quicktime
|
|||
movie=video/x-sgi-movie
|
||||
mp2=audio/mpeg
|
||||
mp3=audio/mpeg
|
||||
mp4=video/mp4
|
||||
mpe=video/mpeg
|
||||
mpeg=video/mpeg
|
||||
mpg=video/mpeg
|
||||
|
|
|
@ -144,7 +144,7 @@ public class CookieCutterTest
|
|||
* Example from RFC2965
|
||||
*/
|
||||
@Test
|
||||
@Ignore("comma separation no longer supported by RFC6265")
|
||||
@Ignore("comma separation no longer supported by new RFC6265")
|
||||
public void testRFC2965_CookieSpoofingExample()
|
||||
{
|
||||
String rawCookie = "$Version=\"1\"; session_id=\"1234\", " +
|
||||
|
|
|
@ -21,7 +21,9 @@ package org.eclipse.jetty.http;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Locale;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
|
@ -31,6 +33,7 @@ import org.junit.Assert;
|
|||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
@ -628,4 +631,68 @@ public class HttpFieldsTest
|
|||
|
||||
assertFalse(header.containsKey("n11"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testIteration() throws Exception
|
||||
{
|
||||
HttpFields header = new HttpFields();
|
||||
Iterator<HttpField> i = header.iterator();
|
||||
assertThat(i.hasNext(),is(false));
|
||||
|
||||
header.put("name1", "valueA");
|
||||
header.put("name2", "valueB");
|
||||
header.add("name3", "valueC");
|
||||
|
||||
i = header.iterator();
|
||||
assertThat(i.hasNext(),is(true));
|
||||
assertThat(i.next().getName(),is("name1"));
|
||||
assertThat(i.next().getName(),is("name2"));
|
||||
i.remove();
|
||||
assertThat(i.next().getName(),is("name3"));
|
||||
assertThat(i.hasNext(),is(false));
|
||||
|
||||
i = header.iterator();
|
||||
assertThat(i.hasNext(),is(true));
|
||||
assertThat(i.next().getName(),is("name1"));
|
||||
assertThat(i.next().getName(),is("name3"));
|
||||
assertThat(i.hasNext(),is(false));
|
||||
|
||||
|
||||
ListIterator<HttpField> l = header.listIterator();
|
||||
assertThat(l.hasNext(),is(true));
|
||||
l.add(new HttpField("name0","value"));
|
||||
assertThat(l.hasNext(),is(true));
|
||||
assertThat(l.next().getName(),is("name1"));
|
||||
l.set(new HttpField("NAME1","value"));
|
||||
assertThat(l.hasNext(),is(true));
|
||||
assertThat(l.hasPrevious(),is(true));
|
||||
assertThat(l.previous().getName(),is("NAME1"));
|
||||
assertThat(l.hasNext(),is(true));
|
||||
assertThat(l.hasPrevious(),is(true));
|
||||
assertThat(l.previous().getName(),is("name0"));
|
||||
assertThat(l.hasNext(),is(true));
|
||||
assertThat(l.hasPrevious(),is(false));
|
||||
assertThat(l.next().getName(),is("name0"));
|
||||
assertThat(l.hasNext(),is(true));
|
||||
assertThat(l.hasPrevious(),is(true));
|
||||
assertThat(l.next().getName(),is("NAME1"));
|
||||
l.add(new HttpField("name2","value"));
|
||||
assertThat(l.next().getName(),is("name3"));
|
||||
assertThat(l.hasNext(),is(false));
|
||||
assertThat(l.hasPrevious(),is(true));
|
||||
l.add(new HttpField("name4","value"));
|
||||
assertThat(l.hasNext(),is(false));
|
||||
assertThat(l.hasPrevious(),is(true));
|
||||
assertThat(l.previous().getName(),is("name4"));
|
||||
|
||||
i = header.iterator();
|
||||
assertThat(i.hasNext(),is(true));
|
||||
assertThat(i.next().getName(),is("name0"));
|
||||
assertThat(i.next().getName(),is("NAME1"));
|
||||
assertThat(i.next().getName(),is("name2"));
|
||||
assertThat(i.next().getName(),is("name3"));
|
||||
assertThat(i.next().getName(),is("name4"));
|
||||
assertThat(i.hasNext(),is(false));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@ public class HttpGeneratorServerTest
|
|||
assertEquals(HttpGenerator.Result.NEED_INFO, result);
|
||||
assertEquals(HttpGenerator.State.START, gen.getState());
|
||||
|
||||
MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), 10);
|
||||
MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, "ØÆ", new HttpFields(), 10);
|
||||
info.getFields().add("Content-Type", "test/data;\r\nextra=value");
|
||||
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
|
||||
|
||||
|
@ -176,7 +176,7 @@ public class HttpGeneratorServerTest
|
|||
|
||||
assertEquals(10, gen.getContentPrepared());
|
||||
|
||||
assertThat(response, containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(response, containsString("HTTP/1.1 200 ØÆ"));
|
||||
assertThat(response, containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
|
||||
assertThat(response, containsString("Content-Type: test/data; extra=value"));
|
||||
assertThat(response, containsString("Content-Length: 10"));
|
||||
|
|
|
@ -573,7 +573,9 @@ public class HttpParserTest
|
|||
BufferUtil.put(BufferUtil.toBuffer(" HTTP/1.0\r\n"), buffer);
|
||||
BufferUtil.put(BufferUtil.toBuffer("Header1: "), buffer);
|
||||
buffer.put("\u00e6 \u00e6".getBytes(StandardCharsets.ISO_8859_1));
|
||||
BufferUtil.put(BufferUtil.toBuffer(" \r\n\r\n"), buffer);
|
||||
BufferUtil.put(BufferUtil.toBuffer(" \r\nHeader2: "), buffer);
|
||||
buffer.put((byte)-1);
|
||||
BufferUtil.put(BufferUtil.toBuffer("\r\n\r\n"), buffer);
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
|
||||
HttpParser.RequestHandler handler = new Handler();
|
||||
|
@ -585,7 +587,9 @@ public class HttpParserTest
|
|||
Assert.assertEquals("HTTP/1.0", _versionOrReason);
|
||||
Assert.assertEquals("Header1", _hdr[0]);
|
||||
Assert.assertEquals("\u00e6 \u00e6", _val[0]);
|
||||
Assert.assertEquals(0, _headers);
|
||||
Assert.assertEquals("Header2", _hdr[1]);
|
||||
Assert.assertEquals(""+(char)255, _val[1]);
|
||||
Assert.assertEquals(1, _headers);
|
||||
Assert.assertEquals(null, _bad);
|
||||
}
|
||||
|
||||
|
@ -1304,6 +1308,22 @@ public class HttpParserTest
|
|||
Assert.assertTrue(_messageCompleted);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseReasonIso8859_1() throws Exception
|
||||
{
|
||||
ByteBuffer buffer = BufferUtil.toBuffer(
|
||||
"HTTP/1.1 302 déplacé temporairement\r\n"
|
||||
+ "Content-Length: 0\r\n"
|
||||
+ "\r\n",StandardCharsets.ISO_8859_1);
|
||||
|
||||
HttpParser.ResponseHandler handler = new Handler();
|
||||
HttpParser parser = new HttpParser(handler);
|
||||
parser.parseNext(buffer);
|
||||
Assert.assertEquals("HTTP/1.1", _methodOrVersion);
|
||||
Assert.assertEquals("302", _uriOrStatus);
|
||||
Assert.assertEquals("déplacé temporairement", _versionOrReason);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSeekEOF() throws Exception
|
||||
{
|
||||
|
|
|
@ -50,15 +50,15 @@
|
|||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.alpn</groupId>
|
||||
<artifactId>alpn-api</artifactId>
|
||||
<version>${alpn.api.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.alpn</groupId>
|
||||
<artifactId>alpn-api</artifactId>
|
||||
<version>${alpn.api.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-server</artifactId>
|
||||
<artifactId>jetty-alpn-openjdk8-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
@ -86,4 +86,21 @@
|
|||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>jdk9</id>
|
||||
<activation>
|
||||
<jdk>[1.9,)</jdk>
|
||||
</activation>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-java-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
|
|
@ -47,7 +47,6 @@ public class HTTP2ClientConnectionFactory implements ClientConnectionFactory
|
|||
public static final String CLIENT_CONTEXT_KEY = "http2.client";
|
||||
public static final String BYTE_BUFFER_POOL_CONTEXT_KEY = "http2.client.byteBufferPool";
|
||||
public static final String EXECUTOR_CONTEXT_KEY = "http2.client.executor";
|
||||
public static final String PREALLOCATED_EXECUTOR_CONTEXT_KEY = "http2.client.preallocatedExecutor";
|
||||
public static final String SCHEDULER_CONTEXT_KEY = "http2.client.scheduler";
|
||||
public static final String SESSION_LISTENER_CONTEXT_KEY = "http2.client.sessionListener";
|
||||
public static final String SESSION_PROMISE_CONTEXT_KEY = "http2.client.sessionPromise";
|
||||
|
@ -60,7 +59,6 @@ public class HTTP2ClientConnectionFactory implements ClientConnectionFactory
|
|||
HTTP2Client client = (HTTP2Client)context.get(CLIENT_CONTEXT_KEY);
|
||||
ByteBufferPool byteBufferPool = (ByteBufferPool)context.get(BYTE_BUFFER_POOL_CONTEXT_KEY);
|
||||
Executor executor = (Executor)context.get(EXECUTOR_CONTEXT_KEY);
|
||||
ReservedThreadExecutor preallocatedExecutor = (ReservedThreadExecutor)context.get(PREALLOCATED_EXECUTOR_CONTEXT_KEY);
|
||||
Scheduler scheduler = (Scheduler)context.get(SCHEDULER_CONTEXT_KEY);
|
||||
Session.Listener listener = (Session.Listener)context.get(SESSION_LISTENER_CONTEXT_KEY);
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -71,37 +69,29 @@ public class HTTP2ClientConnectionFactory implements ClientConnectionFactory
|
|||
HTTP2ClientSession session = new HTTP2ClientSession(scheduler, endPoint, generator, listener, flowControl);
|
||||
Parser parser = new Parser(byteBufferPool, session, 4096, 8192);
|
||||
|
||||
if (preallocatedExecutor==null)
|
||||
{
|
||||
// TODO move this to non lazy construction
|
||||
preallocatedExecutor=client.getBean(ReservedThreadExecutor.class);
|
||||
if (preallocatedExecutor==null)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (preallocatedExecutor==null)
|
||||
{
|
||||
try
|
||||
{
|
||||
preallocatedExecutor = new ReservedThreadExecutor(executor,1); // TODO configure size
|
||||
preallocatedExecutor.start();
|
||||
client.addBean(preallocatedExecutor,true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HTTP2ClientConnection connection = new HTTP2ClientConnection(client, byteBufferPool, preallocatedExecutor, endPoint,
|
||||
ReservedThreadExecutor reservedExecutor = provideReservedThreadExecutor(client, executor);
|
||||
|
||||
HTTP2ClientConnection connection = new HTTP2ClientConnection(client, byteBufferPool, reservedExecutor, endPoint,
|
||||
parser, session, client.getInputBufferSize(), promise, listener);
|
||||
connection.addListener(connectionListener);
|
||||
return customize(connection, context);
|
||||
}
|
||||
|
||||
protected ReservedThreadExecutor provideReservedThreadExecutor(HTTP2Client client, Executor executor)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
ReservedThreadExecutor reservedExecutor = client.getBean(ReservedThreadExecutor.class);
|
||||
if (reservedExecutor == null)
|
||||
{
|
||||
// TODO: see HTTP2Connection.FillableCallback
|
||||
reservedExecutor = new ReservedThreadExecutor(executor, 0);
|
||||
client.addManaged(reservedExecutor);
|
||||
}
|
||||
return reservedExecutor;
|
||||
}
|
||||
}
|
||||
|
||||
private class HTTP2ClientConnection extends HTTP2Connection implements Callback
|
||||
{
|
||||
private final HTTP2Client client;
|
||||
|
|
|
@ -58,7 +58,6 @@ public class HTTP2Connection extends AbstractConnection
|
|||
this.session = session;
|
||||
this.bufferSize = bufferSize;
|
||||
this.strategy = new EatWhatYouKill(producer, executor.getExecutor(), executor);
|
||||
|
||||
LifeCycle.start(strategy);
|
||||
}
|
||||
|
||||
|
@ -274,6 +273,8 @@ public class HTTP2Connection extends AbstractConnection
|
|||
@Override
|
||||
public InvocationType getInvocationType()
|
||||
{
|
||||
// TODO: see also AbstractHTTP2ServerConnectionFactory.reservedThreads.
|
||||
// TODO: it's non blocking here because reservedThreads=0.
|
||||
return InvocationType.NON_BLOCKING;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,6 +154,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
|
|||
|
||||
public void setReservedThreads(int threads)
|
||||
{
|
||||
// TODO: see also HTTP2Connection.FillableCallback.
|
||||
// TODO: currently disabled since the only value that works is 0.
|
||||
// this.reservedThreads = threads;
|
||||
}
|
||||
|
@ -182,29 +183,8 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
|
|||
streamIdleTimeout = endPoint.getIdleTimeout();
|
||||
session.setStreamIdleTimeout(streamIdleTimeout);
|
||||
session.setInitialSessionRecvWindow(getInitialSessionRecvWindow());
|
||||
|
||||
ReservedThreadExecutor executor = connector.getBean(ReservedThreadExecutor.class);
|
||||
if (executor==null)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
executor = connector.getBean(ReservedThreadExecutor.class);
|
||||
if (executor==null)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
executor = new ReservedThreadExecutor(connector.getExecutor(), getReservedThreads());
|
||||
executor.start();
|
||||
connector.addBean(executor,true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ReservedThreadExecutor executor = provideReservedThreadExecutor(connector);
|
||||
|
||||
ServerParser parser = newServerParser(connector, session);
|
||||
HTTP2Connection connection = new HTTP2ServerConnection(connector.getByteBufferPool(), executor,
|
||||
|
@ -213,6 +193,20 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
|
|||
return configure(connection, connector, endPoint);
|
||||
}
|
||||
|
||||
protected ReservedThreadExecutor provideReservedThreadExecutor(Connector connector)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
ReservedThreadExecutor executor = getBean(ReservedThreadExecutor.class);
|
||||
if (executor == null)
|
||||
{
|
||||
executor = new ReservedThreadExecutor(connector.getExecutor(), getReservedThreads());
|
||||
addManaged(executor);
|
||||
}
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract ServerSessionListener newSessionListener(Connector connector, EndPoint endPoint);
|
||||
|
||||
protected ServerParser newServerParser(Connector connector, ServerParser.Listener listener)
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<!-- Get a reference to the default local cache. -->
|
||||
<!-- ===================================================================== -->
|
||||
<New id="local" class="org.infinispan.manager.DefaultCacheManager">
|
||||
<Arg><Property name="jetty.base" default="."/>/etc/infinispan-embedded.xml</Arg>
|
||||
<Get id="cache" name="cache"></Get>
|
||||
</New>
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
[description]
|
||||
Enables session data store in a local Infinispan cache
|
||||
|
||||
[tags]
|
||||
session
|
||||
|
||||
[provides]
|
||||
session-store
|
||||
session-store-infinispan-embedded
|
||||
|
||||
[depend]
|
||||
sessions
|
||||
|
||||
[files]
|
||||
maven://org.infinispan/infinispan-embedded/9.1.0.Final|lib/infinispan/infinispan-embedded-9.1.0.Final.jar
|
||||
basehome:modules/session-store-infinispan-embedded/infinispan-embedded.xml|etc/infinispan-embedded.xml
|
||||
|
||||
|
||||
[xml]
|
||||
etc/sessions/infinispan/default.xml
|
||||
|
||||
[lib]
|
||||
lib/jetty-infinispan-${jetty.version}.jar
|
||||
lib/infinispan/*.jar
|
||||
|
||||
[license]
|
||||
Infinispan is an open source project hosted on Github and released under the Apache 2.0 license.
|
||||
http://infinispan.org/
|
||||
http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
[ini-template]
|
||||
#jetty.session.gracePeriod.seconds=3600
|
||||
#jetty.session.savePeriod.seconds=0
|
|
@ -6,12 +6,15 @@ session
|
|||
|
||||
[provides]
|
||||
session-store
|
||||
session-store-infnispan-embedded
|
||||
|
||||
[depend]
|
||||
sessions
|
||||
|
||||
[files]
|
||||
maven://org.infinispan/infinispan-embedded/7.1.1.Final|lib/infinispan/infinispan-embedded-7.1.1.Final.jar
|
||||
basehome:modules/session-store-infinispan-embedded/infinispan-embedded.xml|etc/infinispan-embedded.xml
|
||||
|
||||
|
||||
[xml]
|
||||
etc/sessions/infinispan/default.xml
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<infinispan>
|
||||
<cache-container default-cache="jetty-sessions">
|
||||
<local-cache name="jetty-sessions"/>
|
||||
</cache-container>
|
||||
</infinispan>
|
|
@ -0,0 +1,35 @@
|
|||
[description]
|
||||
Enables session data store in a remote Infinispan cache
|
||||
|
||||
[tags]
|
||||
session
|
||||
|
||||
[provides]
|
||||
session-store
|
||||
session-store-infinispan-remote
|
||||
|
||||
[depend]
|
||||
sessions
|
||||
|
||||
[files]
|
||||
maven://org.infinispan/infinispan-remote/9.1.0.Final|lib/infinispan/infinispan-remote-9.1.0.Final.jar
|
||||
basehome:modules/session-store-infinispan-remote/
|
||||
|
||||
[xml]
|
||||
etc/sessions/infinispan/remote.xml
|
||||
|
||||
[lib]
|
||||
lib/jetty-infinispan-${jetty.version}.jar
|
||||
lib/infinispan/*.jar
|
||||
|
||||
[license]
|
||||
Infinispan is an open source project hosted on Github and released under the Apache 2.0 license.
|
||||
http://infinispan.org/
|
||||
http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
|
||||
[ini-template]
|
||||
#jetty.session.infinispan.remoteCacheName=sessions
|
||||
#jetty.session.infinispan.idleTimeout.seconds=0
|
||||
#jetty.session.gracePeriod.seconds=3600
|
||||
#jetty.session.savePeriod.seconds=0
|
|
@ -79,6 +79,11 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable
|
|||
setStopTimeout(5000);
|
||||
}
|
||||
|
||||
public Selector getSelector()
|
||||
{
|
||||
return _selector;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
|
@ -140,6 +145,116 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable
|
|||
selector.wakeup();
|
||||
}
|
||||
|
||||
private Runnable processConnect(SelectionKey key, final Connect connect)
|
||||
{
|
||||
SelectableChannel channel = key.channel();
|
||||
try
|
||||
{
|
||||
key.attach(connect.attachment);
|
||||
boolean connected = _selectorManager.doFinishConnect(channel);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Connected {} {}", connected, channel);
|
||||
if (connected)
|
||||
{
|
||||
if (connect.timeout.cancel())
|
||||
{
|
||||
key.interestOps(0);
|
||||
return new CreateEndPoint(channel, key)
|
||||
{
|
||||
@Override
|
||||
protected void failed(Throwable failure)
|
||||
{
|
||||
super.failed(failure);
|
||||
connect.failed(failure);
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SocketTimeoutException("Concurrent Connect Timeout");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ConnectException();
|
||||
}
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
connect.failed(x);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void closeNoExceptions(Closeable closeable)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (closeable != null)
|
||||
closeable.close();
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.ignore(x);
|
||||
}
|
||||
}
|
||||
|
||||
private EndPoint createEndPoint(SelectableChannel channel, SelectionKey selectionKey) throws IOException
|
||||
{
|
||||
EndPoint endPoint = _selectorManager.newEndPoint(channel, this, selectionKey);
|
||||
endPoint.onOpen();
|
||||
_selectorManager.endPointOpened(endPoint);
|
||||
Connection connection = _selectorManager.newConnection(channel, endPoint, selectionKey.attachment());
|
||||
endPoint.setConnection(connection);
|
||||
selectionKey.attach(endPoint);
|
||||
_selectorManager.connectionOpened(connection);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Created {}", endPoint);
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
public void destroyEndPoint(final EndPoint endPoint)
|
||||
{
|
||||
submit(new DestroyEndPoint(endPoint));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dump()
|
||||
{
|
||||
super.dump();
|
||||
return ContainerLifeCycle.dump(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(Appendable out, String indent) throws IOException
|
||||
{
|
||||
Selector selector = _selector;
|
||||
if (selector == null || !selector.isOpen())
|
||||
dumpBeans(out, indent);
|
||||
else
|
||||
{
|
||||
final ArrayList<Object> dump = new ArrayList<>(selector.keys().size() * 2);
|
||||
DumpKeys dumpKeys = new DumpKeys(dump);
|
||||
submit(dumpKeys);
|
||||
dumpKeys.await(5, TimeUnit.SECONDS);
|
||||
if (dump.isEmpty())
|
||||
dumpBeans(out, indent);
|
||||
else
|
||||
dumpBeans(out, indent, dump);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
Selector selector = _selector;
|
||||
return String.format("%s id=%s keys=%d selected=%d",
|
||||
super.toString(),
|
||||
_id,
|
||||
selector != null && selector.isOpen() ? selector.keys().size() : -1,
|
||||
selector != null && selector.isOpen() ? selector.selectedKeys().size() : -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Selectable} is an {@link EndPoint} that wish to be
|
||||
* notified of non-blocking events by the {@link ManagedSelector}.
|
||||
|
@ -161,7 +276,6 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable
|
|||
void updateKey();
|
||||
}
|
||||
|
||||
|
||||
private class SelectorProducer implements ExecutionStrategy.Producer
|
||||
{
|
||||
private Set<SelectionKey> _keys = Collections.emptySet();
|
||||
|
@ -331,7 +445,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable
|
|||
return String.format("%s@%x", getClass().getSimpleName(), hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private abstract static class NonBlockingAction implements Runnable, Invocable
|
||||
{
|
||||
@Override
|
||||
|
@ -341,124 +455,6 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable
|
|||
}
|
||||
}
|
||||
|
||||
private Runnable processConnect(SelectionKey key, final Connect connect)
|
||||
{
|
||||
SelectableChannel channel = key.channel();
|
||||
try
|
||||
{
|
||||
key.attach(connect.attachment);
|
||||
boolean connected = _selectorManager.doFinishConnect(channel);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Connected {} {}", connected, channel);
|
||||
if (connected)
|
||||
{
|
||||
if (connect.timeout.cancel())
|
||||
{
|
||||
key.interestOps(0);
|
||||
return new CreateEndPoint(channel, key)
|
||||
{
|
||||
@Override
|
||||
protected void failed(Throwable failure)
|
||||
{
|
||||
super.failed(failure);
|
||||
connect.failed(failure);
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SocketTimeoutException("Concurrent Connect Timeout");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ConnectException();
|
||||
}
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
connect.failed(x);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void closeNoExceptions(Closeable closeable)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (closeable != null)
|
||||
closeable.close();
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.ignore(x);
|
||||
}
|
||||
}
|
||||
|
||||
private EndPoint createEndPoint(SelectableChannel channel, SelectionKey selectionKey) throws IOException
|
||||
{
|
||||
EndPoint endPoint = _selectorManager.newEndPoint(channel, this, selectionKey);
|
||||
endPoint.onOpen();
|
||||
_selectorManager.endPointOpened(endPoint);
|
||||
Connection connection = _selectorManager.newConnection(channel, endPoint, selectionKey.attachment());
|
||||
endPoint.setConnection(connection);
|
||||
selectionKey.attach(endPoint);
|
||||
_selectorManager.connectionOpened(connection);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Created {}", endPoint);
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
public void destroyEndPoint(final EndPoint endPoint)
|
||||
{
|
||||
final Connection connection = endPoint.getConnection();
|
||||
submit(() ->
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Destroyed {}", endPoint);
|
||||
if (connection != null)
|
||||
_selectorManager.connectionClosed(connection);
|
||||
_selectorManager.endPointClosed(endPoint);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dump()
|
||||
{
|
||||
super.dump();
|
||||
return ContainerLifeCycle.dump(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(Appendable out, String indent) throws IOException
|
||||
{
|
||||
Selector selector = _selector;
|
||||
if (selector == null || !selector.isOpen())
|
||||
dumpBeans(out, indent);
|
||||
else
|
||||
{
|
||||
final ArrayList<Object> dump = new ArrayList<>(selector.keys().size() * 2);
|
||||
DumpKeys dumpKeys = new DumpKeys(dump);
|
||||
submit(dumpKeys);
|
||||
dumpKeys.await(5, TimeUnit.SECONDS);
|
||||
if (dump.isEmpty())
|
||||
dumpBeans(out, indent);
|
||||
else
|
||||
dumpBeans(out, indent, dump);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
Selector selector = _selector;
|
||||
return String.format("%s id=%s keys=%d selected=%d",
|
||||
super.toString(),
|
||||
_id,
|
||||
selector != null && selector.isOpen() ? selector.keys().size() : -1,
|
||||
selector != null && selector.isOpen() ? selector.selectedKeys().size() : -1);
|
||||
}
|
||||
|
||||
private class DumpKeys implements Runnable
|
||||
{
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
@ -523,8 +519,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable
|
|||
if (_key==null)
|
||||
{
|
||||
_key = _channel.register(_selector, SelectionKey.OP_ACCEPT, this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} acceptor={}", this, _key);
|
||||
|
@ -609,7 +604,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable
|
|||
}
|
||||
}
|
||||
|
||||
private class CreateEndPoint implements Runnable, Closeable
|
||||
private class CreateEndPoint extends NonBlockingAction implements Closeable
|
||||
{
|
||||
private final SelectableChannel channel;
|
||||
private final SelectionKey key;
|
||||
|
@ -800,8 +795,24 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable
|
|||
}
|
||||
}
|
||||
|
||||
public Selector getSelector()
|
||||
private class DestroyEndPoint extends NonBlockingAction
|
||||
{
|
||||
return _selector;
|
||||
private final EndPoint endPoint;
|
||||
|
||||
public DestroyEndPoint(EndPoint endPoint)
|
||||
{
|
||||
this.endPoint = endPoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Destroyed {}", endPoint);
|
||||
Connection connection = endPoint.getConnection();
|
||||
if (connection != null)
|
||||
_selectorManager.connectionClosed(connection);
|
||||
_selectorManager.endPointClosed(endPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ public abstract class NegotiatingClientConnection extends AbstractConnection
|
|||
this.context = context;
|
||||
}
|
||||
|
||||
protected SSLEngine getSSLEngine()
|
||||
public SSLEngine getSSLEngine()
|
||||
{
|
||||
return engine;
|
||||
}
|
||||
|
@ -80,13 +80,17 @@ public abstract class NegotiatingClientConnection extends AbstractConnection
|
|||
while (true)
|
||||
{
|
||||
int filled = fill();
|
||||
if (filled == 0 && !completed)
|
||||
fillInterested();
|
||||
if (filled <= 0 || completed)
|
||||
if (completed || filled < 0)
|
||||
{
|
||||
replaceConnection();
|
||||
break;
|
||||
}
|
||||
if (filled == 0)
|
||||
{
|
||||
fillInterested();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (completed)
|
||||
replaceConnection();
|
||||
}
|
||||
|
||||
private int fill()
|
||||
|
|
|
@ -18,35 +18,54 @@
|
|||
|
||||
package org.eclipse.jetty.io.ssl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
|
||||
public interface ALPNProcessor
|
||||
{
|
||||
public interface Server
|
||||
/**
|
||||
* Initializes this ALPNProcessor
|
||||
*
|
||||
* @throws RuntimeException if this processor is unavailable (e.g. missing dependencies or wrong JVM)
|
||||
*/
|
||||
public default void init()
|
||||
{
|
||||
public static final ALPNProcessor.Server NOOP = new ALPNProcessor.Server()
|
||||
{
|
||||
};
|
||||
|
||||
public default void configure(SSLEngine sslEngine)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public interface Client
|
||||
/**
|
||||
* Tests if this processor can be applied to the given SSLEngine.
|
||||
*
|
||||
* @param sslEngine the SSLEngine to check
|
||||
* @return true if the processor can be applied to the given SSLEngine
|
||||
*/
|
||||
public default boolean appliesTo(SSLEngine sslEngine)
|
||||
{
|
||||
public static final Client NOOP = new Client()
|
||||
{
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
public default void configure(SSLEngine sslEngine, List<String> protocols)
|
||||
{
|
||||
}
|
||||
/**
|
||||
* Configures the given SSLEngine and the given Connection for ALPN.
|
||||
*
|
||||
* @param sslEngine the SSLEngine to configure
|
||||
* @param connection the Connection to configure
|
||||
* @throws RuntimeException if this processor cannot be configured
|
||||
*/
|
||||
public default void configure(SSLEngine sslEngine, Connection connection)
|
||||
{
|
||||
}
|
||||
|
||||
public default void process(SSLEngine sslEngine)
|
||||
{
|
||||
}
|
||||
/**
|
||||
* Server-side interface used by ServiceLoader.
|
||||
*/
|
||||
public interface Server extends ALPNProcessor
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Client-side interface used by ServiceLoader.
|
||||
*/
|
||||
public interface Client extends ALPNProcessor
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -178,11 +178,11 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
|
||||
private DirContext _rootContext;
|
||||
|
||||
|
||||
|
||||
public class LDAPUserInfo extends UserInfo
|
||||
{
|
||||
Attributes attributes;
|
||||
|
||||
Attributes attributes;
|
||||
|
||||
/**
|
||||
* @param userName
|
||||
* @param credential
|
||||
|
@ -198,10 +198,10 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
{
|
||||
return getUserRoles(_rootContext, getUserName(), attributes);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* get the available information about the user
|
||||
* <p>
|
||||
|
@ -216,7 +216,7 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
*/
|
||||
public UserInfo getUserInfo(String username) throws Exception
|
||||
{
|
||||
Attributes attributes = getUserAttributes(username);
|
||||
Attributes attributes = getUserAttributes(username);
|
||||
String pwdCredential = getUserCredentials(attributes);
|
||||
|
||||
if (pwdCredential == null)
|
||||
|
@ -271,30 +271,32 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
*/
|
||||
private Attributes getUserAttributes(String username) throws LoginException
|
||||
{
|
||||
Attributes attributes = null;
|
||||
Attributes attributes = null;
|
||||
|
||||
SearchResult result;
|
||||
try {
|
||||
result = findUser(username);
|
||||
attributes = result.getAttributes();
|
||||
}
|
||||
catch (NamingException e) {
|
||||
SearchResult result;
|
||||
try
|
||||
{
|
||||
result = findUser(username);
|
||||
attributes = result.getAttributes();
|
||||
}
|
||||
catch (NamingException e)
|
||||
{
|
||||
throw new LoginException("Root context binding failure.");
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
private String getUserCredentials(Attributes attributes) throws LoginException
|
||||
{
|
||||
String ldapCredential = null;
|
||||
String ldapCredential = null;
|
||||
|
||||
Attribute attribute = attributes.get(_userPasswordAttribute);
|
||||
if (attribute != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] value = (byte[]) attribute.get();
|
||||
byte[] value = (byte[])attribute.get();
|
||||
|
||||
ldapCredential = new String(value);
|
||||
}
|
||||
|
@ -323,16 +325,16 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
{
|
||||
String rdnValue = username;
|
||||
Attribute attribute = attributes.get(_userRdnAttribute);
|
||||
if (attribute != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
rdnValue = (String) attribute.get(); // switch to the value stored in the _userRdnAttribute if we can
|
||||
}
|
||||
catch (NamingException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
if (attribute != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
rdnValue = (String)attribute.get(); // switch to the value stored in the _userRdnAttribute if we can
|
||||
}
|
||||
catch (NamingException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
String userDn = _userRdnAttribute + "=" + rdnValue + "," + _userBaseDn;
|
||||
|
||||
|
@ -361,7 +363,7 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
|
||||
while (results.hasMoreElements())
|
||||
{
|
||||
SearchResult result = (SearchResult) results.nextElement();
|
||||
SearchResult result = (SearchResult)results.nextElement();
|
||||
|
||||
Attributes attributes = result.getAttributes();
|
||||
|
||||
|
@ -410,8 +412,8 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
Callback[] callbacks = configureCallbacks();
|
||||
getCallbackHandler().handle(callbacks);
|
||||
|
||||
String webUserName = ((NameCallback) callbacks[0]).getName();
|
||||
Object webCredential = ((ObjectCallback) callbacks[1]).getObject();
|
||||
String webUserName = ((NameCallback)callbacks[0]).getName();
|
||||
Object webCredential = ((ObjectCallback)callbacks[1]).getObject();
|
||||
|
||||
if (webUserName == null || webCredential == null)
|
||||
{
|
||||
|
@ -424,8 +426,7 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
if (_forceBindingLogin)
|
||||
{
|
||||
authed = bindingLogin(webUserName, webCredential);
|
||||
}
|
||||
else
|
||||
} else
|
||||
{
|
||||
// This sets read and the credential
|
||||
UserInfo userInfo = getUserInfo(webUserName);
|
||||
|
@ -439,7 +440,7 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
setCurrentUser(new JAASUserInfo(userInfo));
|
||||
|
||||
if (webCredential instanceof String)
|
||||
authed = credentialLogin(Credential.getCredential((String) webCredential));
|
||||
authed = credentialLogin(Credential.getCredential((String)webCredential));
|
||||
else
|
||||
authed = credentialLogin(webCredential);
|
||||
}
|
||||
|
@ -494,7 +495,7 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
* @param username the user name
|
||||
* @param password the password
|
||||
* @return true always
|
||||
* @throws LoginException if unable to bind the login
|
||||
* @throws LoginException if unable to bind the login
|
||||
* @throws NamingException if failure to bind login
|
||||
*/
|
||||
public boolean bindingLogin(String username, Object password) throws LoginException, NamingException
|
||||
|
@ -505,15 +506,15 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
|
||||
LOG.info("Attempting authentication: " + userDn);
|
||||
|
||||
Hashtable<Object,Object> environment = getEnvironment();
|
||||
Hashtable<Object, Object> environment = getEnvironment();
|
||||
|
||||
if ( userDn == null || "".equals(userDn) )
|
||||
if (userDn == null || "".equals(userDn))
|
||||
{
|
||||
throw new NamingException("username may not be empty");
|
||||
}
|
||||
environment.put(Context.SECURITY_PRINCIPAL, userDn);
|
||||
// RFC 4513 section 6.3.1, protect against ldap server implementations that allow successful binding on empty passwords
|
||||
if ( password == null || "".equals(password))
|
||||
if (password == null || "".equals(password))
|
||||
{
|
||||
throw new NamingException("password may not be empty");
|
||||
}
|
||||
|
@ -542,9 +543,9 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
LOG.debug("Searching for user " + username + " with filter: \'" + filter + "\'" + " from base dn: " + _userBaseDn);
|
||||
|
||||
Object[] filterArguments = new Object[]{
|
||||
_userObjectClass,
|
||||
_userIdAttribute,
|
||||
username
|
||||
_userObjectClass,
|
||||
_userIdAttribute,
|
||||
username
|
||||
};
|
||||
NamingEnumeration<SearchResult> results = _rootContext.search(_userBaseDn, filter, filterArguments, ctls);
|
||||
|
||||
|
@ -556,7 +557,7 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
throw new LoginException("User not found.");
|
||||
}
|
||||
|
||||
return (SearchResult) results.nextElement();
|
||||
return (SearchResult)results.nextElement();
|
||||
}
|
||||
|
||||
|
||||
|
@ -565,37 +566,37 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
* <p>
|
||||
* Called once by JAAS after new instance is created.
|
||||
*
|
||||
* @param subject the subect
|
||||
* @param subject the subect
|
||||
* @param callbackHandler the callback handler
|
||||
* @param sharedState the shared state map
|
||||
* @param options the option map
|
||||
* @param sharedState the shared state map
|
||||
* @param options the option map
|
||||
*/
|
||||
public void initialize(Subject subject,
|
||||
CallbackHandler callbackHandler,
|
||||
Map<String,?> sharedState,
|
||||
Map<String,?> options)
|
||||
Map<String, ?> sharedState,
|
||||
Map<String, ?> options)
|
||||
{
|
||||
super.initialize(subject, callbackHandler, sharedState, options);
|
||||
|
||||
_hostname = (String) options.get("hostname");
|
||||
_port = Integer.parseInt((String) options.get("port"));
|
||||
_contextFactory = (String) options.get("contextFactory");
|
||||
_bindDn = (String) options.get("bindDn");
|
||||
_bindPassword = (String) options.get("bindPassword");
|
||||
_authenticationMethod = (String) options.get("authenticationMethod");
|
||||
_hostname = (String)options.get("hostname");
|
||||
_port = Integer.parseInt((String)options.get("port"));
|
||||
_contextFactory = (String)options.get("contextFactory");
|
||||
_bindDn = (String)options.get("bindDn");
|
||||
_bindPassword = (String)options.get("bindPassword");
|
||||
_authenticationMethod = (String)options.get("authenticationMethod");
|
||||
|
||||
_userBaseDn = (String) options.get("userBaseDn");
|
||||
_userBaseDn = (String)options.get("userBaseDn");
|
||||
|
||||
_roleBaseDn = (String) options.get("roleBaseDn");
|
||||
_roleBaseDn = (String)options.get("roleBaseDn");
|
||||
|
||||
if (options.containsKey("forceBindingLogin"))
|
||||
{
|
||||
_forceBindingLogin = Boolean.parseBoolean((String) options.get("forceBindingLogin"));
|
||||
_forceBindingLogin = Boolean.parseBoolean((String)options.get("forceBindingLogin"));
|
||||
}
|
||||
|
||||
if (options.containsKey("useLdaps"))
|
||||
{
|
||||
_useLdaps = Boolean.parseBoolean((String) options.get("useLdaps"));
|
||||
_useLdaps = Boolean.parseBoolean((String)options.get("useLdaps"));
|
||||
}
|
||||
|
||||
_userObjectClass = getOption(options, "userObjectClass", _userObjectClass);
|
||||
|
@ -625,7 +626,7 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
}
|
||||
catch (NamingException e)
|
||||
{
|
||||
throw new LoginException( "error closing root context: " + e.getMessage() );
|
||||
throw new LoginException("error closing root context: " + e.getMessage());
|
||||
}
|
||||
|
||||
return super.commit();
|
||||
|
@ -639,13 +640,13 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
}
|
||||
catch (NamingException e)
|
||||
{
|
||||
throw new LoginException( "error closing root context: " + e.getMessage() );
|
||||
throw new LoginException("error closing root context: " + e.getMessage());
|
||||
}
|
||||
|
||||
return super.abort();
|
||||
}
|
||||
|
||||
private String getOption(Map<String,?> options, String key, String defaultValue)
|
||||
private String getOption(Map<String, ?> options, String key, String defaultValue)
|
||||
{
|
||||
Object value = options.get(key);
|
||||
|
||||
|
@ -654,7 +655,7 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
return defaultValue;
|
||||
}
|
||||
|
||||
return (String) value;
|
||||
return (String)value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -670,7 +671,7 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
|
||||
if (_hostname != null)
|
||||
{
|
||||
env.put(Context.PROVIDER_URL, (_useLdaps?"ldaps://":"ldap://") + _hostname + (_port==0?"":":"+_port) +"/");
|
||||
env.put(Context.PROVIDER_URL, (_useLdaps ? "ldaps://" : "ldap://") + _hostname + (_port == 0 ? "" : ":" + _port) + "/");
|
||||
}
|
||||
|
||||
if (_authenticationMethod != null)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue