Merge remote-tracking branch 'origin/jetty-9.4.x' into jetty-9.4.x-3278-empty-resourcecollection
This commit is contained in:
commit
7ee7554b8f
|
@ -45,7 +45,6 @@ pipeline {
|
|||
options { timeout(time: 120, unit: 'MINUTES') }
|
||||
steps {
|
||||
mavenBuild("jdk11", "-Pmongodb install", "maven3", false)
|
||||
junit '**/target/surefire-reports/TEST-*.xml,**/target/failsafe-reports/TEST-*.xml'
|
||||
warnings consoleParsers: [[parserName: 'Maven'], [parserName: 'Java']]
|
||||
maven_invoker reportsFilenamePattern: "**/target/invoker-reports/BUILD*.xml", invokerBuildDir: "**/target/it"
|
||||
}
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
<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</groupId>
|
||||
<artifactId>example-async-rest</artifactId>
|
||||
<version>9.4.15-SNAPSHOT</version>
|
||||
</parent>
|
||||
<version>9.4.15-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.eclipse.jetty.example-async-rest</groupId>
|
||||
<artifactId>example-async-rest-jar</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Example Async Rest :: Jar</name>
|
||||
<url>http://www.eclipse.org/jetty</url>
|
||||
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.examples.async.rest</bundle-symbolic-name>
|
||||
<bundle-symbolic-name>${project.parent.groupId}.examples.async.rest</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
<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</groupId>
|
||||
<artifactId>example-async-rest</artifactId>
|
||||
<version>9.4.15-SNAPSHOT</version>
|
||||
</parent>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.eclipse.jetty.example-async-rest</groupId>
|
||||
<artifactId>example-async-rest-webapp</artifactId>
|
||||
<packaging>war</packaging>
|
||||
<name>Example Async Rest :: Webapp</name>
|
||||
|
||||
<build>
|
||||
<finalName>async-rest</finalName>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.example-async-rest</groupId>
|
||||
|
@ -24,10 +28,10 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
<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.examples</groupId>
|
||||
<artifactId>examples-parent</artifactId>
|
||||
<version>9.4.15-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>example-async-rest</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>Example Async Rest</name>
|
||||
|
||||
<modules>
|
||||
<module>async-rest-jar</module>
|
||||
<module>async-rest-webapp</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
<?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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<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</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.4.15-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.eclipse.jetty.examples</groupId>
|
||||
<artifactId>examples-parent</artifactId>
|
||||
<name>Jetty Examples :: Parent</name>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<sonar.skip>true</sonar.skip>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
@ -26,6 +29,7 @@
|
|||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<modules>
|
||||
<!--
|
||||
- The async-rest and embedded are examples that have historical locations,
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package org.eclipse.jetty.alpn.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -34,11 +33,10 @@ import org.eclipse.jetty.io.EndPoint;
|
|||
import org.eclipse.jetty.io.NegotiatingClientConnectionFactory;
|
||||
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.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFactory implements SslHandshakeListener
|
||||
public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFactory
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(ALPNClientConnectionFactory.class);
|
||||
|
||||
|
@ -96,7 +94,7 @@ public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFact
|
|||
}
|
||||
|
||||
@Override
|
||||
public Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
|
||||
public Connection newConnection(EndPoint endPoint, Map<String, Object> context)
|
||||
{
|
||||
SSLEngine engine = (SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY);
|
||||
for (Client processor : processors)
|
||||
|
|
|
@ -182,12 +182,6 @@ public class HttpClient extends ContainerLifeCycle
|
|||
{
|
||||
this.transport = transport;
|
||||
addBean(transport);
|
||||
|
||||
if (sslContextFactory == null)
|
||||
{
|
||||
sslContextFactory = new SslContextFactory(false);
|
||||
sslContextFactory.setEndpointIdentificationAlgorithm("HTTPS");
|
||||
}
|
||||
this.sslContextFactory = sslContextFactory;
|
||||
addBean(sslContextFactory);
|
||||
addBean(handlers);
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.eclipse.jetty.util.BufferUtil;
|
|||
* specified to the constructors.</p>
|
||||
* <p>The content may be retrieved from {@link #onSuccess(Response)} or {@link #onComplete(Result)}
|
||||
* via {@link #getContent()} or {@link #getContentAsString()}.</p>
|
||||
* <p>Instances of this class are not reusable, so one must be allocated for each request.</p>
|
||||
*/
|
||||
public abstract class BufferingResponseListener extends Listener.Adapter
|
||||
{
|
||||
|
|
|
@ -32,6 +32,7 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
|
@ -325,7 +326,6 @@ public class HttpClientTLSTest
|
|||
@Test
|
||||
public void testHandshakeSucceededWithSessionResumption() throws Exception
|
||||
{
|
||||
|
||||
SslContextFactory serverTLSFactory = createSslContextFactory();
|
||||
startServer(serverTLSFactory, new EmptyServerHandler());
|
||||
|
||||
|
@ -405,7 +405,6 @@ public class HttpClientTLSTest
|
|||
@Test
|
||||
public void testClientRawCloseDoesNotInvalidateSession() throws Exception
|
||||
{
|
||||
|
||||
SslContextFactory serverTLSFactory = createSslContextFactory();
|
||||
startServer(serverTLSFactory, new EmptyServerHandler());
|
||||
|
||||
|
@ -527,4 +526,30 @@ public class HttpClientTLSTest
|
|||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHostNameVerificationFailure() throws Exception
|
||||
{
|
||||
SslContextFactory serverTLSFactory = createSslContextFactory();
|
||||
startServer(serverTLSFactory, new EmptyServerHandler());
|
||||
|
||||
SslContextFactory clientTLSFactory = createSslContextFactory();
|
||||
// Make sure the host name is not verified at the TLS level.
|
||||
clientTLSFactory.setEndpointIdentificationAlgorithm(null);
|
||||
// Add host name verification after the TLS handshake.
|
||||
clientTLSFactory.setHostnameVerifier((host, session) -> false);
|
||||
startClient(clientTLSFactory);
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(HttpScheme.HTTPS.asString())
|
||||
.send(result ->
|
||||
{
|
||||
Throwable failure = result.getFailure();
|
||||
if (failure instanceof SSLPeerUnverifiedException)
|
||||
latch.countDown();
|
||||
});
|
||||
|
||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.hazelcast.session;
|
|||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jetty.server.session.AbstractSessionDataStore;
|
||||
|
@ -74,7 +75,12 @@ public class HazelcastSessionDataStore
|
|||
public boolean delete( String id )
|
||||
throws Exception
|
||||
{
|
||||
return sessionDataMap == null ? false : sessionDataMap.remove( getCacheKey( id ) ) != null;
|
||||
if (sessionDataMap == null)
|
||||
return false;
|
||||
|
||||
//use delete which does not deserialize the SessionData object being removed
|
||||
sessionDataMap.delete( getCacheKey(id));
|
||||
return true;
|
||||
}
|
||||
|
||||
public IMap<String, SessionData> getSessionDataMap()
|
||||
|
|
|
@ -23,7 +23,10 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.ClientConnectionFactory;
|
||||
|
@ -100,6 +103,7 @@ public class SslClientConnectionFactory implements ClientConnectionFactory
|
|||
EndPoint appEndPoint = sslConnection.getDecryptedEndPoint();
|
||||
appEndPoint.setConnection(connectionFactory.newConnection(appEndPoint, context));
|
||||
|
||||
sslConnection.addHandshakeListener(new HTTPSHandshakeListener(context));
|
||||
customize(sslConnection, context);
|
||||
|
||||
return sslConnection;
|
||||
|
@ -124,4 +128,37 @@ public class SslClientConnectionFactory implements ClientConnectionFactory
|
|||
}
|
||||
return ClientConnectionFactory.super.customize(connection, context);
|
||||
}
|
||||
|
||||
private class HTTPSHandshakeListener implements SslHandshakeListener
|
||||
{
|
||||
private final Map<String, Object> context;
|
||||
|
||||
private HTTPSHandshakeListener(Map<String, Object> context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handshakeSucceeded(Event event) throws SSLException
|
||||
{
|
||||
HostnameVerifier verifier = sslContextFactory.getHostnameVerifier();
|
||||
if (verifier != null)
|
||||
{
|
||||
String host = (String)context.get(SSL_PEER_HOST_CONTEXT_KEY);
|
||||
try
|
||||
{
|
||||
if (!verifier.verify(host, event.getSSLEngine().getSession()))
|
||||
throw new SSLPeerUnverifiedException("Host name verification failed for host: " + host);
|
||||
}
|
||||
catch (SSLException x)
|
||||
{
|
||||
throw x;
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
throw (SSLException)new SSLPeerUnverifiedException("Host name verification failed for host: " + host).initCause(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -759,7 +759,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
|||
}
|
||||
}
|
||||
|
||||
private void handshakeSucceeded()
|
||||
private void handshakeSucceeded() throws SSLException
|
||||
{
|
||||
if (_handshake.compareAndSet(Handshake.INITIAL, Handshake.SUCCEEDED))
|
||||
{
|
||||
|
@ -1182,7 +1182,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
|||
}
|
||||
}
|
||||
|
||||
private void notifyHandshakeSucceeded(SSLEngine sslEngine)
|
||||
private void notifyHandshakeSucceeded(SSLEngine sslEngine) throws SSLException
|
||||
{
|
||||
SslHandshakeListener.Event event = null;
|
||||
for (SslHandshakeListener listener : handshakeListeners)
|
||||
|
@ -1193,6 +1193,10 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
|||
{
|
||||
listener.handshakeSucceeded(event);
|
||||
}
|
||||
catch (SSLException x)
|
||||
{
|
||||
throw x;
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.EventListener;
|
|||
import java.util.EventObject;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
/**
|
||||
* <p>Implementations of this interface are notified of TLS handshake events.</p>
|
||||
|
@ -36,7 +37,7 @@ public interface SslHandshakeListener extends EventListener
|
|||
*
|
||||
* @param event the event object carrying information about the TLS handshake event
|
||||
*/
|
||||
default void handshakeSucceeded(Event event)
|
||||
default void handshakeSucceeded(Event event) throws SSLException
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
<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</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.4.15-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-jmh</artifactId>
|
||||
<name>Jetty :: Jmh</name>
|
||||
<description>Jmh classes for Jetty</description>
|
||||
<url>http://www.eclipse.org/jetty</url>
|
||||
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.jmh</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
@ -39,8 +45,7 @@
|
|||
<finalName>${jmhjar.name}</finalName>
|
||||
<shadeTestJar>true</shadeTestJar>
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>org.openjdk.jmh.Main</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
|
@ -72,6 +77,7 @@
|
|||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<name>Jetty :: OSGi ALPN Fragment</name>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<bundle-symbolic-name>org.eclipse.jetty.osgi.alpn.fragment</bundle-symbolic-name>
|
||||
<bundle-symbolic-name>${project.groupId}.alpn.fragment</bundle-symbolic-name>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
|
|
|
@ -400,8 +400,7 @@ public class CrossOriginFilter implements Filter
|
|||
{
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, origin);
|
||||
//W3C CORS spec http://www.w3.org/TR/cors/#resource-implementation
|
||||
if (!anyOriginAllowed)
|
||||
response.addHeader("Vary", ORIGIN_HEADER);
|
||||
response.addHeader("Vary", ORIGIN_HEADER);
|
||||
if (allowCredentials)
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER, "true");
|
||||
if (!exposedHeaders.isEmpty())
|
||||
|
|
|
@ -135,7 +135,7 @@ public class CrossOriginFilterTest
|
|||
Set<String> fieldNames = response.getFieldNamesCollection();
|
||||
assertThat(response.toString(), CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, isIn(fieldNames));
|
||||
assertThat(response.toString(), CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER, isIn(fieldNames));
|
||||
assertThat(response.toString(), "Vary", not(isIn(fieldNames)));
|
||||
assertThat(response.toString(), "Vary", isIn(fieldNames));
|
||||
assertTrue(latch.await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ import java.util.regex.Matcher;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.net.ssl.CertPathTrustManagerParameters;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SNIHostName;
|
||||
|
@ -194,6 +195,7 @@ public class SslContextFactory extends AbstractLifeCycle implements Dumpable
|
|||
private int _renegotiationLimit = 5;
|
||||
private Factory _factory;
|
||||
private PKIXCertPathChecker _pkixCertPathChecker;
|
||||
private HostnameVerifier _hostnameVerifier;
|
||||
|
||||
/**
|
||||
* Construct an instance of SslContextFactory
|
||||
|
@ -1596,6 +1598,31 @@ public class SslContextFactory extends AbstractLifeCycle implements Dumpable
|
|||
_sslSessionTimeout = sslSessionTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the HostnameVerifier used by a client to verify host names in the server certificate
|
||||
*/
|
||||
public HostnameVerifier getHostnameVerifier()
|
||||
{
|
||||
return _hostnameVerifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets a {@code HostnameVerifier} used by a client to verify host names in the server certificate.</p>
|
||||
* <p>The {@code HostnameVerifier} works in conjunction with {@link #setEndpointIdentificationAlgorithm(String)}.</p>
|
||||
* <p>When {@code endpointIdentificationAlgorithm=="HTTPS"} (the default) the JDK TLS implementation
|
||||
* checks that the host name indication set by the client matches the host names in the server certificate.
|
||||
* If this check passes successfully, the {@code HostnameVerifier} is invoked and the application
|
||||
* can perform additional checks and allow/deny the connection to the server.</p>
|
||||
* <p>When {@code endpointIdentificationAlgorithm==null} the JDK TLS implementation will not check
|
||||
* the host names, and any check is therefore performed only by the {@code HostnameVerifier.}</p>
|
||||
*
|
||||
* @param hostnameVerifier the HostnameVerifier used by a client to verify host names in the server certificate
|
||||
*/
|
||||
public void setHostnameVerifier(HostnameVerifier hostnameVerifier)
|
||||
{
|
||||
_hostnameVerifier = hostnameVerifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the password object for the given realm.
|
||||
*
|
||||
|
|
|
@ -278,12 +278,20 @@ public class WebAppClassLoader extends URLClassLoader
|
|||
StringTokenizer tokenizer= new StringTokenizer(classPath, ",;");
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
Resource resource= _context.newResource(tokenizer.nextToken().trim());
|
||||
String token = tokenizer.nextToken().trim();
|
||||
Resource resource= _context.newResource(token);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Path resource=" + resource);
|
||||
|
||||
// Add the resource
|
||||
if (resource.isDirectory() && resource instanceof ResourceCollection)
|
||||
if(token.endsWith("*"))
|
||||
{
|
||||
if(token.length() > 1)
|
||||
{
|
||||
token = token.substring(0, token.length() - 1);
|
||||
resource= _context.newResource(token);
|
||||
addJars(resource);
|
||||
}
|
||||
} else if (resource.isDirectory() && resource instanceof ResourceCollection)
|
||||
addClassPath(resource);
|
||||
else
|
||||
{
|
||||
|
@ -525,7 +533,8 @@ public class WebAppClassLoader extends URLClassLoader
|
|||
// If it is a server class, doesn't matter as we have loaded it from the
|
||||
// webapp
|
||||
webapp_class = this.findClass(name);
|
||||
resolveClass(webapp_class);
|
||||
if (resolve)
|
||||
resolveClass(webapp_class);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("PLP webapp loaded {}",webapp_class);
|
||||
return webapp_class;
|
||||
|
|
|
@ -64,6 +64,7 @@ public class WebAppClassLoaderTest
|
|||
_context = new WebAppContext();
|
||||
_context.setBaseResource(webapp);
|
||||
_context.setContextPath("/test");
|
||||
_context.setExtraClasspath("src/test/resources/ext/*");
|
||||
|
||||
_loader = new WebAppClassLoader(_context);
|
||||
_loader.addJars(webapp.addPath("WEB-INF/lib"));
|
||||
|
@ -105,6 +106,10 @@ public class WebAppClassLoaderTest
|
|||
assertCanLoadClass("org.acme.webapp.ClassInJarB");
|
||||
assertCanLoadClass("org.acme.other.ClassInClassesC");
|
||||
|
||||
assertCanLoadClass("org.acme.extone.Main");
|
||||
assertCanLoadClass("org.acme.exttwo.Main");
|
||||
assertCantLoadClass("org.acme.extthree.Main");
|
||||
|
||||
assertCantLoadClass("org.eclipse.jetty.webapp.Configuration");
|
||||
|
||||
Class<?> clazzA = _loader.loadClass("org.acme.webapp.ClassInJarA");
|
||||
|
@ -119,6 +124,10 @@ public class WebAppClassLoaderTest
|
|||
assertCanLoadClass("org.acme.webapp.ClassInJarB");
|
||||
assertCanLoadClass("org.acme.other.ClassInClassesC");
|
||||
|
||||
assertCanLoadClass("org.acme.extone.Main");
|
||||
assertCanLoadClass("org.acme.exttwo.Main");
|
||||
assertCantLoadClass("org.acme.extthree.Main");
|
||||
|
||||
assertCantLoadClass("org.eclipse.jetty.webapp.Configuration");
|
||||
|
||||
Class<?> clazzA = _loader.loadClass("org.acme.webapp.ClassInJarA");
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -25,7 +25,6 @@ import java.nio.ByteBuffer;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.eclipse.jetty.io.AbstractConnection;
|
||||
|
@ -125,12 +124,11 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
|
|||
private final Generator generator;
|
||||
private final Parser parser;
|
||||
private final WebSocketPolicy policy;
|
||||
private final AtomicBoolean suspendToken;
|
||||
private final ReadState readState;
|
||||
private final FrameFlusher flusher;
|
||||
private final String id;
|
||||
private WebSocketSession session;
|
||||
private List<ExtensionConfig> extensions;
|
||||
private boolean isFilling;
|
||||
private ByteBuffer prefillBuffer;
|
||||
private ReadMode readMode = ReadMode.PARSE;
|
||||
private IOState ioState;
|
||||
|
@ -147,7 +145,7 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
|
|||
this.parser = new Parser(policy,bufferPool);
|
||||
this.scheduler = scheduler;
|
||||
this.extensions = new ArrayList<>();
|
||||
this.suspendToken = new AtomicBoolean(false);
|
||||
this.readState = new ReadState();
|
||||
this.ioState = new IOState();
|
||||
this.ioState.addListener(this);
|
||||
this.flusher = new Flusher(bufferPool,generator,endp);
|
||||
|
@ -302,7 +300,7 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
|
|||
@Override
|
||||
public boolean isReading()
|
||||
{
|
||||
return isFilling;
|
||||
return readState.isReading();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -384,8 +382,6 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
|
|||
|
||||
try
|
||||
{
|
||||
isFilling = true;
|
||||
|
||||
if(readMode == ReadMode.PARSE)
|
||||
{
|
||||
readMode = readParse(buffer);
|
||||
|
@ -400,14 +396,10 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
|
|||
bufferPool.release(buffer);
|
||||
}
|
||||
|
||||
if ((readMode != ReadMode.EOF) && (suspendToken.get() == false))
|
||||
{
|
||||
if (readMode == ReadMode.EOF)
|
||||
readState.eof();
|
||||
else if (!readState.suspend())
|
||||
fillInterested();
|
||||
}
|
||||
else
|
||||
{
|
||||
isFilling = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -586,13 +578,8 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
|
|||
@Override
|
||||
public void resume()
|
||||
{
|
||||
if (suspendToken.getAndSet(false))
|
||||
{
|
||||
if (!isReading())
|
||||
{
|
||||
fillInterested();
|
||||
}
|
||||
}
|
||||
if (readState.resume())
|
||||
fillInterested();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -627,7 +614,7 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
|
|||
@Override
|
||||
public SuspendToken suspend()
|
||||
{
|
||||
suspendToken.set(true);
|
||||
readState.suspending();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -652,43 +639,6 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
|
|||
hashCode(),
|
||||
ioState,flusher,generator,parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
|
||||
EndPoint endp = getEndPoint();
|
||||
if(endp != null)
|
||||
{
|
||||
result = prime * result + endp.getLocalAddress().hashCode();
|
||||
result = prime * result + endp.getRemoteAddress().hashCode();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
AbstractWebSocketConnection other = (AbstractWebSocketConnection)obj;
|
||||
EndPoint endp = getEndPoint();
|
||||
EndPoint otherEndp = other.getEndPoint();
|
||||
if (endp == null)
|
||||
{
|
||||
if (otherEndp != null)
|
||||
return false;
|
||||
}
|
||||
else if (!endp.equals(otherEndp))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra bytes from the initial HTTP upgrade that need to
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.io;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
class ReadState
|
||||
{
|
||||
private final AtomicReference<State> state = new AtomicReference<>(State.READING);
|
||||
|
||||
boolean isReading()
|
||||
{
|
||||
return state.get() == State.READING;
|
||||
}
|
||||
|
||||
boolean isSuspended()
|
||||
{
|
||||
State current = state.get();
|
||||
return current == State.SUSPENDED || current == State.EOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that reads from the connection be suspended when {@link #suspend()} is called.
|
||||
*
|
||||
* @return whether the suspending was successful
|
||||
*/
|
||||
boolean suspending()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
State current = state.get();
|
||||
switch (current)
|
||||
{
|
||||
case READING:
|
||||
if (state.compareAndSet(current, State.SUSPENDING))
|
||||
return true;
|
||||
break;
|
||||
case EOF:
|
||||
return false;
|
||||
default:
|
||||
throw new IllegalStateException(toString(current));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspends reads from the connection if {@link #suspending()} was called.
|
||||
*
|
||||
* @return whether reads from the connection should be suspended
|
||||
*/
|
||||
boolean suspend()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
State current = state.get();
|
||||
switch (current)
|
||||
{
|
||||
case READING:
|
||||
return false;
|
||||
case SUSPENDING:
|
||||
if (state.compareAndSet(current, State.SUSPENDED))
|
||||
return true;
|
||||
break;
|
||||
case EOF:
|
||||
return true;
|
||||
default:
|
||||
throw new IllegalStateException(toString(current));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if reads from the connection were suspended and should now resume.
|
||||
*/
|
||||
boolean resume()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
State current = state.get();
|
||||
switch (current)
|
||||
{
|
||||
case SUSPENDING:
|
||||
if (state.compareAndSet(current, State.READING))
|
||||
return false;
|
||||
break;
|
||||
case SUSPENDED:
|
||||
if (state.compareAndSet(current, State.READING))
|
||||
return true;
|
||||
break;
|
||||
case EOF:
|
||||
return false;
|
||||
default:
|
||||
throw new IllegalStateException(toString(current));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void eof()
|
||||
{
|
||||
state.set(State.EOF);
|
||||
}
|
||||
|
||||
private String toString(State state)
|
||||
{
|
||||
return String.format("%s@%x[%s]", getClass().getSimpleName(), hashCode(), state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return toString(state.get());
|
||||
}
|
||||
|
||||
private enum State
|
||||
{
|
||||
/**
|
||||
* Reading from the connection.
|
||||
*/
|
||||
READING,
|
||||
|
||||
/**
|
||||
* Suspend has been requested but not yet taken effect.
|
||||
*/
|
||||
SUSPENDING,
|
||||
|
||||
/**
|
||||
* Suspended, won't read from the connection until resumed.
|
||||
*/
|
||||
SUSPENDED,
|
||||
|
||||
/**
|
||||
* Won't read from the connection (terminal state).
|
||||
*/
|
||||
EOF,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.common.io;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class ReadStateTest
|
||||
{
|
||||
@Test
|
||||
public void testReading()
|
||||
{
|
||||
ReadState readState = new ReadState();
|
||||
assertThat("Initially reading", readState.isReading(), is(true));
|
||||
|
||||
assertThat("No prior suspending", readState.suspend(), is(false));
|
||||
assertThat("No prior suspending", readState.isSuspended(), is(false));
|
||||
|
||||
assertThrows(IllegalStateException.class, readState::resume, "No suspending to resume");
|
||||
assertThat("No suspending to resume", readState.isSuspended(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuspendingThenResume()
|
||||
{
|
||||
ReadState readState = new ReadState();
|
||||
assertThat("Initially reading", readState.isReading(), is(true));
|
||||
|
||||
assertTrue(readState.suspending());
|
||||
assertThat("Suspending doesn't take effect immediately", readState.isSuspended(), is(false));
|
||||
|
||||
assertThat("Resume from suspending requires no followup", readState.resume(), is(false));
|
||||
assertThat("Resume from suspending requires no followup", readState.isSuspended(), is(false));
|
||||
|
||||
assertThat("Suspending was discarded", readState.suspend(), is(false));
|
||||
assertThat("Suspending was discarded", readState.isSuspended(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuspendingThenSuspendThenResume()
|
||||
{
|
||||
ReadState readState = new ReadState();
|
||||
assertThat("Initially reading", readState.isReading(), is(true));
|
||||
|
||||
assertThat(readState.suspending(), is(true));
|
||||
assertThat("Suspending doesn't take effect immediately", readState.isSuspended(), is(false));
|
||||
|
||||
assertThat("Suspended", readState.suspend(), is(true));
|
||||
assertThat("Suspended", readState.isSuspended(), is(true));
|
||||
|
||||
assertThat("Resumed", readState.resume(), is(true));
|
||||
assertThat("Resumed", readState.isSuspended(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEof()
|
||||
{
|
||||
ReadState readState = new ReadState();
|
||||
readState.eof();
|
||||
|
||||
assertThat(readState.isReading(), is(false));
|
||||
assertThat(readState.isSuspended(), is(true));
|
||||
assertThat(readState.suspend(), is(true));
|
||||
|
||||
assertThat(readState.suspending(), is(false));
|
||||
assertThat(readState.isSuspended(), is(true));
|
||||
|
||||
assertThat(readState.suspend(), is(true));
|
||||
assertThat(readState.isSuspended(), is(true));
|
||||
|
||||
assertThat(readState.resume(), is(false));
|
||||
assertThat(readState.isSuspended(), is(true));
|
||||
}
|
||||
}
|
5
pom.xml
5
pom.xml
|
@ -34,13 +34,15 @@
|
|||
<tycho-version>1.2.0</tycho-version>
|
||||
<cbi-plugins.version>1.1.5</cbi-plugins.version>
|
||||
<junit.version>5.3.1</junit.version>
|
||||
<maven.version>3.5.0</maven.version>
|
||||
<maven.version>3.6.0</maven.version>
|
||||
<maven.resolver.version>1.3.1</maven.resolver.version>
|
||||
<javax.servlet.api.version>3.1.0</javax.servlet.api.version>
|
||||
<weld.version>2.4.5.Final</weld.version>
|
||||
<jetty.perf-helper.version>1.0.5</jetty.perf-helper.version>
|
||||
<unix.socket.tmp></unix.socket.tmp>
|
||||
<!-- enable or not TestTracker junit5 extension i.e log message when test method is starting -->
|
||||
<jetty.testtracker.log>false</jetty.testtracker.log>
|
||||
<jpms-module-name>${bundle-symbolic-name}</jpms-module-name>
|
||||
|
||||
<!-- some maven plugins versions -->
|
||||
<maven.surefire.version>3.0.0-M2</maven.surefire.version>
|
||||
|
@ -514,6 +516,7 @@
|
|||
<archive>
|
||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||
<manifestEntries>
|
||||
<Automatic-Module-Name>${jpms-module-name}</Automatic-Module-Name>
|
||||
<Implementation-Version>${project.version}</Implementation-Version>
|
||||
<Implementation-Vendor>Eclipse Jetty Project</Implementation-Vendor>
|
||||
<url>${jetty.url}</url>
|
||||
|
|
|
@ -67,5 +67,6 @@
|
|||
<module>test-quickstart</module>
|
||||
<module>test-jmx</module>
|
||||
<module>test-http-client-transport</module>
|
||||
<module>test-distribution</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
<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>
|
||||
<artifactId>tests-parent</artifactId>
|
||||
<groupId>org.eclipse.jetty.tests</groupId>
|
||||
<version>9.4.15-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>test-distribution</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.tests.distribution</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-util</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-artifact</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-resolver-provider</artifactId>
|
||||
<version>${maven.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
<artifactId>maven-resolver-connector-basic</artifactId>
|
||||
<version>${maven.resolver.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
<artifactId>maven-resolver-transport-file</artifactId>
|
||||
<version>${maven.resolver.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
<artifactId>maven-resolver-transport-http</artifactId>
|
||||
<version>${maven.resolver.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-distribution</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>zip</type>
|
||||
<scope>test</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.http2</groupId>
|
||||
<artifactId>http2-http-client-transport</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.tests</groupId>
|
||||
<artifactId>test-simple-webapp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>war</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||
<artifactId>jetty-test-helper</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<mavenRepoPath>${settings.localRepository}</mavenRepoPath>
|
||||
<jettyVersion>${project.version}</jettyVersion>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,604 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2019 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.tests.distribution;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
|
||||
import org.codehaus.plexus.util.IOUtil;
|
||||
import org.eclipse.aether.AbstractRepositoryListener;
|
||||
import org.eclipse.aether.DefaultRepositorySystemSession;
|
||||
import org.eclipse.aether.RepositoryEvent;
|
||||
import org.eclipse.aether.RepositorySystem;
|
||||
import org.eclipse.aether.RepositorySystemSession;
|
||||
import org.eclipse.aether.artifact.Artifact;
|
||||
import org.eclipse.aether.artifact.DefaultArtifact;
|
||||
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
|
||||
import org.eclipse.aether.impl.DefaultServiceLocator;
|
||||
import org.eclipse.aether.repository.LocalRepository;
|
||||
import org.eclipse.aether.repository.RemoteRepository;
|
||||
import org.eclipse.aether.resolution.ArtifactRequest;
|
||||
import org.eclipse.aether.resolution.ArtifactResolutionException;
|
||||
import org.eclipse.aether.resolution.ArtifactResult;
|
||||
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
|
||||
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
|
||||
import org.eclipse.aether.transfer.AbstractTransferListener;
|
||||
import org.eclipse.aether.transport.file.FileTransporterFactory;
|
||||
import org.eclipse.aether.transport.http.HttpTransporterFactory;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/**
|
||||
* <p>Helper class to test the Jetty Distribution</p>.
|
||||
* <p>API can change without any further notice.</p>
|
||||
* <p>Usage:</p>
|
||||
* <pre>
|
||||
* // Create the distribution.
|
||||
* String jettyVersion = "9.4.14.v20181114";
|
||||
* DistributionTester distribution = DistributionTester.Builder.newInstance()
|
||||
* .jettyVersion(jettyVersion)
|
||||
* .jettyBase(Paths.get("demo-base"))
|
||||
* .build();
|
||||
*
|
||||
* // The first run initializes the Jetty Base.
|
||||
* try (DistributionTester.Run run1 = distribution.start("--create-startd", "--add-to-start=http2c,jsp,deploy"))
|
||||
* {
|
||||
* assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
|
||||
* assertEquals(0, run1.getExitValue());
|
||||
*
|
||||
* // Install a web application.
|
||||
* File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion);
|
||||
* distribution.installWarFile(war, "test");
|
||||
*
|
||||
* // The second run starts the distribution.
|
||||
* int port = 9090;
|
||||
* try (DistributionTester.Run run = distribution.start("jetty.http.port=" + port))
|
||||
* {
|
||||
* // Wait for Jetty to be fully started.
|
||||
* assertTrue(run1.awaitConsoleLogsFor("Started @", 20, TimeUnit.SECONDS));
|
||||
*
|
||||
* // Make a HTTP request to the web application.
|
||||
* HttpClient client = new HttpClient();
|
||||
* client.start();
|
||||
* ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp");
|
||||
* assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public class DistributionTester
|
||||
{
|
||||
private static final Logger LOGGER = Log.getLogger(DistributionTester.class);
|
||||
|
||||
private Config config;
|
||||
|
||||
private DistributionTester(Config config)
|
||||
{
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the distribution with the given arguments
|
||||
*
|
||||
* @param args arguments to use to start the distribution
|
||||
*/
|
||||
public DistributionTester.Run start(String... args) throws Exception
|
||||
{
|
||||
return start(Arrays.asList(args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the distribution with the arguments
|
||||
*
|
||||
* @param args arguments to use to start the distribution
|
||||
*/
|
||||
public DistributionTester.Run start(List<String> args) throws Exception
|
||||
{
|
||||
File jettyBaseDir = config.jettyBase.toFile();
|
||||
Path workDir = Files.createDirectories(jettyBaseDir.toPath().resolve("work"));
|
||||
|
||||
List<String> commands = new ArrayList<>();
|
||||
commands.add(getJavaExecutable());
|
||||
commands.add("-Djava.io.tmpdir=" + workDir.toAbsolutePath().toString());
|
||||
commands.add("-jar");
|
||||
commands.add(config.jettyHome.toAbsolutePath() + "/start.jar");
|
||||
commands.addAll(args);
|
||||
|
||||
LOGGER.info("Executing: {}", commands);
|
||||
LOGGER.info("Working Dir: {}", jettyBaseDir.getAbsolutePath());
|
||||
|
||||
ProcessBuilder pbCmd = new ProcessBuilder(commands);
|
||||
pbCmd.directory(jettyBaseDir);
|
||||
Process process = pbCmd.start();
|
||||
|
||||
return new Run(process);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a free port chosen by the OS that can be used to listen to
|
||||
* @throws IOException if a free port is not available
|
||||
*/
|
||||
public int freePort() throws IOException
|
||||
{
|
||||
try (ServerSocket server = new ServerSocket())
|
||||
{
|
||||
server.setReuseAddress(true);
|
||||
server.bind(new InetSocketAddress("localhost", 0));
|
||||
return server.getLocalPort();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs in {@code ${jetty.base}/webapps} the given war file under the given context path.
|
||||
*
|
||||
* @param warFile the war file to install
|
||||
* @param context the context path
|
||||
* @throws IOException if the installation fails
|
||||
*/
|
||||
public void installWarFile(File warFile, String context) throws IOException
|
||||
{
|
||||
//webapps
|
||||
Path webapps = Paths.get(config.jettyBase.toString(), "webapps", context);
|
||||
if (!Files.exists(webapps))
|
||||
Files.createDirectories(webapps);
|
||||
unzip(warFile, webapps.toFile());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves an artifact given its Maven coordinates.
|
||||
*
|
||||
* @param coordinates <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>
|
||||
* @return the artifact
|
||||
* @see #installWarFile(File, String)
|
||||
*/
|
||||
public File resolveArtifact(String coordinates) throws ArtifactResolutionException
|
||||
{
|
||||
RepositorySystem repositorySystem = newRepositorySystem();
|
||||
|
||||
Artifact artifact = new DefaultArtifact(coordinates);
|
||||
|
||||
RepositorySystemSession session = newRepositorySystemSession(repositorySystem);
|
||||
|
||||
ArtifactRequest artifactRequest = new ArtifactRequest();
|
||||
artifactRequest.setArtifact(artifact);
|
||||
artifactRequest.setRepositories(newRepositories());
|
||||
ArtifactResult artifactResult = repositorySystem.resolveArtifact(session, artifactRequest);
|
||||
|
||||
artifact = artifactResult.getArtifact();
|
||||
return artifact.getFile();
|
||||
}
|
||||
|
||||
private void init() throws Exception
|
||||
{
|
||||
if (config.jettyHome == null)
|
||||
config.jettyHome = resolveDistribution(config.jettyVersion);
|
||||
|
||||
if (config.jettyBase == null)
|
||||
{
|
||||
config.jettyBase = Files.createTempDirectory("jetty_base_");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!config.jettyBase.isAbsolute())
|
||||
config.jettyBase = config.jettyHome.resolve(config.jettyBase);
|
||||
}
|
||||
}
|
||||
|
||||
private String getJavaExecutable()
|
||||
{
|
||||
String[] javaExecutables = new String[]{"java", "java.exe"};
|
||||
File javaHomeDir = new File(System.getProperty("java.home"));
|
||||
for (String javaExecutable : javaExecutables)
|
||||
{
|
||||
File javaFile = new File(javaHomeDir, "bin" + File.separator + javaExecutable);
|
||||
if (javaFile.exists() && javaFile.isFile())
|
||||
return javaFile.getAbsolutePath();
|
||||
}
|
||||
return "java";
|
||||
}
|
||||
|
||||
private void unzip(File zipFile, File output) throws IOException
|
||||
{
|
||||
try (InputStream fileInputStream = Files.newInputStream(zipFile.toPath());
|
||||
ZipInputStream zipInputStream = new ZipInputStream(fileInputStream))
|
||||
{
|
||||
ZipEntry entry = zipInputStream.getNextEntry();
|
||||
while (entry != null)
|
||||
{
|
||||
if (entry.isDirectory())
|
||||
{
|
||||
File dir = new File(output, entry.getName());
|
||||
if (!Files.exists(dir.toPath()))
|
||||
{
|
||||
Files.createDirectories(dir.toPath());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read zipEntry and write to a file.
|
||||
File file = new File(output, entry.getName());
|
||||
if (!Files.exists(file.getParentFile().toPath()))
|
||||
{
|
||||
Files.createDirectories(file.getParentFile().toPath());
|
||||
}
|
||||
try (OutputStream outputStream = Files.newOutputStream(file.toPath()))
|
||||
{
|
||||
IOUtil.copy(zipInputStream, outputStream);
|
||||
}
|
||||
}
|
||||
// Get next entry
|
||||
entry = zipInputStream.getNextEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Path resolveDistribution(String version) throws Exception
|
||||
{
|
||||
File artifactFile = resolveArtifact("org.eclipse.jetty:jetty-distribution:zip:" + version);
|
||||
|
||||
// create tmp directory to unzip distribution
|
||||
Path tmp = Files.createTempDirectory("jetty_home_");
|
||||
File tmpFile = tmp.toFile();
|
||||
|
||||
unzip(artifactFile, tmpFile);
|
||||
|
||||
return tmp.resolve("jetty-distribution-" + version);
|
||||
}
|
||||
|
||||
private RepositorySystem newRepositorySystem()
|
||||
{
|
||||
DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
|
||||
locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
|
||||
locator.addService(TransporterFactory.class, FileTransporterFactory.class);
|
||||
locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
|
||||
|
||||
locator.setErrorHandler(new DefaultServiceLocator.ErrorHandler()
|
||||
{
|
||||
@Override
|
||||
public void serviceCreationFailed(Class<?> type, Class<?> impl, Throwable exception)
|
||||
{
|
||||
LOGGER.warn("Service creation failed for {} implementation {}: {}",
|
||||
type, impl, exception.getMessage(), exception);
|
||||
}
|
||||
});
|
||||
|
||||
return locator.getService(RepositorySystem.class);
|
||||
}
|
||||
|
||||
private List<RemoteRepository> newRepositories()
|
||||
{
|
||||
List<RemoteRepository> remoteRepositories = new ArrayList<>(config.mavenRemoteRepositories.size() + 1);
|
||||
config.mavenRemoteRepositories.forEach((key, value) -> remoteRepositories.add(new RemoteRepository.Builder(key, "default", value).build()));
|
||||
remoteRepositories.add(newCentralRepository());
|
||||
return remoteRepositories;
|
||||
}
|
||||
|
||||
private static RemoteRepository newCentralRepository()
|
||||
{
|
||||
return new RemoteRepository.Builder("central", "default", "https://repo.maven.apache.org/maven2/").build();
|
||||
}
|
||||
|
||||
private DefaultRepositorySystemSession newRepositorySystemSession(RepositorySystem system)
|
||||
{
|
||||
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
|
||||
|
||||
LocalRepository localRepo = new LocalRepository(config.mavenLocalRepository);
|
||||
session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));
|
||||
|
||||
session.setTransferListener(new LogTransferListener());
|
||||
session.setRepositoryListener(new LogRepositoryListener());
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
private static class Config
|
||||
{
|
||||
private Path jettyBase;
|
||||
private Path jettyHome;
|
||||
private String jettyVersion;
|
||||
private String mavenLocalRepository = System.getProperty("user.home") + "/.m2/repository";
|
||||
private Map<String, String> mavenRemoteRepositories = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s@%x{jettyBase=%s, jettyHome=%s, jettyVersion=%s, mavenLocalRepository=%s, mavenRemoteRepositories=%s}",
|
||||
getClass().getSimpleName(),
|
||||
hashCode(),
|
||||
jettyBase,
|
||||
jettyHome,
|
||||
jettyVersion,
|
||||
mavenLocalRepository,
|
||||
mavenRemoteRepositories);
|
||||
}
|
||||
}
|
||||
|
||||
private static class LogTransferListener extends AbstractTransferListener
|
||||
{
|
||||
// no op
|
||||
}
|
||||
|
||||
private static class LogRepositoryListener extends AbstractRepositoryListener
|
||||
{
|
||||
@Override
|
||||
public void artifactDownloaded(RepositoryEvent event)
|
||||
{
|
||||
LOGGER.debug("distribution downloaded to {}", event.getFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void artifactResolved(RepositoryEvent event)
|
||||
{
|
||||
LOGGER.debug("distribution resolved to {}", event.getFile());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A distribution run wraps the process that started the Jetty distribution.
|
||||
*/
|
||||
public static class Run implements Closeable
|
||||
{
|
||||
private final Process process;
|
||||
private final List<ConsoleStreamer> consoleStreamers = new ArrayList<>();
|
||||
private final List<String> logs = new ArrayList<>();
|
||||
|
||||
private Run(Process process)
|
||||
{
|
||||
this.process = process;
|
||||
consoleStreamers.add(startPump("STDOUT", process.getInputStream()));
|
||||
consoleStreamers.add(startPump("STDERR", process.getErrorStream()));
|
||||
}
|
||||
|
||||
private ConsoleStreamer startPump(String mode, InputStream stream)
|
||||
{
|
||||
ConsoleStreamer pump = new ConsoleStreamer(stream);
|
||||
Thread thread = new Thread(pump, "ConsoleStreamer/" + mode);
|
||||
thread.start();
|
||||
return pump;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the given time for the distribution process to stop.
|
||||
*
|
||||
* @param time the time to wait
|
||||
* @param unit the unit of time
|
||||
* @return true if the distribution process is terminated, false if the timeout elapsed
|
||||
* @throws InterruptedException if the wait is interrupted
|
||||
*/
|
||||
public boolean awaitFor(long time, TimeUnit unit) throws InterruptedException
|
||||
{
|
||||
boolean result = process.waitFor(time, unit);
|
||||
if (result)
|
||||
stopConsoleStreamers();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the distribution process exit value
|
||||
* @throws IllegalThreadStateException if the distribution process is not terminated yet
|
||||
*/
|
||||
public int getExitValue() throws IllegalThreadStateException
|
||||
{
|
||||
return process.exitValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the distribution process.
|
||||
*
|
||||
* @see #awaitFor(long, TimeUnit)
|
||||
*/
|
||||
public void stop()
|
||||
{
|
||||
process.destroy();
|
||||
stopConsoleStreamers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcibly destroys the distribution process.
|
||||
*/
|
||||
public void destroy()
|
||||
{
|
||||
process.destroyForcibly();
|
||||
stopConsoleStreamers();
|
||||
}
|
||||
|
||||
private void stopConsoleStreamers()
|
||||
{
|
||||
consoleStreamers.forEach(ConsoleStreamer::stop);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #destroy()
|
||||
*/
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Awaits the console logs to contain the given text, for the given amount of time.
|
||||
*
|
||||
* @param txt the text that must be present in the console logs
|
||||
* @param time the time to wait
|
||||
* @param unit the unit of time
|
||||
* @return true if the text was found, false if the timeout elapsed
|
||||
* @throws InterruptedException if the wait is interrupted
|
||||
*/
|
||||
public boolean awaitConsoleLogsFor(String txt, long time, TimeUnit unit) throws InterruptedException
|
||||
{
|
||||
long end = System.nanoTime() + unit.toNanos(time);
|
||||
while (System.nanoTime() < end)
|
||||
{
|
||||
boolean result = logs.stream().anyMatch(s -> s.contains(txt));
|
||||
if (result)
|
||||
return true;
|
||||
Thread.sleep(250);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple streamer for the console output from a Process
|
||||
*/
|
||||
private class ConsoleStreamer implements Runnable
|
||||
{
|
||||
private final BufferedReader reader;
|
||||
private volatile boolean stop;
|
||||
|
||||
public ConsoleStreamer(InputStream stream)
|
||||
{
|
||||
this.reader = new BufferedReader(new InputStreamReader(stream));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null && !stop)
|
||||
{
|
||||
LOGGER.info("{}", line);
|
||||
logs.add(line);
|
||||
}
|
||||
}
|
||||
catch (IOException ignore)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
finally
|
||||
{
|
||||
IO.close(reader);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop()
|
||||
{
|
||||
stop = true;
|
||||
IO.close(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder
|
||||
{
|
||||
private Config config = new Config();
|
||||
|
||||
private Builder()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jettyVersion the version to use (format: 9.4.14.v20181114 9.4.15-SNAPSHOT).
|
||||
* The distribution will be downloaded from local repository or remote
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public Builder jettyVersion(String jettyVersion)
|
||||
{
|
||||
config.jettyVersion = jettyVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jettyHome Path to the local exploded jetty distribution
|
||||
* if configured the jettyVersion parameter will not be used
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public Builder jettyHome(Path jettyHome)
|
||||
{
|
||||
config.jettyHome = jettyHome;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the path for the Jetty Base directory.</p>
|
||||
* <p>If the path is relative, it will be resolved against the Jetty Home directory.</p>
|
||||
*
|
||||
* @param jettyBase Path to the local Jetty Base directory
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public Builder jettyBase(Path jettyBase)
|
||||
{
|
||||
config.jettyBase = jettyBase;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mavenLocalRepository Path to the local maven repository
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public Builder mavenLocalRepository(String mavenLocalRepository)
|
||||
{
|
||||
config.mavenLocalRepository = mavenLocalRepository;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If needed to resolve the Jetty distribution from another Maven remote repositories
|
||||
*
|
||||
* @param id the id
|
||||
* @param url the Maven remote repository url
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public Builder addRemoteRepository(String id, String url)
|
||||
{
|
||||
config.mavenRemoteRepositories.put(id, url);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an empty instance of {@link Builder}
|
||||
*/
|
||||
public static Builder newInstance()
|
||||
{
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a new configured instance of {@link DistributionTester}
|
||||
*/
|
||||
public DistributionTester build() throws Exception
|
||||
{
|
||||
DistributionTester tester = new DistributionTester(config);
|
||||
tester.init();
|
||||
return tester;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2019 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.tests.distribution;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
|
||||
public class AbstractDistributionTest
|
||||
{
|
||||
protected HttpClient client;
|
||||
|
||||
protected void startHttpClient() throws Exception
|
||||
{
|
||||
startHttpClient(HttpClient::new);
|
||||
}
|
||||
|
||||
protected void startHttpClient(Supplier<HttpClient> supplier) throws Exception
|
||||
{
|
||||
client = supplier.get();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void dispose() throws Exception
|
||||
{
|
||||
if (client != null)
|
||||
client.stop();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2019 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.tests.distribution;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http2.client.HTTP2Client;
|
||||
import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.DisabledOnJre;
|
||||
import org.junit.jupiter.api.condition.JRE;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class DistributionTests extends AbstractDistributionTest
|
||||
{
|
||||
@Test
|
||||
public void testStartStop() throws Exception
|
||||
{
|
||||
String jettyVersion = System.getProperty("jettyVersion");
|
||||
DistributionTester distribution = DistributionTester.Builder.newInstance()
|
||||
.jettyVersion(jettyVersion)
|
||||
.mavenLocalRepository(System.getProperty("mavenRepoPath"))
|
||||
.build();
|
||||
|
||||
try (DistributionTester.Run run1 = distribution.start("--add-to-start=http"))
|
||||
{
|
||||
assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
|
||||
assertEquals(0, run1.getExitValue());
|
||||
|
||||
int port = distribution.freePort();
|
||||
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
|
||||
{
|
||||
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
|
||||
|
||||
startHttpClient();
|
||||
ContentResponse response = client.GET("http://localhost:" + port);
|
||||
assertEquals(HttpStatus.NOT_FOUND_404, response.getStatus());
|
||||
|
||||
run2.stop();
|
||||
assertTrue(run2.awaitFor(5, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleWebAppWithJSP() throws Exception
|
||||
{
|
||||
String jettyVersion = System.getProperty("jettyVersion");
|
||||
DistributionTester distribution = DistributionTester.Builder.newInstance()
|
||||
.jettyVersion(jettyVersion)
|
||||
.mavenLocalRepository(System.getProperty("mavenRepoPath"))
|
||||
.build();
|
||||
|
||||
String[] args1 = {
|
||||
"--create-startd",
|
||||
"--approve-all-licenses",
|
||||
"--add-to-start=resources,server,http,webapp,deploy,jsp,jmx,servlet,servlets"
|
||||
};
|
||||
try (DistributionTester.Run run1 = distribution.start(args1))
|
||||
{
|
||||
assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
|
||||
assertEquals(0, run1.getExitValue());
|
||||
|
||||
File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion);
|
||||
distribution.installWarFile(war, "test");
|
||||
|
||||
int port = distribution.freePort();
|
||||
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
|
||||
{
|
||||
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
|
||||
|
||||
startHttpClient();
|
||||
ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp");
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
assertThat(response.getContentAsString(), containsString("Hello"));
|
||||
assertThat(response.getContentAsString(), not(containsString("<%")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledOnJre(JRE.JAVA_8)
|
||||
public void testSimpleWebAppWithJSPOnModulePath() throws Exception
|
||||
{
|
||||
String jettyVersion = System.getProperty("jettyVersion");
|
||||
DistributionTester distribution = DistributionTester.Builder.newInstance()
|
||||
.jettyVersion(jettyVersion)
|
||||
.mavenLocalRepository(System.getProperty("mavenRepoPath"))
|
||||
.build();
|
||||
|
||||
String[] args1 = {
|
||||
"--create-startd",
|
||||
"--approve-all-licenses",
|
||||
"--add-to-start=resources,server,http,webapp,deploy,jsp,jmx,servlet,servlets"
|
||||
};
|
||||
try (DistributionTester.Run run1 = distribution.start(args1))
|
||||
{
|
||||
assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
|
||||
assertEquals(0, run1.getExitValue());
|
||||
|
||||
File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion);
|
||||
distribution.installWarFile(war, "test");
|
||||
|
||||
int port = distribution.freePort();
|
||||
String[] args2 = {
|
||||
"--jpms",
|
||||
"jetty.http.port=" + port
|
||||
};
|
||||
try (DistributionTester.Run run2 = distribution.start(args2))
|
||||
{
|
||||
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
|
||||
|
||||
startHttpClient();
|
||||
ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp");
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
assertThat(response.getContentAsString(), containsString("Hello"));
|
||||
assertThat(response.getContentAsString(), not(containsString("<%")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledOnJre(JRE.JAVA_8)
|
||||
public void testSimpleWebAppWithJSPOverH2C() throws Exception
|
||||
{
|
||||
String jettyVersion = System.getProperty("jettyVersion");
|
||||
DistributionTester distribution = DistributionTester.Builder.newInstance()
|
||||
.jettyVersion(jettyVersion)
|
||||
.mavenLocalRepository(System.getProperty("mavenRepoPath"))
|
||||
.build();
|
||||
|
||||
String[] args1 = {
|
||||
"--create-startd",
|
||||
"--add-to-start=http2c,jsp,deploy"
|
||||
};
|
||||
try (DistributionTester.Run run1 = distribution.start(args1))
|
||||
{
|
||||
assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
|
||||
assertEquals(0, run1.getExitValue());
|
||||
|
||||
File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion);
|
||||
distribution.installWarFile(war, "test");
|
||||
|
||||
int port = distribution.freePort();
|
||||
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
|
||||
{
|
||||
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
|
||||
|
||||
HTTP2Client h2Client = new HTTP2Client();
|
||||
startHttpClient(() -> new HttpClient(new HttpClientTransportOverHTTP2(h2Client), null));
|
||||
ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp");
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
assertThat(response.getContentAsString(), containsString("Hello"));
|
||||
assertThat(response.getContentAsString(), not(containsString("<%")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDemoBase() throws Exception
|
||||
{
|
||||
String jettyVersion = System.getProperty("jettyVersion");
|
||||
DistributionTester distribution = DistributionTester.Builder.newInstance()
|
||||
.jettyVersion(jettyVersion)
|
||||
.jettyBase(Paths.get("demo-base"))
|
||||
.mavenLocalRepository(System.getProperty("mavenRepoPath"))
|
||||
.build();
|
||||
|
||||
int port = distribution.freePort();
|
||||
try (DistributionTester.Run run1 = distribution.start("jetty.http.port=" + port))
|
||||
{
|
||||
assertTrue(run1.awaitConsoleLogsFor("Started @", 20, TimeUnit.SECONDS));
|
||||
|
||||
startHttpClient();
|
||||
ContentResponse response = client.GET("http://localhost:" + port + "/test/jsp/dump.jsp");
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
assertThat(response.getContentAsString(), containsString("PathInfo"));
|
||||
assertThat(response.getContentAsString(), not(containsString("<%")));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
#org.eclipse.jetty.LEVEL=DEBUG
|
|
@ -56,6 +56,7 @@ import org.eclipse.jetty.toolchain.test.JAR;
|
|||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.toolchain.test.PathAssert;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -315,19 +316,15 @@ public class JettyDistro
|
|||
|
||||
// The actual jetty-distribution-${version} directory is under this directory.
|
||||
// Lets find it.
|
||||
File subdirs[] = distroUnpackDir.listFiles(new FileFilter()
|
||||
{
|
||||
@Override
|
||||
public boolean accept(File path)
|
||||
File subdirs[] = distroUnpackDir.listFiles( path ->
|
||||
{
|
||||
if (!path.isDirectory())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return path.getName().startsWith(artifactName + "-");
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
if (subdirs.length == 0)
|
||||
{
|
||||
|
@ -550,7 +547,7 @@ public class JettyDistro
|
|||
|
||||
if (cmdLine == null || !cmdLine.contains("XmlConfiguration"))
|
||||
{
|
||||
fail("Unable to get Jetty command line");
|
||||
Assertions.fail( "Unable to get Jetty command line");
|
||||
}
|
||||
|
||||
// Need to breakdown commandline into parts, as spaces in command line will cause failures.
|
||||
|
@ -595,7 +592,7 @@ public class JettyDistro
|
|||
catch (InterruptedException e)
|
||||
{
|
||||
pid.destroy();
|
||||
fail("Unable to get required information within time limit");
|
||||
Assertions.fail( "Unable to get required information within time limit");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -804,7 +801,7 @@ public class JettyDistro
|
|||
}
|
||||
}
|
||||
|
||||
fail("Unable to find java bin");
|
||||
Assertions.fail( "Unable to find java bin");
|
||||
return "java";
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ import org.junit.jupiter.api.Disabled;
|
|||
public class TestableJettyServer
|
||||
{
|
||||
private List<URL> _xmlConfigurations;
|
||||
private final Map<String,String> _properties = new HashMap<String, String>();
|
||||
private final Map<String,String> _properties = new HashMap<>();
|
||||
private Server _server;
|
||||
private int _serverPort;
|
||||
private String _scheme = HttpScheme.HTTP.asString();
|
||||
|
@ -60,7 +60,7 @@ public class TestableJettyServer
|
|||
|
||||
public TestableJettyServer() throws IOException
|
||||
{
|
||||
_xmlConfigurations = new ArrayList<URL>();
|
||||
_xmlConfigurations = new ArrayList<>();
|
||||
Properties properties = new Properties();
|
||||
|
||||
/* Establish Popular Directories */
|
||||
|
|
|
@ -39,5 +39,6 @@
|
|||
<module>test-jaas-webapp</module>
|
||||
<module>test-jndi-webapp</module>
|
||||
<module>test-http2-webapp</module>
|
||||
<module>test-simple-webapp</module>
|
||||
</modules>
|
||||
</project>
|
||||
</project>
|
|
@ -0,0 +1,16 @@
|
|||
<?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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.tests</groupId>
|
||||
<artifactId>test-webapps-parent</artifactId>
|
||||
<version>9.4.15-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>test-simple-webapp</artifactId>
|
||||
|
||||
<packaging>war</packaging>
|
||||
|
||||
<name>Test :: Jetty Test Simple Webapp</name>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,10 @@
|
|||
<?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"
|
||||
metadata-complete="false"
|
||||
version="3.1">
|
||||
|
||||
<display-name>Very Simple Web Application</display-name>
|
||||
</web-app>
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
<h2>Hello World!</h2>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue