Merged branch 'jetty-9.4.x' into 'master'.

This commit is contained in:
Simone Bordet 2017-10-03 01:22:35 +02:00
commit bee383c894
29 changed files with 613 additions and 89 deletions

View File

@ -32,7 +32,6 @@ 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;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
/**
* A Jetty server with multiple connectors.

View File

@ -96,20 +96,18 @@ public class ConscryptServerALPNProcessor implements ALPNProcessor.Server
@Override
public void handshakeSucceeded(Event event)
{
String protocol = alpnConnection.getProtocol();
if (LOG.isDebugEnabled())
LOG.debug("handshakeSucceeded {} {}", alpnConnection, event);
if (alpnConnection.getProtocol()==null)
{
LOG.warn("No ALPN callback! {} {}",alpnConnection, event);
LOG.debug("TLS handshake succeeded, protocol={} for {}", protocol, alpnConnection);
if (protocol ==null)
alpnConnection.unsupported();
}
}
@Override
public void handshakeFailed(Event event, Throwable failure)
{
if (LOG.isDebugEnabled())
LOG.debug("handshakeFailed {} {} {}", alpnConnection, event, failure);
LOG.debug("TLS handshake failed " + alpnConnection, failure);
}
}
}

View File

@ -78,20 +78,18 @@ public class JDK9ServerALPNProcessor implements ALPNProcessor.Server, SslHandsha
@Override
public void handshakeSucceeded(Event event)
{
String protocol = alpnConnection.getProtocol();
if (LOG.isDebugEnabled())
LOG.debug("handshakeSucceeded {} {}", alpnConnection, event);
if (alpnConnection.getProtocol()==null)
{
LOG.warn("No ALPN callback! {} {}",alpnConnection, event);
LOG.debug("TLS handshake succeeded, protocol={} for {}", protocol, alpnConnection);
if (protocol ==null)
alpnConnection.unsupported();
}
}
@Override
public void handshakeFailed(Event event, Throwable failure)
{
if (LOG.isDebugEnabled())
LOG.debug("handshakeFailed {} {} {}", alpnConnection, event, failure);
LOG.debug("TLS handshake failed " + alpnConnection, failure);
}
}
}

View File

@ -40,7 +40,9 @@ public class OpenJDK8ClientALPNProcessor implements ALPNProcessor.Client
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");
throw new IllegalStateException(ALPN.class.getName() + " must be on JVM boot classpath");
if (LOG.isDebugEnabled())
ALPN.debug = true;
}
@Override

View File

@ -206,7 +206,7 @@ public class TestAnnotationConfiguration
String dir = MavenTestingUtils.getTargetTestingDir("getFragmentFromJar").getAbsolutePath();
File file = new File(dir);
file=new File(file.getCanonicalPath());
URL url=file.toURL();
URL url=file.toURI().toURL();
Resource jar1 = Resource.newResource(url+"file.jar");

View File

@ -73,7 +73,6 @@ 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.ThreadBudget;
import org.eclipse.jetty.util.thread.ThreadPool;
/**

View File

@ -0,0 +1,58 @@
//
// ========================================================================
// 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.client;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.Assert;
import org.junit.Test;
public class InsufficientThreadsDetectionTest
{
@Test(expected = IllegalStateException.class)
public void testInsufficientThreads() throws Exception
{
QueuedThreadPool clientThreads = new QueuedThreadPool(1);
HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP(1), null);
httpClient.setExecutor(clientThreads);
httpClient.start();
}
@Test
public void testInsufficientThreadsForMultipleHttpClients() throws Exception
{
QueuedThreadPool clientThreads = new QueuedThreadPool(3);
HttpClient httpClient1 = new HttpClient(new HttpClientTransportOverHTTP(1), null);
httpClient1.setExecutor(clientThreads);
httpClient1.start();
try
{
// Share the same thread pool with another instance.
HttpClient httpClient2 = new HttpClient(new HttpClientTransportOverHTTP(1), null);
httpClient2.setExecutor(clientThreads);
httpClient2.start();
Assert.fail();
}
catch (IllegalStateException expected)
{
// Expected.
}
}
}

View File

@ -25,6 +25,7 @@ import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.LeakTrackingConnectionPool;
import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.LeakTrackingByteBufferPool;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.server.Handler;
@ -45,7 +46,7 @@ public abstract class AbstractHttpClientServerTest
@Rule
public final TestTracker tracker = new TestTracker();
private LeakTrackingByteBufferPool serverBufferPool;
private LeakTrackingByteBufferPool clientBufferPool;
protected ByteBufferPool clientBufferPool;
private final AtomicLong connectionLeaks = new AtomicLong();
protected Server server;
protected ServerConnector connector;
@ -80,7 +81,8 @@ public abstract class AbstractHttpClientServerTest
});
client = new HttpClient(transport, null);
client.setExecutor(executor);
clientBufferPool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged());
if (clientBufferPool == null)
clientBufferPool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged());
client.setByteBufferPool(clientBufferPool);
client.start();
}
@ -94,9 +96,13 @@ public abstract class AbstractHttpClientServerTest
assertThat("Server BufferPool - leaked releases", serverBufferPool.getLeakedReleases(), Matchers.is(0L));
assertThat("Server BufferPool - unreleased", serverBufferPool.getLeakedResources(), Matchers.is(0L));
assertThat("Client BufferPool - leaked acquires", clientBufferPool.getLeakedAcquires(), Matchers.is(0L));
assertThat("Client BufferPool - leaked releases", clientBufferPool.getLeakedReleases(), Matchers.is(0L));
assertThat("Client BufferPool - unreleased", clientBufferPool.getLeakedResources(), Matchers.is(0L));
if (clientBufferPool instanceof LeakTrackingByteBufferPool)
{
LeakTrackingByteBufferPool pool = (LeakTrackingByteBufferPool)clientBufferPool;
assertThat("Client BufferPool - leaked acquires", pool.getLeakedAcquires(), Matchers.is(0L));
assertThat("Client BufferPool - leaked releases", pool.getLeakedReleases(), Matchers.is(0L));
assertThat("Client BufferPool - unreleased", pool.getLeakedResources(), Matchers.is(0L));
}
assertThat("Connection Leaks", connectionLeaks.get(), Matchers.is(0L));

View File

@ -46,6 +46,7 @@ import org.eclipse.jetty.client.util.BytesContentProvider;
import org.eclipse.jetty.client.util.DeferredContentProvider;
import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.IO;
import org.eclipse.jetty.toolchain.test.annotation.Slow;
@ -380,6 +381,11 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@Test
public void testGZIPContentEncoding() throws Exception
{
// GZIPContentDecoder returns to application pooled
// buffers, which is fine, but in this test they will
// appear as "leaked", so we use a normal ByteBufferPool.
clientBufferPool = new MappedByteBufferPool.Tagged();
final byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
start(new AbstractHandler()
{

View File

@ -31,7 +31,6 @@ public class MappedByteBufferPool implements ByteBufferPool
private final ConcurrentMap<Integer, Bucket> directBuffers = new ConcurrentHashMap<>();
private final ConcurrentMap<Integer, Bucket> heapBuffers = new ConcurrentHashMap<>();
private final int _factor;
private final int _maxQueue;
private final Function<Integer, Bucket> _newBucket;
public MappedByteBufferPool()
@ -52,8 +51,7 @@ public class MappedByteBufferPool implements ByteBufferPool
public MappedByteBufferPool(int factor,int maxQueue,Function<Integer, Bucket> newBucket)
{
_factor = factor<=0?1024:factor;
_maxQueue = maxQueue;
_newBucket = newBucket!=null?newBucket:i->new Bucket(this,i*_factor,_maxQueue);
_newBucket = newBucket!=null?newBucket:i->new Bucket(this,i*_factor,maxQueue);
}
@Override

View File

@ -38,8 +38,8 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.ReservedThreadExecutor;
import org.eclipse.jetty.util.thread.Scheduler;
import org.eclipse.jetty.util.thread.ThreadBudget;
import org.eclipse.jetty.util.thread.ThreadPool;
import org.eclipse.jetty.util.thread.ThreadPoolBudget;
import org.eclipse.jetty.util.thread.strategy.EatWhatYouKill;
/**
@ -61,6 +61,7 @@ public abstract class SelectorManager extends ContainerLifeCycle implements Dump
private long _connectTimeout = DEFAULT_CONNECT_TIMEOUT;
private long _selectorIndex;
private int _reservedThreads = -1;
private ThreadPoolBudget.Lease _lease;
private static int defaultSelectors(Executor executor)
{
@ -70,7 +71,6 @@ public abstract class SelectorManager extends ContainerLifeCycle implements Dump
int cpus = Runtime.getRuntime().availableProcessors();
return Math.max(1,Math.min(cpus/2,threads/16));
}
return Math.max(1,Runtime.getRuntime().availableProcessors()/2);
}
@ -297,14 +297,13 @@ public abstract class SelectorManager extends ContainerLifeCycle implements Dump
protected void doStart() throws Exception
{
addBean(new ReservedThreadExecutor(getExecutor(),_reservedThreads,this),true);
_lease = ThreadPoolBudget.leaseFrom(getExecutor(), this, _selectors.length);
for (int i = 0; i < _selectors.length; i++)
{
ManagedSelector selector = newSelector(i);
_selectors[i] = selector;
addBean(selector);
}
super.doStart();
}
@ -325,6 +324,8 @@ public abstract class SelectorManager extends ContainerLifeCycle implements Dump
super.doStop();
for (ManagedSelector selector : _selectors)
removeBean(selector);
if (_lease != null)
_lease.close();
}
/**

View File

@ -18,14 +18,6 @@
package org.eclipse.jetty.io;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.nio.ByteBuffer;
import java.util.concurrent.ConcurrentMap;
@ -34,6 +26,14 @@ import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.junit.Test;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class MappedByteBufferPoolTest
{
@Test
@ -93,8 +93,10 @@ public class MappedByteBufferPoolTest
}
/**
* In a scenario where MappedByteBufferPool is being used improperly, such as releasing a buffer that wasn't created/acquired by the MappedByteBufferPool,
* an assertion is tested for.
* In a scenario where MappedByteBufferPool is being used improperly,
* such as releasing a buffer that wasn't created/acquired by the
* MappedByteBufferPool, an assertion is tested for.
*
* @throws Exception test failure
*/
@Test
@ -133,8 +135,6 @@ public class MappedByteBufferPoolTest
buffer = pool.acquire(1024,false);
assertThat(BufferUtil.toDetailString(buffer),containsString("@T00000002"));
}
@Test
public void testMaxQueue() throws Exception
@ -157,6 +157,5 @@ public class MappedByteBufferPoolTest
bufferPool.release(buffer3);
assertEquals(2, bucket.size());
}
}

