Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-10.0.x-8216-openid-logout
This commit is contained in:
commit
bf6a394fef
|
@ -0,0 +1,87 @@
|
|||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ 'jetty-10.[1-9]?[0-9].x', 'jetty-11.[1-9]?[0-9].x', 'jetty-12.[1-9]?[0-9].x' ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ 'jetty-10.[1-9]?[0-9].x', 'jetty-11.[1-9]?[0-9].x', 'jetty-12.[1-9]?[0-9].x' ]
|
||||
schedule:
|
||||
- cron: '22 1 * * 2'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'java', 'javascript' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Install and setup JDK 11
|
||||
- name: Setup JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
if: ${{
|
||||
startsWith(github.ref, 'refs/heads/jetty-10.') ||
|
||||
startsWith(github.ref, 'refs/heads/jetty-11.') ||
|
||||
startsWith(github.base_ref, 'jetty-10.') ||
|
||||
startsWith(github.base_ref, 'jetty-11.')
|
||||
}}
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
cache: maven
|
||||
|
||||
# Install and setup JDK 17
|
||||
- name: Setup JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
if: ${{
|
||||
startsWith(github.ref, 'refs/heads/jetty-12.') ||
|
||||
startsWith(github.base_ref, 'jetty-12.')
|
||||
}}
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
cache: maven
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
|
@ -3,7 +3,10 @@
|
|||
pipeline {
|
||||
agent any
|
||||
// save some io during the build
|
||||
options { durabilityHint('PERFORMANCE_OPTIMIZED') }
|
||||
options {
|
||||
skipDefaultCheckout()
|
||||
durabilityHint('PERFORMANCE_OPTIMIZED')
|
||||
}
|
||||
stages {
|
||||
stage("Parallel Stage") {
|
||||
parallel {
|
||||
|
@ -12,6 +15,7 @@ pipeline {
|
|||
steps {
|
||||
container('jetty-build') {
|
||||
timeout( time: 180, unit: 'MINUTES' ) {
|
||||
checkout scm
|
||||
mavenBuild( "jdk17", "clean install -Perrorprone", "maven3")
|
||||
// Collect up the jacoco execution results (only on main build)
|
||||
jacoco inclusionPattern: '**/org/eclipse/jetty/**/*.class',
|
||||
|
@ -42,6 +46,7 @@ pipeline {
|
|||
steps {
|
||||
container( 'jetty-build' ) {
|
||||
timeout( time: 180, unit: 'MINUTES' ) {
|
||||
checkout scm
|
||||
mavenBuild( "jdk11", "clean install -Dspotbugs.skip=true -Djacoco.skip=true", "maven3")
|
||||
recordIssues id: "jdk11", name: "Static Analysis jdk11", aggregatingResults: true, enabledForFailure: true, tools: [mavenConsole(), java(), checkStyle()]
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<name>Jetty :: GCloud</name>
|
||||
|
||||
<properties>
|
||||
<gcloud.version>2.7.0</gcloud.version>
|
||||
<gcloud.version>2.9.1</gcloud.version>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
|
|
|
@ -53,12 +53,15 @@ public class ServerHTTP2StreamEndPoint extends HTTP2StreamEndPoint implements HT
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("idle timeout on {}: {}", this, failure);
|
||||
offerFailure(failure);
|
||||
boolean result = true;
|
||||
Connection connection = getConnection();
|
||||
if (connection != null)
|
||||
result = connection.onIdleExpired();
|
||||
consumer.accept(() -> close(failure));
|
||||
if (result)
|
||||
{
|
||||
offerFailure(failure);
|
||||
consumer.accept(() -> close(failure));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<argLine>-Dstop.port=@{test.stopPort} -Djetty.port=@{test.jettyPort}</argLine>
|
||||
|
@ -114,11 +115,6 @@
|
|||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.shared</groupId>
|
||||
<artifactId>maven-artifact-transfer</artifactId>
|
||||
<version>${maven-artifact-transfer.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-plugin-api</artifactId>
|
||||
|
@ -150,6 +146,16 @@
|
|||
<artifactId>maven-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
<artifactId>maven-resolver-api</artifactId>
|
||||
<version>${maven.resolver.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.plexus</groupId>
|
||||
<artifactId>plexus-utils</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.plugin-tools</groupId>
|
||||
<artifactId>maven-plugin-tools-api</artifactId>
|
||||
|
|
|
@ -45,8 +45,8 @@ import org.apache.maven.plugin.descriptor.PluginDescriptor;
|
|||
import org.apache.maven.plugins.annotations.Component;
|
||||
import org.apache.maven.plugins.annotations.Parameter;
|
||||
import org.apache.maven.project.MavenProject;
|
||||
import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver;
|
||||
import org.codehaus.plexus.util.StringUtils;
|
||||
import org.eclipse.aether.RepositorySystem;
|
||||
import org.eclipse.jetty.maven.plugin.utils.MavenProjectHelper;
|
||||
import org.eclipse.jetty.security.LoginService;
|
||||
import org.eclipse.jetty.server.RequestLog;
|
||||
|
@ -357,7 +357,7 @@ public abstract class AbstractWebAppMojo extends AbstractMojo
|
|||
*
|
||||
*/
|
||||
@Component
|
||||
private ArtifactResolver artifactResolver;
|
||||
private RepositorySystem repositorySystem;
|
||||
|
||||
/**
|
||||
* The current maven session
|
||||
|
@ -410,7 +410,7 @@ public abstract class AbstractWebAppMojo extends AbstractMojo
|
|||
}
|
||||
|
||||
getLog().info("Configuring Jetty for project: " + getProjectName());
|
||||
mavenProjectHelper = new MavenProjectHelper(project, artifactResolver, remoteRepositories, session);
|
||||
mavenProjectHelper = new MavenProjectHelper(project, repositorySystem, remoteRepositories, session);
|
||||
mergedSystemProperties = mergeSystemProperties();
|
||||
configureSystemProperties();
|
||||
augmentPluginClasspath();
|
||||
|
|
|
@ -25,15 +25,16 @@ import java.util.Set;
|
|||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.maven.RepositoryUtils;
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.artifact.repository.ArtifactRepository;
|
||||
import org.apache.maven.execution.MavenSession;
|
||||
import org.apache.maven.project.DefaultProjectBuildingRequest;
|
||||
import org.apache.maven.project.MavenProject;
|
||||
import org.apache.maven.project.ProjectBuildingRequest;
|
||||
import org.apache.maven.shared.transfer.artifact.DefaultArtifactCoordinate;
|
||||
import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver;
|
||||
import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException;
|
||||
import org.eclipse.aether.RepositorySystem;
|
||||
import org.eclipse.aether.artifact.DefaultArtifact;
|
||||
import org.eclipse.aether.resolution.ArtifactRequest;
|
||||
import org.eclipse.aether.resolution.ArtifactResolutionException;
|
||||
import org.eclipse.aether.resolution.ArtifactResult;
|
||||
import org.eclipse.jetty.maven.plugin.OverlayManager;
|
||||
import org.eclipse.jetty.maven.plugin.WarPluginInfo;
|
||||
|
||||
|
@ -46,7 +47,7 @@ import org.eclipse.jetty.maven.plugin.WarPluginInfo;
|
|||
public class MavenProjectHelper
|
||||
{
|
||||
private MavenProject project;
|
||||
private ArtifactResolver artifactResolver;
|
||||
private RepositorySystem repositorySystem;
|
||||
private List<ArtifactRepository> remoteRepositories;
|
||||
private MavenSession session;
|
||||
private final Map<String, MavenProject> artifactToReactorProjectMap;
|
||||
|
@ -62,14 +63,14 @@ public class MavenProjectHelper
|
|||
|
||||
/**
|
||||
* @param project the project being built
|
||||
* @param artifactResolver a resolve for artifacts
|
||||
* @param repositorySystem a resolve for artifacts
|
||||
* @param remoteRepositories repositories from which to resolve artifacts
|
||||
* @param session the current maven build session
|
||||
*/
|
||||
public MavenProjectHelper(MavenProject project, ArtifactResolver artifactResolver, List<ArtifactRepository> remoteRepositories, MavenSession session)
|
||||
public MavenProjectHelper(MavenProject project, RepositorySystem repositorySystem, List<ArtifactRepository> remoteRepositories, MavenSession session)
|
||||
{
|
||||
this.project = project;
|
||||
this.artifactResolver = artifactResolver;
|
||||
this.repositorySystem = repositorySystem;
|
||||
this.remoteRepositories = remoteRepositories;
|
||||
this.session = session;
|
||||
//work out which dependent projects are in the reactor
|
||||
|
@ -145,26 +146,18 @@ public class MavenProjectHelper
|
|||
* @param version the version of the artifact to resolve
|
||||
* @param type the type of the artifact to resolve
|
||||
* @return a File representing the location of the artifact or null if not resolved
|
||||
* @throws ArtifactResolverException
|
||||
* @throws ArtifactResolutionException
|
||||
*/
|
||||
public File resolveArtifact(String groupId, String artifactId, String version, String type)
|
||||
throws ArtifactResolverException
|
||||
throws ArtifactResolutionException
|
||||
{
|
||||
DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate();
|
||||
coordinate.setGroupId(groupId);
|
||||
coordinate.setArtifactId(artifactId);
|
||||
coordinate.setVersion(version);
|
||||
coordinate.setExtension(type);
|
||||
ArtifactRequest request = new ArtifactRequest();
|
||||
request.setRepositories(RepositoryUtils.toRepos(remoteRepositories));
|
||||
request.setArtifact(new DefaultArtifact(groupId, artifactId, "", type, version));
|
||||
ArtifactResult result = repositorySystem.resolveArtifact(session.getRepositorySession(), request);
|
||||
|
||||
ProjectBuildingRequest buildingRequest =
|
||||
new DefaultProjectBuildingRequest(session.getProjectBuildingRequest());
|
||||
|
||||
buildingRequest.setRemoteRepositories(remoteRepositories);
|
||||
|
||||
Artifact a = artifactResolver.resolveArtifact(buildingRequest, coordinate).getArtifact();
|
||||
|
||||
if (a != null)
|
||||
return a.getFile();
|
||||
if (result.isResolved())
|
||||
return result.getArtifact().getFile();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<osgi-version>3.17.200</osgi-version>
|
||||
<osgi-version>3.18.0</osgi-version>
|
||||
<osgi-services-version>3.10.200</osgi-services-version>
|
||||
<osgi-util-version>3.6.100</osgi-util-version>
|
||||
<equinox-http-servlet-version>1.0.0-v20070606</equinox-http-servlet-version>
|
||||
|
|
|
@ -153,7 +153,6 @@
|
|||
<dependency>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-boot</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
|
@ -169,7 +168,6 @@
|
|||
<dependency>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-boot-jsp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
|
@ -185,7 +183,6 @@
|
|||
<dependency>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-httpservice</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
@ -346,7 +343,6 @@
|
|||
<dependency>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-alpn</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
@ -47,5 +47,25 @@
|
|||
<version>${project.version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-alpn</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-boot</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-boot-jsp</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-boot-warurl</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-httpservice</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
|
||||
|
||||
<!-- =============================================================== -->
|
||||
<!-- Configure extended support for webapps -->
|
||||
<!-- =============================================================== -->
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Add plus Configuring classes to all webapps for this Server -->
|
||||
<!-- =========================================================== -->
|
||||
<Call class="org.eclipse.jetty.webapp.Configurations" name="setServerDefault">
|
||||
<Arg><Ref refid="Server" /></Arg>
|
||||
<Call name="add">
|
||||
<Arg>
|
||||
<Array type="String">
|
||||
<Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item>
|
||||
<Item>org.eclipse.jetty.plus.webapp.PlusConfiguration</Item>
|
||||
</Array>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Call>
|
||||
|
||||
<Call name="addBean">
|
||||
<Arg><New class="org.eclipse.jetty.plus.jndi.NamingDump"/></Arg>
|
||||
</Call>
|
||||
|
||||
</Configure>
|
||||
|
|
@ -29,6 +29,7 @@ import java.net.Socket;
|
|||
import java.net.SocketTimeoutException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -187,7 +188,7 @@ public class Main
|
|||
|
||||
public void invokeMain(ClassLoader classloader, StartArgs args) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, IOException
|
||||
{
|
||||
if (args.getEnabledModules().isEmpty())
|
||||
if (args.getSelectedModules().isEmpty())
|
||||
{
|
||||
if (Files.exists(getBaseHome().getBasePath("start.jar")))
|
||||
StartLog.error("Do not start with ${jetty.base} == ${jetty.home}!");
|
||||
|
@ -325,12 +326,22 @@ public class Main
|
|||
modules.registerAll();
|
||||
|
||||
// 4) Active Module Resolution
|
||||
for (String enabledModule : modules.getSortedNames(args.getEnabledModules()))
|
||||
List<String> selectedModules = args.getSelectedModules();
|
||||
List<String> sortedSelectedModules = modules.getSortedNames(selectedModules);
|
||||
List<String> unknownModules = new ArrayList<>(selectedModules);
|
||||
unknownModules.removeAll(sortedSelectedModules);
|
||||
if (unknownModules.size() >= 1)
|
||||
{
|
||||
for (String source : args.getSources(enabledModule))
|
||||
throw new UsageException(UsageException.ERR_UNKNOWN, "Unknown module%s=[%s] List available with --list-modules",
|
||||
unknownModules.size() > 1 ? 's' : "",
|
||||
String.join(", ", unknownModules));
|
||||
}
|
||||
for (String selectedModule : sortedSelectedModules)
|
||||
{
|
||||
for (String source : args.getSources(selectedModule))
|
||||
{
|
||||
String shortForm = baseHome.toShortForm(source);
|
||||
modules.enable(enabledModule, shortForm);
|
||||
modules.enable(selectedModule, shortForm);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -470,8 +481,7 @@ public class Main
|
|||
CommandLineBuilder cmd = args.getMainArgs(StartArgs.ALL_PARTS);
|
||||
cmd.debug();
|
||||
|
||||
List<String> execModules = args.getEnabledModules().stream()
|
||||
.map(name -> args.getAllModules().get(name))
|
||||
List<String> execModules = args.getAllModules().getEnabled().stream()
|
||||
// Keep only the forking modules.
|
||||
.filter(module -> !module.getJvmArgs().isEmpty())
|
||||
.map(Module::getName)
|
||||
|
|
|
@ -395,7 +395,6 @@ public class Modules implements Iterable<Module>
|
|||
order.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
return order;
|
||||
}
|
||||
|
||||
|
|
|
@ -646,7 +646,29 @@ public class StartArgs
|
|||
return classpath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #getSelectedModules()} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public List<String> getEnabledModules()
|
||||
{
|
||||
return getSelectedModules();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The list of selected Modules to enable based on configuration
|
||||
* obtained from {@code start.d/*.ini}, {@code start.ini}, and command line.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* For full list of enabled modules, use {@link Modules#getEnabled()}
|
||||
* </p>
|
||||
*
|
||||
* @return the list of selected modules (by name) that the configuration has.
|
||||
* @see Modules#getEnabled()
|
||||
*/
|
||||
public List<String> getSelectedModules()
|
||||
{
|
||||
return this.modules;
|
||||
}
|
||||
|
@ -1315,11 +1337,11 @@ public class StartArgs
|
|||
return;
|
||||
}
|
||||
|
||||
// Enable a module
|
||||
// Select a module to eventually be enabled
|
||||
if (arg.startsWith("--module="))
|
||||
{
|
||||
List<String> moduleNames = Props.getValues(arg);
|
||||
enableModules(source, moduleNames);
|
||||
selectModules(source, moduleNames);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1464,7 +1486,7 @@ public class StartArgs
|
|||
setProperty(key, value, source);
|
||||
}
|
||||
|
||||
private void enableModules(String source, List<String> moduleNames)
|
||||
private void selectModules(String source, List<String> moduleNames)
|
||||
{
|
||||
for (String moduleName : moduleNames)
|
||||
{
|
||||
|
|
|
@ -71,7 +71,7 @@ public class MavenMetadataTest
|
|||
@Test
|
||||
public void testIsExpiredTimestampNow()
|
||||
{
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime now = LocalDateTime.now(ZoneId.of("UTC"));
|
||||
String timestamp = getTimestampFormatter().format(now);
|
||||
assertFalse(MavenMetadata.isExpiredTimestamp(timestamp), "Timestamp should NOT be stale: " + timestamp);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.start.Props;
|
||||
import org.eclipse.jetty.start.UsageException;
|
||||
import org.eclipse.jetty.toolchain.test.FS;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.toolchain.test.PathAssert;
|
||||
|
@ -34,10 +35,12 @@ import org.junit.jupiter.api.Test;
|
|||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
public class BasicTest extends AbstractUseCase
|
||||
{
|
||||
|
@ -110,6 +113,59 @@ public class BasicTest extends AbstractUseCase
|
|||
assertThat("System.getProperty(jetty.base)", System.getProperty("jetty.base"), is(not(startsWith("file:"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddModuleDoesNotExist() throws Exception
|
||||
{
|
||||
setupDistHome();
|
||||
|
||||
Files.write(baseDir.resolve("start.ini"),
|
||||
List.of(
|
||||
"--module=main",
|
||||
"--module=does-not-exist"
|
||||
),
|
||||
StandardCharsets.UTF_8);
|
||||
|
||||
// === Execute Main
|
||||
List<String> runArgs = new ArrayList<>();
|
||||
runArgs.add("--create-files");
|
||||
UsageException usage = assertThrows(UsageException.class, () ->
|
||||
{
|
||||
ExecResults results = exec(runArgs, true);
|
||||
if (results.exception != null)
|
||||
{
|
||||
throw results.exception;
|
||||
}
|
||||
});
|
||||
assertThat(usage.getMessage(), containsString("Unknown module=[does-not-exist]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddModuleDoesNotExistMultiple() throws Exception
|
||||
{
|
||||
setupDistHome();
|
||||
|
||||
Files.write(baseDir.resolve("start.ini"),
|
||||
List.of(
|
||||
"--module=main",
|
||||
"--module=does-not-exist",
|
||||
"--module=also-not-present"
|
||||
),
|
||||
StandardCharsets.UTF_8);
|
||||
|
||||
// === Execute Main
|
||||
List<String> runArgs = new ArrayList<>();
|
||||
runArgs.add("--create-files");
|
||||
UsageException usage = assertThrows(UsageException.class, () ->
|
||||
{
|
||||
ExecResults results = exec(runArgs, true);
|
||||
if (results.exception != null)
|
||||
{
|
||||
throw results.exception;
|
||||
}
|
||||
});
|
||||
assertThat(usage.getMessage(), containsString("Unknown modules=[does-not-exist, also-not-present]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProvidersUsingDefault() throws Exception
|
||||
{
|
||||
|
|
|
@ -243,9 +243,8 @@ public class QueuedThreadPool extends ContainerLifeCycle implements ThreadFactor
|
|||
{
|
||||
// Fill the job queue with noop jobs to wakeup idle threads.
|
||||
for (int i = 0; i < threads; ++i)
|
||||
{
|
||||
jobs.offer(NOOP);
|
||||
}
|
||||
if (!jobs.offer(NOOP))
|
||||
break;
|
||||
|
||||
// try to let jobs complete naturally for half our stop time
|
||||
joinThreads(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2);
|
||||
|
@ -255,6 +254,8 @@ public class QueuedThreadPool extends ContainerLifeCycle implements ThreadFactor
|
|||
// interrupt remaining threads
|
||||
for (Thread thread : _threads)
|
||||
{
|
||||
if (thread == Thread.currentThread())
|
||||
continue;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Interrupting {}", thread);
|
||||
thread.interrupt();
|
||||
|
@ -264,24 +265,21 @@ public class QueuedThreadPool extends ContainerLifeCycle implements ThreadFactor
|
|||
joinThreads(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2);
|
||||
|
||||
Thread.yield();
|
||||
if (LOG.isDebugEnabled())
|
||||
|
||||
for (Thread unstopped : _threads)
|
||||
{
|
||||
for (Thread unstopped : _threads)
|
||||
if (unstopped == Thread.currentThread())
|
||||
continue;
|
||||
String stack = "";
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
StringBuilder dmp = new StringBuilder();
|
||||
for (StackTraceElement element : unstopped.getStackTrace())
|
||||
{
|
||||
dmp.append(System.lineSeparator()).append("\tat ").append(element);
|
||||
}
|
||||
LOG.warn("Couldn't stop {}{}", unstopped, dmp.toString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Thread unstopped : _threads)
|
||||
{
|
||||
LOG.warn("{} Couldn't stop {}", this, unstopped);
|
||||
stack = dmp.toString();
|
||||
}
|
||||
|
||||
LOG.warn("Couldn't stop {}{}", unstopped, stack);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -315,13 +313,32 @@ public class QueuedThreadPool extends ContainerLifeCycle implements ThreadFactor
|
|||
|
||||
private void joinThreads(long stopByNanos) throws InterruptedException
|
||||
{
|
||||
for (Thread thread : _threads)
|
||||
loop : while (true)
|
||||
{
|
||||
long canWait = TimeUnit.NANOSECONDS.toMillis(stopByNanos - System.nanoTime());
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Waiting for {} for {}", thread, canWait);
|
||||
if (canWait > 0)
|
||||
thread.join(canWait);
|
||||
for (Thread thread : _threads)
|
||||
{
|
||||
// Don't join ourselves
|
||||
if (thread == Thread.currentThread())
|
||||
continue;
|
||||
|
||||
long canWait = TimeUnit.NANOSECONDS.toMillis(stopByNanos - System.nanoTime());
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Waiting for {} for {}", thread, canWait);
|
||||
if (canWait <= 0)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
thread.join(canWait);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
// Don't stop waiting for a join if interrupted
|
||||
continue loop;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -101,6 +101,43 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
|
|||
}
|
||||
}
|
||||
|
||||
private static class StoppingTask implements Runnable
|
||||
{
|
||||
private final CountDownLatch _running;
|
||||
private final CountDownLatch _blocked;
|
||||
private final QueuedThreadPool _tp;
|
||||
Thread _thread;
|
||||
CountDownLatch _completed = new CountDownLatch(1);
|
||||
|
||||
public StoppingTask(CountDownLatch running, CountDownLatch blocked, QueuedThreadPool tp)
|
||||
{
|
||||
_running = running;
|
||||
_blocked = blocked;
|
||||
_tp = tp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
_thread = Thread.currentThread();
|
||||
_running.countDown();
|
||||
_blocked.await();
|
||||
_tp.doStop();
|
||||
_completed.countDown();
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
x.printStackTrace();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class RunningJob implements Runnable
|
||||
{
|
||||
final CountDownLatch _run = new CountDownLatch(1);
|
||||
|
@ -947,6 +984,49 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInterruptedStop() throws Exception
|
||||
{
|
||||
QueuedThreadPool tp = new QueuedThreadPool();
|
||||
tp.setStopTimeout(1000);
|
||||
tp.start();
|
||||
|
||||
CountDownLatch running = new CountDownLatch(3);
|
||||
CountDownLatch blocked = new CountDownLatch(1);
|
||||
CountDownLatch forever = new CountDownLatch(2);
|
||||
CountDownLatch interrupted = new CountDownLatch(1);
|
||||
|
||||
Runnable runForever = () ->
|
||||
{
|
||||
try
|
||||
{
|
||||
running.countDown();
|
||||
forever.await();
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
interrupted.countDown();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
|
||||
StoppingTask stopping = new StoppingTask(running, blocked, tp);
|
||||
|
||||
tp.execute(runForever);
|
||||
tp.execute(stopping);
|
||||
tp.execute(runForever);
|
||||
|
||||
assertTrue(running.await(5, TimeUnit.SECONDS));
|
||||
blocked.countDown();
|
||||
Thread.sleep(100); // wait until in doStop, then....
|
||||
stopping._thread.interrupt(); // spurious interrupt
|
||||
assertTrue(interrupted.await(5, TimeUnit.SECONDS));
|
||||
assertTrue(stopping._completed.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
private int count(String s, String p)
|
||||
{
|
||||
int c = 0;
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.FileStore;
|
||||
|
@ -188,6 +189,7 @@ public class HugeResourceTest
|
|||
context.setBaseResource(new PathResource(staticBase));
|
||||
|
||||
context.addServlet(PostServlet.class, "/post");
|
||||
context.addServlet(ChunkedServlet.class, "/chunked/*");
|
||||
|
||||
String location = multipartTempDir.toString();
|
||||
long maxFileSize = Long.MAX_VALUE;
|
||||
|
@ -223,7 +225,7 @@ public class HugeResourceTest
|
|||
|
||||
@ParameterizedTest
|
||||
@MethodSource("staticFiles")
|
||||
public void testDownload(String filename, long expectedSize) throws Exception
|
||||
public void testDownloadStatic(String filename, long expectedSize) throws Exception
|
||||
{
|
||||
URI destUri = server.getURI().resolve("/" + filename);
|
||||
InputStreamResponseListener responseListener = new InputStreamResponseListener();
|
||||
|
@ -250,7 +252,33 @@ public class HugeResourceTest
|
|||
|
||||
@ParameterizedTest
|
||||
@MethodSource("staticFiles")
|
||||
public void testHead(String filename, long expectedSize) throws Exception
|
||||
public void testDownloadChunked(String filename, long expectedSize) throws Exception
|
||||
{
|
||||
URI destUri = server.getURI().resolve("/chunked/" + filename);
|
||||
InputStreamResponseListener responseListener = new InputStreamResponseListener();
|
||||
|
||||
Request request = client.newRequest(destUri)
|
||||
.method(HttpMethod.GET);
|
||||
request.send(responseListener);
|
||||
Response response = responseListener.get(5, TimeUnit.SECONDS);
|
||||
|
||||
assertThat("HTTP Response Code", response.getStatus(), is(200));
|
||||
// dumpResponse(response);
|
||||
|
||||
String transferEncoding = response.getHeaders().get(HttpHeader.TRANSFER_ENCODING);
|
||||
assertThat("Http Response Header: \"Transfer-Encoding\"", transferEncoding, is("chunked"));
|
||||
|
||||
try (ByteCountingOutputStream out = new ByteCountingOutputStream();
|
||||
InputStream in = responseListener.getInputStream())
|
||||
{
|
||||
IO.copy(in, out);
|
||||
assertThat("Downloaded Files Size: " + filename, out.getCount(), is(expectedSize));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("staticFiles")
|
||||
public void testHeadStatic(String filename, long expectedSize) throws Exception
|
||||
{
|
||||
URI destUri = server.getURI().resolve("/" + filename);
|
||||
InputStreamResponseListener responseListener = new InputStreamResponseListener();
|
||||
|
@ -273,6 +301,30 @@ public class HugeResourceTest
|
|||
assertThat("Http Response Header: \"Content-Length: " + contentLength + "\"", contentLengthLong, is(expectedSize));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("staticFiles")
|
||||
public void testHeadChunked(String filename, long expectedSize) throws Exception
|
||||
{
|
||||
URI destUri = server.getURI().resolve("/chunked/" + filename);
|
||||
InputStreamResponseListener responseListener = new InputStreamResponseListener();
|
||||
|
||||
Request request = client.newRequest(destUri)
|
||||
.method(HttpMethod.HEAD);
|
||||
request.send(responseListener);
|
||||
Response response = responseListener.get(5, TimeUnit.SECONDS);
|
||||
|
||||
try (InputStream in = responseListener.getInputStream())
|
||||
{
|
||||
assertThat(in.read(), is(-1));
|
||||
}
|
||||
|
||||
assertThat("HTTP Response Code", response.getStatus(), is(200));
|
||||
// dumpResponse(response);
|
||||
|
||||
String transferEncoding = response.getHeaders().get(HttpHeader.TRANSFER_ENCODING);
|
||||
assertThat("Http Response Header: \"Transfer-Encoding\"", transferEncoding, is("chunked"));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("staticFiles")
|
||||
public void testUpload(String filename, long expectedSize) throws Exception
|
||||
|
@ -359,6 +411,22 @@ public class HugeResourceTest
|
|||
}
|
||||
}
|
||||
|
||||
public static class ChunkedServlet extends HttpServlet
|
||||
{
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
|
||||
{
|
||||
URL resource = req.getServletContext().getResource(req.getPathInfo());
|
||||
OutputStream output = resp.getOutputStream();
|
||||
try (InputStream input = resource.openStream())
|
||||
{
|
||||
resp.setContentType("application/octet-stream");
|
||||
resp.flushBuffer();
|
||||
IO.copy(input, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class MultipartServlet extends HttpServlet
|
||||
{
|
||||
@Override
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.javax.common;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.security.Principal;
|
||||
import java.time.Duration;
|
||||
|
@ -24,7 +23,6 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.websocket.CloseReason;
|
||||
import javax.websocket.EndpointConfig;
|
||||
|
@ -35,7 +33,7 @@ import javax.websocket.RemoteEndpoint.Basic;
|
|||
import javax.websocket.Session;
|
||||
import javax.websocket.WebSocketContainer;
|
||||
|
||||
import org.eclipse.jetty.util.FutureCallback;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.websocket.core.CoreSession;
|
||||
import org.eclipse.jetty.websocket.core.ExtensionConfig;
|
||||
import org.eclipse.jetty.websocket.core.internal.util.ReflectUtils;
|
||||
|
@ -190,13 +188,11 @@ public class JavaxWebSocketSession implements javax.websocket.Session
|
|||
{
|
||||
try
|
||||
{
|
||||
FutureCallback b = new FutureCallback();
|
||||
coreSession.close(closeReason.getCloseCode().getCode(), closeReason.getReasonPhrase(), b);
|
||||
b.block(getBlockingTimeout(), TimeUnit.MILLISECONDS);
|
||||
coreSession.close(closeReason.getCloseCode().getCode(), closeReason.getReasonPhrase(), Callback.NOOP);
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOG.trace("IGNORED", e);
|
||||
LOG.trace("IGNORED", t);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.javax.tests;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
@ -72,7 +73,8 @@ public class JavaxOnCloseTest
|
|||
public void onClose(CloseReason reason)
|
||||
{
|
||||
super.onClose(reason);
|
||||
onClose.accept(session);
|
||||
if (onClose != null)
|
||||
onClose.accept(session);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,4 +228,36 @@ public class JavaxOnCloseTest
|
|||
assertThat(clientEndpoint.error, instanceOf(RuntimeException.class));
|
||||
assertThat(clientEndpoint.error.getMessage(), containsString("trigger onError from client onClose"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseFromCallback() throws Exception
|
||||
{
|
||||
EventSocket clientEndpoint = new EventSocket();
|
||||
URI uri = new URI("ws://localhost:" + connector.getLocalPort() + "/");
|
||||
client.connectToServer(clientEndpoint, uri);
|
||||
|
||||
OnCloseEndpoint serverEndpoint = Objects.requireNonNull(serverEndpoints.poll(5, TimeUnit.SECONDS));
|
||||
assertTrue(serverEndpoint.openLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
CountDownLatch closeSent = new CountDownLatch(1);
|
||||
clientEndpoint.session.getAsyncRemote().sendText("GOODBYE", sendResult ->
|
||||
{
|
||||
try
|
||||
{
|
||||
clientEndpoint.session.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
closeSent.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
assertTrue(closeSent.await(5, TimeUnit.SECONDS));
|
||||
assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
|
||||
assertThat(clientEndpoint.closeReason.getCloseCode(), is(CloseCodes.NORMAL_CLOSURE));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,6 +63,32 @@ public interface Session extends WebSocketPolicy, Closeable
|
|||
*/
|
||||
void close(int statusCode, String reason);
|
||||
|
||||
/**
|
||||
* Send a websocket Close frame, with status code.
|
||||
* <p>
|
||||
* This will enqueue a graceful close to the remote endpoint.
|
||||
*
|
||||
* @param statusCode the status code
|
||||
* @param reason the (optional) reason. (can be null for no reason)
|
||||
* @param callback the callback to track close frame sent (or failed)
|
||||
* @see StatusCode
|
||||
* @see #close()
|
||||
* @see #close(CloseStatus)
|
||||
* @see #disconnect()
|
||||
*/
|
||||
default void close(int statusCode, String reason, WriteCallback callback)
|
||||
{
|
||||
try
|
||||
{
|
||||
close(statusCode, reason);
|
||||
callback.writeSuccess();
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
callback.writeFailed(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue a harsh disconnect of the underlying connection.
|
||||
* <p>
|
||||
|
|
|
@ -20,7 +20,9 @@ package org.eclipse.jetty.websocket.api;
|
|||
*/
|
||||
public interface WriteCallback
|
||||
{
|
||||
WriteCallback NOOP = new Adaptor();
|
||||
WriteCallback NOOP = new WriteCallback()
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* <p>
|
||||
|
@ -44,6 +46,7 @@ public interface WriteCallback
|
|||
{
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
class Adaptor implements WriteCallback
|
||||
{
|
||||
@Override
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.eclipse.jetty.util.BufferUtil;
|
|||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.FutureCallback;
|
||||
import org.eclipse.jetty.websocket.api.BatchMode;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.api.WriteCallback;
|
||||
import org.eclipse.jetty.websocket.core.CoreSession;
|
||||
import org.eclipse.jetty.websocket.core.Frame;
|
||||
|
@ -48,37 +47,6 @@ public class JettyWebSocketRemoteEndpoint implements org.eclipse.jetty.websocket
|
|||
this.batchMode = batchMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate close of the Remote with no status code (no payload)
|
||||
*
|
||||
* @since 10.0
|
||||
*/
|
||||
public void close()
|
||||
{
|
||||
close(StatusCode.NO_CODE, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate close of the Remote with specified status code and optional reason phrase
|
||||
*
|
||||
* @param statusCode the status code (must be valid and can be sent)
|
||||
* @param reason optional reason code
|
||||
* @since 10.0
|
||||
*/
|
||||
public void close(int statusCode, String reason)
|
||||
{
|
||||
try
|
||||
{
|
||||
FutureCallback b = new FutureCallback();
|
||||
coreSession.close(statusCode, reason, b);
|
||||
b.block(getBlockingTimeout(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.trace("IGNORED", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendString(String text) throws IOException
|
||||
{
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.net.SocketAddress;
|
|||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.websocket.api.CloseStatus;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
|
@ -27,6 +28,7 @@ import org.eclipse.jetty.websocket.api.UpgradeRequest;
|
|||
import org.eclipse.jetty.websocket.api.UpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketContainer;
|
||||
import org.eclipse.jetty.websocket.api.WriteCallback;
|
||||
import org.eclipse.jetty.websocket.core.CoreSession;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -53,19 +55,25 @@ public class WebSocketSession implements Session, SuspendToken, Dumpable
|
|||
@Override
|
||||
public void close()
|
||||
{
|
||||
remoteEndpoint.close(StatusCode.NORMAL, null);
|
||||
coreSession.close(StatusCode.NORMAL, null, Callback.NOOP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(CloseStatus closeStatus)
|
||||
{
|
||||
remoteEndpoint.close(closeStatus.getCode(), closeStatus.getPhrase());
|
||||
coreSession.close(closeStatus.getCode(), closeStatus.getPhrase(), Callback.NOOP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(int statusCode, String reason)
|
||||
{
|
||||
remoteEndpoint.close(statusCode, reason);
|
||||
coreSession.close(statusCode, reason, Callback.NOOP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(int statusCode, String reason, WriteCallback callback)
|
||||
{
|
||||
coreSession.close(statusCode, reason, Callback.from(callback::writeSuccess, callback::writeFailed));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.io.InterruptedIOException;
|
|||
import java.net.ConnectException;
|
||||
import java.net.URI;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -57,6 +58,7 @@ import org.eclipse.jetty.websocket.api.StatusCode;
|
|||
import org.eclipse.jetty.websocket.api.exceptions.UpgradeException;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.core.server.internal.UpgradeHttpServletRequest;
|
||||
import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer;
|
||||
import org.eclipse.jetty.websocket.server.JettyWebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory;
|
||||
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
|
||||
|
@ -68,6 +70,7 @@ import org.junit.jupiter.api.condition.OS;
|
|||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsStringIgnoringCase;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
@ -80,6 +83,7 @@ public class WebSocketOverHTTP2Test
|
|||
private ServerConnector connector;
|
||||
private ServerConnector tlsConnector;
|
||||
private WebSocketClient wsClient;
|
||||
private ServletContextHandler context;
|
||||
|
||||
private void startServer() throws Exception
|
||||
{
|
||||
|
@ -112,7 +116,7 @@ public class WebSocketOverHTTP2Test
|
|||
tlsConnector = new ServerConnector(server, 1, 1, ssl, alpn, h1s, h2s);
|
||||
server.addConnector(tlsConnector);
|
||||
|
||||
ServletContextHandler context = new ServletContextHandler(server, "/");
|
||||
context = new ServletContextHandler(server, "/");
|
||||
context.addServlet(new ServletHolder(servlet), "/ws/*");
|
||||
JettyWebSocketServletContainerInitializer.configure(context, null);
|
||||
|
||||
|
@ -337,6 +341,41 @@ public class WebSocketOverHTTP2Test
|
|||
assertThat(cause, instanceOf(ClosedChannelException.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerTimeout() throws Exception
|
||||
{
|
||||
startServer();
|
||||
JettyWebSocketServerContainer container = JettyWebSocketServerContainer.getContainer(context.getServletContext());
|
||||
startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector)));
|
||||
EchoSocket serverEndpoint = new EchoSocket();
|
||||
container.addMapping("/specialEcho", (req, resp) -> serverEndpoint);
|
||||
|
||||
// Set up idle timeouts.
|
||||
long timeout = 1000;
|
||||
container.setIdleTimeout(Duration.ofMillis(timeout));
|
||||
wsClient.setIdleTimeout(Duration.ZERO);
|
||||
|
||||
// Setup a websocket connection.
|
||||
EventSocket clientEndpoint = new EventSocket();
|
||||
URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/specialEcho");
|
||||
Session session = wsClient.connect(clientEndpoint, uri).get(5, TimeUnit.SECONDS);
|
||||
session.getRemote().sendString("hello world");
|
||||
String received = clientEndpoint.textMessages.poll(5, TimeUnit.SECONDS);
|
||||
assertThat(received, equalTo("hello world"));
|
||||
|
||||
// Wait for timeout on server.
|
||||
assertTrue(serverEndpoint.closeLatch.await(timeout * 2, TimeUnit.MILLISECONDS));
|
||||
assertThat(serverEndpoint.closeCode, equalTo(StatusCode.SHUTDOWN));
|
||||
assertThat(serverEndpoint.closeReason, containsStringIgnoringCase("timeout"));
|
||||
assertNotNull(serverEndpoint.error);
|
||||
|
||||
// Wait for timeout on client.
|
||||
assertTrue(clientEndpoint.closeLatch.await(timeout * 2, TimeUnit.MILLISECONDS));
|
||||
assertThat(clientEndpoint.closeCode, equalTo(StatusCode.SHUTDOWN));
|
||||
assertThat(clientEndpoint.closeReason, containsStringIgnoringCase("timeout"));
|
||||
assertNull(clientEndpoint.error);
|
||||
}
|
||||
|
||||
private static class TestJettyWebSocketServlet extends JettyWebSocketServlet
|
||||
{
|
||||
@Override
|
||||
|
|
34
pom.xml
34
pom.xml
|
@ -34,7 +34,7 @@
|
|||
<awaitility.version>4.2.0</awaitility.version>
|
||||
<bndlib.version>6.3.1</bndlib.version>
|
||||
<build-support.version>1.5</build-support.version>
|
||||
<checkstyle.version>10.3</checkstyle.version>
|
||||
<checkstyle.version>10.3.1</checkstyle.version>
|
||||
<commons-codec.version>1.15</commons-codec.version>
|
||||
<commons.compress.version>1.21</commons.compress.version>
|
||||
<commons.io.version>2.11.0</commons.io.version>
|
||||
|
@ -90,7 +90,7 @@
|
|||
<jetty-test-policy.version>1.2</jetty-test-policy.version>
|
||||
<jetty.test.version>5.9</jetty.test.version>
|
||||
<jmh.version>1.35</jmh.version>
|
||||
<jna.version>5.11.0</jna.version>
|
||||
<jna.version>5.12.1</jna.version>
|
||||
<jnr-constants.version>0.10.3</jnr-constants.version>
|
||||
<jnr-enxio.version>0.32.13</jnr-enxio.version>
|
||||
<jnr-ffi.version>2.2.12</jnr-ffi.version>
|
||||
|
@ -104,11 +104,10 @@
|
|||
<kerb-simplekdc.version>2.0.2</kerb-simplekdc.version>
|
||||
<log4j2.version>2.17.2</log4j2.version>
|
||||
<logback.version>1.3.0-alpha16</logback.version>
|
||||
<mariadb.version>3.0.5</mariadb.version>
|
||||
<mariadb.version>3.0.6</mariadb.version>
|
||||
<mariadb.docker.version>10.3.6</mariadb.docker.version>
|
||||
<maven-artifact-transfer.version>0.13.1</maven-artifact-transfer.version>
|
||||
<maven.resolver.version>1.8.1</maven.resolver.version>
|
||||
<maven.version>3.8.4</maven.version>
|
||||
<maven.version>3.8.6</maven.version>
|
||||
<mongodb.version>3.12.11</mongodb.version>
|
||||
<openpojo.version>0.9.1</openpojo.version>
|
||||
<org.osgi.annotation.version>8.1.0</org.osgi.annotation.version>
|
||||
|
@ -121,7 +120,7 @@
|
|||
<springboot.version>2.1.1.RELEASE</springboot.version>
|
||||
<taglibs-standard-impl.version>1.2.5</taglibs-standard-impl.version>
|
||||
<taglibs-standard-spec.version>1.2.5</taglibs-standard-spec.version>
|
||||
<testcontainers.version>1.17.2</testcontainers.version>
|
||||
<testcontainers.version>1.17.3</testcontainers.version>
|
||||
<weld.version>3.1.9.Final</weld.version>
|
||||
<wildfly.common.version>1.6.0.Final</wildfly.common.version>
|
||||
<wildfly.elytron.version>1.19.0.Final</wildfly.elytron.version>
|
||||
|
@ -148,7 +147,7 @@
|
|||
<maven.dependency.plugin.version>3.3.0</maven.dependency.plugin.version>
|
||||
<maven.deploy.plugin.version>3.0.0-M2</maven.deploy.plugin.version>
|
||||
<maven.eclipse.plugin.version>2.10</maven.eclipse.plugin.version>
|
||||
<maven.enforcer.plugin.version>3.0.0</maven.enforcer.plugin.version>
|
||||
<maven.enforcer.plugin.version>3.1.0</maven.enforcer.plugin.version>
|
||||
<maven.exec.plugin.version>3.0.0</maven.exec.plugin.version>
|
||||
<maven.gpg.plugin.version>3.0.1</maven.gpg.plugin.version>
|
||||
<maven.install.plugin.version>3.0.0-M1</maven.install.plugin.version>
|
||||
|
@ -1681,11 +1680,32 @@
|
|||
<artifactId>jetty-memcached-sessions</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-alpn</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-boot</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-boot-jsp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-boot-warurl</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-httpservice</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.quic</groupId>
|
||||
<artifactId>quic-client</artifactId>
|
||||
|
|
Loading…
Reference in New Issue