diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000000..4e77e200e14 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -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 diff --git a/Jenkinsfile b/Jenkinsfile index a07d6021278..f91e72730d6 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -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()] } diff --git a/jetty-gcloud/pom.xml b/jetty-gcloud/pom.xml index e8a1946bf7c..78554cae1ab 100644 --- a/jetty-gcloud/pom.xml +++ b/jetty-gcloud/pom.xml @@ -13,7 +13,7 @@ Jetty :: GCloud - 2.7.0 + 2.9.1 diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/ServerHTTP2StreamEndPoint.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/ServerHTTP2StreamEndPoint.java index 5a37f48366a..01ada136c91 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/ServerHTTP2StreamEndPoint.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/ServerHTTP2StreamEndPoint.java @@ -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; } diff --git a/jetty-maven-plugin/pom.xml b/jetty-maven-plugin/pom.xml index 530c586f60f..7c1fc57d140 100644 --- a/jetty-maven-plugin/pom.xml +++ b/jetty-maven-plugin/pom.xml @@ -49,6 +49,7 @@ + org.apache.maven.plugins maven-surefire-plugin -Dstop.port=@{test.stopPort} -Djetty.port=@{test.jettyPort} @@ -114,11 +115,6 @@ - - org.apache.maven.shared - maven-artifact-transfer - ${maven-artifact-transfer.version} - org.apache.maven maven-plugin-api @@ -150,6 +146,16 @@ maven-core provided + + org.apache.maven.resolver + maven-resolver-api + ${maven.resolver.version} + provided + + + org.codehaus.plexus + plexus-utils + org.apache.maven.plugin-tools maven-plugin-tools-api diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractWebAppMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractWebAppMojo.java index 834c010f91d..de1dddff76b 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractWebAppMojo.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractWebAppMojo.java @@ -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(); diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/utils/MavenProjectHelper.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/utils/MavenProjectHelper.java index b5b9d03da94..9d17ff2975f 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/utils/MavenProjectHelper.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/utils/MavenProjectHelper.java @@ -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 remoteRepositories; private MavenSession session; private final Map 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 remoteRepositories, MavenSession session) + public MavenProjectHelper(MavenProject project, RepositorySystem repositorySystem, List 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; } diff --git a/jetty-osgi/pom.xml b/jetty-osgi/pom.xml index a4c1333349c..b7178ff398a 100644 --- a/jetty-osgi/pom.xml +++ b/jetty-osgi/pom.xml @@ -12,7 +12,7 @@ pom - 3.17.200 + 3.18.0 3.10.200 3.6.100 1.0.0-v20070606 diff --git a/jetty-osgi/test-jetty-osgi/pom.xml b/jetty-osgi/test-jetty-osgi/pom.xml index afdd265ebfa..437843594a5 100644 --- a/jetty-osgi/test-jetty-osgi/pom.xml +++ b/jetty-osgi/test-jetty-osgi/pom.xml @@ -153,7 +153,6 @@ org.eclipse.jetty.osgi jetty-osgi-boot - ${project.version} test @@ -169,7 +168,6 @@ org.eclipse.jetty.osgi jetty-osgi-boot-jsp - ${project.version} test @@ -185,7 +183,6 @@ org.eclipse.jetty.osgi jetty-httpservice - ${project.version} test @@ -346,7 +343,6 @@ org.eclipse.jetty.osgi jetty-osgi-alpn - ${project.version} test diff --git a/jetty-p2/pom.xml b/jetty-p2/pom.xml index 939deac0873..0845293497a 100644 --- a/jetty-p2/pom.xml +++ b/jetty-p2/pom.xml @@ -47,5 +47,25 @@ ${project.version} pom + + org.eclipse.jetty.osgi + jetty-osgi-alpn + + + org.eclipse.jetty.osgi + jetty-osgi-boot + + + org.eclipse.jetty.osgi + jetty-osgi-boot-jsp + + + org.eclipse.jetty.osgi + jetty-osgi-boot-warurl + + + org.eclipse.jetty.osgi + jetty-httpservice + diff --git a/jetty-plus/src/main/config/etc/jetty-plus.xml b/jetty-plus/src/main/config/etc/jetty-plus.xml deleted file mode 100644 index ddc46f4bf29..00000000000 --- a/jetty-plus/src/main/config/etc/jetty-plus.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - org.eclipse.jetty.plus.webapp.EnvConfiguration - org.eclipse.jetty.plus.webapp.PlusConfiguration - - - - - - - - - - - diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java index 3de6eabcfe2..be8c99fc5b5 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java @@ -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 selectedModules = args.getSelectedModules(); + List sortedSelectedModules = modules.getSortedNames(selectedModules); + List 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 execModules = args.getEnabledModules().stream() - .map(name -> args.getAllModules().get(name)) + List execModules = args.getAllModules().getEnabled().stream() // Keep only the forking modules. .filter(module -> !module.getJvmArgs().isEmpty()) .map(Module::getName) diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java index 6bcca35ff67..0735a7b0698 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java @@ -395,7 +395,6 @@ public class Modules implements Iterable order.add(name); } } - return order; } diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java b/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java index b99d46b08ec..ca98f7f67d3 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java @@ -646,7 +646,29 @@ public class StartArgs return classpath; } + /** + * @deprecated use {@link #getSelectedModules()} instead + */ + @Deprecated public List getEnabledModules() + { + return getSelectedModules(); + } + + /** + *