View File

@ -53,8 +53,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.Locker;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
import org.eclipse.jetty.util.thread.Scheduler;
import org.eclipse.jetty.util.thread.ThreadBudget;
import org.eclipse.jetty.util.thread.ThreadPool;
import org.eclipse.jetty.util.thread.ThreadPoolBudget;
/**
* <p>An abstract implementation of {@link Connector} that provides a {@link ConnectionFactory} mechanism
@ -160,8 +159,7 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co
private String _name;
private int _acceptorPriorityDelta=-2;
private boolean _accepting = true;
private ThreadBudget.Lease lease;
private ThreadPoolBudget.Lease _lease;
/**
* @param server The server this connector will be added to. Must not be null.
@ -276,7 +274,7 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co
throw new IllegalStateException("No protocol factory for SSL next protocol: '" + next + "' in " + this);
}
lease = ThreadBudget.leaseFrom(getExecutor(),this,_acceptors.length);
_lease = ThreadPoolBudget.leaseFrom(getExecutor(),this,_acceptors.length);
super.doStart();
_stopping=new CountDownLatch(_acceptors.length);
@ -312,8 +310,8 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co
@Override
protected void doStop() throws Exception
{
if (lease!=null)
lease.close();
if (_lease!=null)
_lease.close();
// Tell the acceptors we are stopping
interruptAcceptors();

View File

@ -24,7 +24,6 @@ import java.net.InetSocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
@ -66,9 +65,7 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Locker;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ShutdownThread;
import org.eclipse.jetty.util.thread.ThreadBudget;
import org.eclipse.jetty.util.thread.ThreadPool;
import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
/* ------------------------------------------------------------ */
/** Jetty HTTP Servlet Server.

View File

@ -21,8 +21,8 @@ package org.eclipse.jetty.server;
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ThreadBudget;
import org.eclipse.jetty.util.thread.ThreadPool;
import org.eclipse.jetty.util.thread.ThreadPoolBudget;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
@ -61,7 +61,7 @@ public class InsufficientThreadsDetectionTest
}
catch(IllegalStateException e)
{
Log.getLogger(ThreadBudget.class).warn(e.toString());
Log.getLogger(ThreadPoolBudget.class).warn(e.toString());
}
}
@ -82,8 +82,6 @@ public class InsufficientThreadsDetectionTest
_server.start();
}
// Github issue #586
@Test
public void testCaseForMultipleConnectors() throws Exception
{
@ -95,22 +93,21 @@ public class InsufficientThreadsDetectionTest
// first connector consumes 3 threads from server pool
_server.addConnector(new ServerConnector(_server, null, null, null, 1, 1, new HttpConnectionFactory()));
// second connect also require 4 threads but uses own executor, so its threads should not be counted
// second connect also require 3 threads but uses own executor, so its threads should not be counted
final QueuedThreadPool connectorPool = new QueuedThreadPool(4, 4);
_server.addConnector(new ServerConnector(_server, connectorPool, null, null, 1, 1, new HttpConnectionFactory()));
// first connector consumes 3 threads from server pool
// third connector consumes 3 threads from server pool
_server.addConnector(new ServerConnector(_server, null, null, null, 1, 1, new HttpConnectionFactory()));
// should not throw exception because limit was not overflown
// should throw exception because limit was overflown
_server.start();
Assert.fail();
}
catch(IllegalStateException e)
{
Log.getLogger(ThreadBudget.class).warn(e.toString());
Log.getLogger(ThreadPoolBudget.class).warn(e.toString());
}
}
}

View File

@ -18,11 +18,7 @@
package org.eclipse.jetty.util.component;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* A Container

View File

@ -65,7 +65,7 @@ public class QueuedThreadPool extends AbstractLifeCycle implements SizedThreadPo
private boolean _daemon = false;
private boolean _detailedDump = false;
private int _lowThreadsThreshold = 1;
private ThreadBudget _budget;
private ThreadPoolBudget _budget;
public QueuedThreadPool()
{
@ -106,23 +106,20 @@ public class QueuedThreadPool extends AbstractLifeCycle implements SizedThreadPo
}
_jobs=queue;
_threadGroup=threadGroup;
_budget=new ThreadBudget(this);
_budget=new ThreadPoolBudget(this);
}
@Override
public ThreadBudget getThreadBudget()
public ThreadPoolBudget getThreadPoolBudget()
{
return _budget;
}
public void setThreadBudget(ThreadBudget budget)
public void setThreadPoolBudget(ThreadPoolBudget budget)
{
if (budget!=null && budget.getSizedThreadPool()!=this)
throw new IllegalArgumentException();
synchronized (this)
{
_budget = budget;
}
_budget = budget;
}
@Override

View File

@ -64,7 +64,7 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements Executo
private final AtomicInteger _size = new AtomicInteger();
private final AtomicInteger _pending = new AtomicInteger();
private ThreadBudget.Lease _lease;
private ThreadPoolBudget.Lease _lease;
private Object _owner;
private long _idleTime = 1L;
private TimeUnit _idleTimeUnit = TimeUnit.MINUTES;
@ -168,7 +168,7 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements Executo
@Override
public void doStart() throws Exception
{
_lease = ThreadBudget.leaseFrom(getExecutor(),this,_capacity);
_lease = ThreadPoolBudget.leaseFrom(getExecutor(),this,_capacity);
super.doStart();
}

View File

@ -71,8 +71,7 @@ public interface ThreadPool extends Executor
int getMaxThreads();
void setMinThreads(int threads);
void setMaxThreads(int threads);
default ThreadBudget getThreadBudget()
default ThreadPoolBudget getThreadPoolBudget()
{
return null;
}

View File

@ -31,11 +31,11 @@ import org.eclipse.jetty.util.log.Logger;
/**
* <p>A budget of required thread usage, used to warn or error for insufficient configured threads.</p>
*
* @see ThreadPool.SizedThreadPool#getThreadBudget()
* @see ThreadPool.SizedThreadPool#getThreadPoolBudget()
*/
public class ThreadBudget
public class ThreadPoolBudget
{
static final Logger LOG = Log.getLogger(ThreadBudget.class);
static final Logger LOG = Log.getLogger(ThreadPoolBudget.class);
public interface Lease extends Closeable
{
@ -92,10 +92,10 @@ public class ThreadBudget
final int warnAt;
/**
* Construct a bedget for a SizedThreadPool, with the warning level set by heuristic.
* Construct a budget for a SizedThreadPool, with the warning level set by heuristic.
* @param pool The pool to budget thread allocation for.
*/
public ThreadBudget(ThreadPool.SizedThreadPool pool)
public ThreadPoolBudget(ThreadPool.SizedThreadPool pool)
{
this(pool,Runtime.getRuntime().availableProcessors());
}
@ -104,7 +104,7 @@ public class ThreadBudget
* @param pool The pool to budget thread allocation for.
* @param warnAt The level of free threads at which a warning is generated.
*/
public ThreadBudget(ThreadPool.SizedThreadPool pool, int warnAt)
public ThreadPoolBudget(ThreadPool.SizedThreadPool pool, int warnAt)
{
this.pool = pool;
this.warnAt = warnAt;
@ -139,20 +139,20 @@ public class ThreadBudget
int required = allocations.stream()
.mapToInt(Lease::getThreads)
.sum();
int maximum = pool.getMaxThreads();
int actual = maximum - required;
if (required>=maximum)
if (actual <= 0)
{
infoOnLeases();
throw new IllegalStateException(String.format("Insuffient configured threads: required=%d < max=%d for %s", required, maximum, pool));
throw new IllegalStateException(String.format("Insufficient configured threads: required=%d < max=%d for %s", required, maximum, pool));
}
if ((maximum-required) < warnAt)
if (actual < warnAt)
{
infoOnLeases();
if (warned.compareAndSet(false,true))
LOG.warn("Low configured threads: ( max={} - required={} ) < warnAt={} for {}", maximum, required, warnAt, pool);
LOG.warn("Low configured threads: (max={} - required={})={} < warnAt={} for {}", maximum, required, actual, warnAt, pool);
}
}
@ -169,7 +169,7 @@ public class ThreadBudget
{
if (executor instanceof ThreadPool.SizedThreadPool)
{
ThreadBudget budget = ((ThreadPool.SizedThreadPool)executor).getThreadBudget();
ThreadPoolBudget budget = ((ThreadPool.SizedThreadPool)executor).getThreadPoolBudget();
if (budget!=null)
return budget.leaseTo(leasee,threads);
}

View File

@ -417,7 +417,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.20</version>
<version>2.20.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>

View File

@ -38,5 +38,6 @@
<module>test-servlet-spec</module>
<module>test-jaas-webapp</module>
<module>test-jndi-webapp</module>
<module>test-http2-webapp</module>
</modules>
</project>

View File

@ -0,0 +1,175 @@
<?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">
<parent>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-webapps-parent</artifactId>
<version>9.4.8-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-http2-webapp</artifactId>
<name>Test :: Jetty HTTP2 Webapp</name>
<packaging>war</packaging>
<properties>
<bundle-symbolic-name>${project.groupId}.http2</bundle-symbolic-name>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-webapp</id>
<phase>pre-integration-test</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<type>war</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/webapp</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>jdk8</id>
<activation>
<jdk>[1.8,1.9)</jdk>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-alpn-boot</id>
<phase>pre-integration-test</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.mortbay.jetty.alpn</groupId>
<artifactId>alpn-boot</artifactId>
<version>${alpn.version}</version>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/alpn</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<argLine>-Xbootclasspath/p:${project.build.directory}/alpn/alpn-boot-${alpn.version}.jar</argLine>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-openjdk8-client</artifactId>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty.alpn</groupId>
<artifactId>alpn-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-openjdk8-server</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
<profile>
<id>jdk9</id>
<activation>
<jdk>[1.9,)</jdk>
</activation>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-java-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-java-server</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-client</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-server</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,138 @@
//
// ========================================================================
// 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.test.webapp;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
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.Promise;
import org.eclipse.jetty.util.ssl.SslContextFactory;
public class HTTP1Servlet extends HttpServlet
{
private SslContextFactory sslContextFactory;
private HTTP2Client http2Client;
@Override
public void init() throws ServletException
{
try
{
sslContextFactory = new SslContextFactory(true);
http2Client = new HTTP2Client();
http2Client.addBean(sslContextFactory);
http2Client.start();
}
catch (Exception x)
{
throw new ServletException(x);
}
}
@Override
public void destroy()
{
try
{
http2Client.stop();
}
catch (Exception x)
{
x.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String host = "localhost";
int port = request.getServerPort();
String contextPath = request.getContextPath();
ServletOutputStream output = response.getOutputStream();
AsyncContext asyncContext = request.startAsync();
http2Client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener.Adapter(), new Promise<Session>()
{
@Override
public void succeeded(Session session)
{
HttpURI uri = new HttpURI(request.getScheme(), host, port, contextPath + "/h2");
MetaData.Request metaData = new MetaData.Request(HttpMethod.GET.asString(), uri, HttpVersion.HTTP_2, new HttpFields());
HeadersFrame frame = new HeadersFrame(metaData, null, true);
session.newStream(frame, new Promise.Adapter<Stream>()
{
@Override
public void failed(Throwable x)
{
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
response.setHeader("X-Failure", "stream");
asyncContext.complete();
}
}, new Stream.Listener.Adapter()
{
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
try
{
ByteBuffer buffer = frame.getData();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
output.write(bytes);
callback.succeeded();
if (frame.isEndStream())
asyncContext.complete();
}
catch (IOException x)
{
asyncContext.complete();
}
}
});
}
@Override
public void failed(Throwable x)
{
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
response.setHeader("X-Failure", "session");
asyncContext.complete();
}
});
}
}

View File

@ -0,0 +1,35 @@
//
// ========================================================================
// 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.test.webapp;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HTTP2Servlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.getOutputStream().print("ok");
}
}

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>h1</servlet-name>
<servlet-class>org.eclipse.jetty.test.webapp.HTTP1Servlet</servlet-class>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>h1</servlet-name>
<url-pattern>/h1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>h2</servlet-name>
<servlet-class>org.eclipse.jetty.test.webapp.HTTP2Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>h2</servlet-name>
<url-pattern>/h2</url-pattern>
</servlet-mapping>
</web-app>

View File

@ -0,0 +1,96 @@
//
// ========================================================================
// 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.test.webapp;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.http.HttpScheme;
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;
import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.Assert;
import org.junit.Test;
public class HTTP2FromWebAppIT
{
@Test
public void testHTTP2FromWebApp() throws Exception
{
Server server = new Server();
SslContextFactory serverTLS = new SslContextFactory();
serverTLS.setKeyStorePath("src/test/resources/keystore.jks");
serverTLS.setKeyStorePassword("storepwd");
serverTLS.setCipherComparator(new HTTP2Cipher.CipherComparator());
HttpConfiguration httpsConfig = new HttpConfiguration();
httpsConfig.addCustomizer(new SecureRequestCustomizer());
HttpConnectionFactory h1 = new HttpConnectionFactory(httpsConfig);
ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
alpn.setDefaultProtocol(h1.getProtocol());
SslConnectionFactory ssl = new SslConnectionFactory(serverTLS, alpn.getProtocol());
HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpsConfig);
ServerConnector connector = new ServerConnector(server, ssl, alpn, h2, h1);
server.addConnector(connector);
String contextPath = "/http2_from_webapp";
WebAppContext context = new WebAppContext("target/webapp", contextPath);
server.setHandler(context);
server.start();
try
{
SslContextFactory clientTLS = new SslContextFactory(true);
HttpClient client = new HttpClient(clientTLS);
client.start();
try
{
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(HttpScheme.HTTPS.asString())
.path(contextPath + "/h1")
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals("ok", response.getContentAsString());
}
finally
{
client.stop();
}
}
finally
{
server.stop();
}
}
}

View File

@ -0,0 +1,3 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.LEVEL=INFO
#org.eclipse.jetty.alpn.LEVEL=DEBUG