+ * The list of selected Modules to enable based on configuration + * obtained from {@code start.d/*.ini}, {@code start.ini}, and command line. + *

+ * + *

+ * For full list of enabled modules, use {@link Modules#getEnabled()} + *

+ * + * @return the list of selected modules (by name) that the configuration has. + * @see Modules#getEnabled() + */ + public List 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 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 moduleNames) + private void selectModules(String source, List moduleNames) { for (String moduleName : moduleNames) { diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/fileinits/MavenMetadataTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/fileinits/MavenMetadataTest.java index 787c4c59347..9566da86add 100644 --- a/jetty-start/src/test/java/org/eclipse/jetty/start/fileinits/MavenMetadataTest.java +++ b/jetty-start/src/test/java/org/eclipse/jetty/start/fileinits/MavenMetadataTest.java @@ -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); } diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/usecases/BasicTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/usecases/BasicTest.java index e3abadc441a..37ae415da93 100644 --- a/jetty-start/src/test/java/org/eclipse/jetty/start/usecases/BasicTest.java +++ b/jetty-start/src/test/java/org/eclipse/jetty/start/usecases/BasicTest.java @@ -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 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 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 { diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java index d717f12a502..8033d6cd035 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java @@ -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; } } diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java index a8199035bbc..fa58df21680 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java @@ -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; diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/HugeResourceTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/HugeResourceTest.java index d4f225e1309..92ba1f8af4e 100644 --- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/HugeResourceTest.java +++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/HugeResourceTest.java @@ -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 diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java index 53f6f933d25..3b640bc05e5 100644 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java @@ -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); } } diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/JavaxOnCloseTest.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/JavaxOnCloseTest.java index 11a883b4edc..9143ca5387e 100644 --- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/JavaxOnCloseTest.java +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/JavaxOnCloseTest.java @@ -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)); + } } diff --git a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/Session.java b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/Session.java index fc10c00f8c4..355368bb362 100644 --- a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/Session.java +++ b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/Session.java @@ -63,6 +63,32 @@ public interface Session extends WebSocketPolicy, Closeable */ void close(int statusCode, String reason); + /** + * Send a websocket Close frame, with status code. + *

+ * 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. *

diff --git a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WriteCallback.java b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WriteCallback.java index 97b191d8d22..e42cfd22c16 100644 --- a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WriteCallback.java +++ b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WriteCallback.java @@ -20,7 +20,9 @@ package org.eclipse.jetty.websocket.api; */ public interface WriteCallback { - WriteCallback NOOP = new Adaptor(); + WriteCallback NOOP = new WriteCallback() + { + }; /** *

@@ -44,6 +46,7 @@ public interface WriteCallback { } + @Deprecated class Adaptor implements WriteCallback { @Override diff --git a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketRemoteEndpoint.java b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketRemoteEndpoint.java index a48d825bb87..c0c42348733 100644 --- a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketRemoteEndpoint.java +++ b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketRemoteEndpoint.java @@ -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 { diff --git a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java index 76aa6541d67..1e88f92836f 100644 --- a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java +++ b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java @@ -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 diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketOverHTTP2Test.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketOverHTTP2Test.java index 013b471d90c..4d951d0e43d 100644 --- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketOverHTTP2Test.java +++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketOverHTTP2Test.java @@ -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 diff --git a/pom.xml b/pom.xml index eba9f3a2970..e1fe911a70a 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ 4.2.0 6.3.1 1.5 - 10.3 + 10.3.1 1.15 1.21 2.11.0 @@ -90,7 +90,7 @@ 1.2 5.9 1.35 - 5.11.0 + 5.12.1 0.10.3 0.32.13 2.2.12 @@ -104,11 +104,10 @@ 2.0.2 2.17.2 1.3.0-alpha16 - 3.0.5 + 3.0.6 10.3.6 - 0.13.1 1.8.1 - 3.8.4 + 3.8.6 3.12.11 0.9.1 8.1.0 @@ -121,7 +120,7 @@ 2.1.1.RELEASE 1.2.5 1.2.5 - 1.17.2 + 1.17.3 3.1.9.Final 1.6.0.Final 1.19.0.Final @@ -148,7 +147,7 @@ 3.3.0 3.0.0-M2 2.10 - 3.0.0 + 3.1.0 3.0.0 3.0.1 3.0.0-M1 @@ -1681,11 +1680,32 @@ jetty-memcached-sessions ${project.version} + + org.eclipse.jetty.osgi + jetty-osgi-alpn + ${project.version} + org.eclipse.jetty.osgi jetty-osgi-boot ${project.version} + + org.eclipse.jetty.osgi + jetty-osgi-boot-jsp + ${project.version} + + + org.eclipse.jetty.osgi + jetty-osgi-boot-warurl + ${project.version} + + + org.eclipse.jetty.osgi + jetty-httpservice + ${project.version} + + org.eclipse.jetty.quic quic-client