Merged branch 'jetty-11.0.x' into 'jetty-12.0.x'.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2022-11-07 19:34:43 +01:00
commit 4225544222
No known key found for this signature in database
GPG Key ID: 1677D141BCF3584D
10 changed files with 378 additions and 19 deletions

View File

@ -7,7 +7,8 @@ updates:
schedule:
interval: "monthly"
day: "saturday"
time: "02:00"
time: "10:00"
timezone: "Australia/Brisbane"
# Associate with milestone 10.0.x
milestone: 6
ignore:
@ -50,7 +51,8 @@ updates:
schedule:
interval: "monthly"
day: "saturday"
time: "02:00"
time: "10:00"
timezone: "Australia/Brisbane"
# Associate with milestone 11.0.x
milestone: 7
ignore:
@ -95,3 +97,17 @@ updates:
versions: [ ">=5.0.0" ]
- dependency-name: "org.infinispan:*"
versions: [ ">=12" ]
- package-ecosystem: "maven"
directory: "/"
open-pull-requests-limit: 30
target-branch: "jetty-9.4.x"
schedule:
interval: "monthly"
day: "saturday"
time: "10:00"
timezone: "Australia/Brisbane"
ignore:
# Restrict updates in this branch to jetty in the 9.4.x space
- dependency-name: "org.infinispan:*"
versions: [ ">=12" ]

View File

@ -1444,14 +1444,15 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session
/**
* <p>The HTTP/2 specification requires that stream ids are monotonically increasing,
* see https://tools.ietf.org/html/rfc7540#section-5.1.1.</p>
* see <a href="https://tools.ietf.org/html/rfc7540#section-5.1.1">RFC 7540, 5.1.1</a>.</p>
* <p>This implementation uses a queue to atomically reserve a stream id and claim
* a slot in the queue; the slot is then assigned the entries to write.</p>
* <p>Concurrent threads push slots in the queue but only one thread flushes
* the slots, up to the slot that has a non-null entries to write, therefore
* guaranteeing that frames are sent strictly in their stream id order.</p>
* <p>This class also coordinates the creation of streams with the close of
* the session, see https://tools.ietf.org/html/rfc7540#section-6.8.</p>
* the session, see
* <a href="https://tools.ietf.org/html/rfc7540#section-6.8">RFC 7540, 6.8</a>.</p>
*/
private class StreamsState
{
@ -1483,6 +1484,8 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session
{
if (shutdownCallback != null)
return shutdownCallback;
if (closed == CloseState.CLOSED)
return CompletableFuture.completedFuture(null);
shutdownCallback = future = new Callback.Completable();
}
goAway(GoAwayFrame.GRACEFUL, Callback.NOOP);
@ -1946,7 +1949,6 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session
// such as onGoAway() may be in a race to finish,
// but only one moves to CLOSED and runs the action.
Runnable action = null;
CompletableFuture<Void> future;
try (AutoLock ignored = lock.lock())
{
long count = streamCount.get();
@ -1957,8 +1959,6 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session
return;
}
future = shutdownCallback;
switch (closed)
{
case LOCALLY_CLOSED ->
@ -1991,14 +1991,21 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session
LOG.debug("Executing zero streams action on {}", HTTP2Session.this);
action.run();
}
if (future != null)
future.complete(null);
}
private void terminate(GoAwayFrame frame)
{
if (LOG.isDebugEnabled())
LOG.debug("Terminating {}", HTTP2Session.this);
CompletableFuture<Void> completable;
try (AutoLock ignored = lock.lock())
{
completable = shutdownCallback;
}
if (completable != null)
completable.complete(null);
HTTP2Session.this.terminate(failure);
notifyClose(HTTP2Session.this, frame, Callback.NOOP);
}

View File

@ -0,0 +1,234 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.http2.tests;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.GoAwayFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.component.Graceful;
import org.eclipse.jetty.util.component.LifeCycle;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class GracefulShutdownTest extends AbstractTest
{
@Test
public void testGracefulShutdownWhileIdle() throws Exception
{
start(new ServerSessionListener()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
{
MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, HttpFields.EMPTY);
stream.headers(new HeadersFrame(stream.getId(), response, null, true), Callback.NOOP);
return null;
}
});
CountDownLatch clientRequestLatch = new CountDownLatch(1);
CountDownLatch clientGoAwayLatch = new CountDownLatch(2);
CountDownLatch clientCloseLatch = new CountDownLatch(1);
Session clientSession = newClientSession(new Session.Listener()
{
@Override
public void onGoAway(Session session, GoAwayFrame frame)
{
// One graceful GOAWAY and one normal GOAWAY.
clientGoAwayLatch.countDown();
}
@Override
public void onClose(Session session, GoAwayFrame frame, Callback callback)
{
clientCloseLatch.countDown();
callback.succeeded();
}
});
MetaData.Request request = newRequest(HttpMethod.GET.asString(), HttpFields.EMPTY);
clientSession.newStream(new HeadersFrame(request, null, true), new Stream.Listener()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
{
MetaData.Response response = (MetaData.Response)frame.getMetaData();
if (frame.isEndStream() && response.getStatus() == HttpStatus.OK_200)
clientRequestLatch.countDown();
}
});
assertTrue(clientRequestLatch.await(5, TimeUnit.SECONDS));
// Initiate graceful shutdown on server side.
CompletableFuture<Void> completable = Graceful.shutdown(connector);
assertTrue(clientGoAwayLatch.await(5, TimeUnit.SECONDS));
assertTrue(clientCloseLatch.await(5, TimeUnit.SECONDS));
assertNull(completable.get(5, TimeUnit.SECONDS));
}
@Test
public void testGracefulShutdownWithPendingStream() throws Exception
{
CountDownLatch serverLatch = new CountDownLatch(1);
start(new ServerSessionListener()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
{
stream.demand();
return new Stream.Listener()
{
@Override
public void onDataAvailable(Stream stream)
{
Stream.Data data = stream.readData();
data.release();
if (data.frame().isEndStream())
{
MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, HttpFields.EMPTY);
stream.headers(new HeadersFrame(stream.getId(), response, null, true), Callback.NOOP);
}
else
{
serverLatch.countDown();
stream.demand();
}
}
};
}
});
CountDownLatch clientRequestLatch = new CountDownLatch(1);
CountDownLatch clientGoAwayLatch = new CountDownLatch(2);
CountDownLatch clientCloseLatch = new CountDownLatch(1);
Session clientSession = newClientSession(new Session.Listener()
{
@Override
public void onGoAway(Session session, GoAwayFrame frame)
{
// One graceful GOAWAY and one normal GOAWAY.
clientGoAwayLatch.countDown();
}
@Override
public void onClose(Session session, GoAwayFrame frame, Callback callback)
{
clientCloseLatch.countDown();
callback.succeeded();
}
});
MetaData.Request request = newRequest(HttpMethod.GET.asString(), HttpFields.EMPTY);
Stream stream = clientSession.newStream(new HeadersFrame(request, null, false), new Stream.Listener()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
{
MetaData.Response response = (MetaData.Response)frame.getMetaData();
if (frame.isEndStream() && response.getStatus() == HttpStatus.OK_200)
clientRequestLatch.countDown();
}
}).get(5, TimeUnit.SECONDS);
stream.data(new DataFrame(stream.getId(), BufferUtil.toBuffer("hello"), false));
// Make sure the server has seen the stream.
assertTrue(serverLatch.await(5, TimeUnit.SECONDS));
// Initiate graceful shutdown on server side.
CompletableFuture<Void> completable = Graceful.shutdown(connector);
// Make sure the completable is not completed yet, waiting for the stream.
Thread.sleep(1000);
assertFalse(completable.isDone());
// Complete the stream.
stream.data(new DataFrame(stream.getId(), BufferUtil.toBuffer("world"), true));
assertTrue(clientGoAwayLatch.await(5, TimeUnit.SECONDS));
assertTrue(clientCloseLatch.await(5, TimeUnit.SECONDS));
assertNull(completable.get(5, TimeUnit.SECONDS));
}
@Test
public void testGracefulShutdownAfterSessionAlreadyClosed() throws Exception
{
CountDownLatch serverCloseLatch = new CountDownLatch(1);
AtomicReference<Session> serverSessionRef = new AtomicReference<>();
start(new ServerSessionListener()
{
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
{
serverSessionRef.set(stream.getSession());
MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, HttpFields.EMPTY);
stream.headers(new HeadersFrame(stream.getId(), response, null, true), Callback.NOOP);
return null;
}
@Override
public void onClose(Session session, GoAwayFrame frame, Callback callback)
{
serverCloseLatch.countDown();
callback.succeeded();
}
});
CountDownLatch clientRequestLatch = new CountDownLatch(1);
Session clientSession = newClientSession(new Session.Listener() {});
MetaData.Request request = newRequest(HttpMethod.GET.asString(), HttpFields.EMPTY);
clientSession.newStream(new HeadersFrame(request, null, true), new Stream.Listener()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
{
MetaData.Response response = (MetaData.Response)frame.getMetaData();
if (frame.isEndStream() && response.getStatus() == HttpStatus.OK_200)
clientRequestLatch.countDown();
}
});
assertTrue(clientRequestLatch.await(5, TimeUnit.SECONDS));
LifeCycle.stop(clientSession);
assertTrue(serverCloseLatch.await(5, TimeUnit.SECONDS));
Session serverSession = serverSessionRef.get();
assertNotNull(serverSession);
// Simulate a race condition where session.shutdown()
// is called after the session is closed.
CompletableFuture<Void> completable = serverSession.shutdown();
// Verify that it is completed.
assertTrue(completable.isDone());
}
}

View File

@ -39,6 +39,7 @@ import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
@ -100,6 +101,7 @@ public class End2EndClientTest
}
@Test
@Tag("flaky") // Issue #8815
public void testSimpleHTTP1() throws Exception
{
ContentResponse response = client.newRequest("https://localhost:" + connector.getLocalPort())
@ -137,6 +139,7 @@ public class End2EndClientTest
}
@Test
@Tag("flaky") // Issue #8815
public void testMultiThreadedHTTP1()
{
int count = 1000;

View File

@ -14,6 +14,8 @@
package org.eclipse.jetty.rewrite.handler;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.component.LifeCycle;
@ -22,7 +24,8 @@ import org.junit.jupiter.api.AfterEach;
public abstract class AbstractRuleTest
{
protected Server _server = new Server();
protected LocalConnector _connector = new LocalConnector(_server);
protected HttpConfiguration httpConfig = new HttpConfiguration();
protected LocalConnector _connector = new LocalConnector(_server, new HttpConnectionFactory(httpConfig));
protected RewriteHandler _rewriteHandler = new RewriteHandler();
@AfterEach

View File

@ -0,0 +1,83 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.rewrite.handler;
import java.util.stream.Stream;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.UriCompliance;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.URIUtil;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class CompactPathRuleTest extends AbstractRuleTest
{
public static Stream<Arguments> scenarios()
{
return Stream.of(
// shouldn't change anything
Arguments.of("/foo", null, "/foo", null, "/foo"),
Arguments.of("/", null, "/", null, "/"),
// simple compact path
Arguments.of("////foo", null, "/foo", null, "/foo"),
// with simple query
Arguments.of("//foo//bar", "a=b", "/foo/bar", "a=b", "/foo/bar?a=b"),
// with query that has double slashes (should preserve slashes in query)
Arguments.of("//foo//bar", "a=b//c", "/foo/bar", "a=b//c", "/foo/bar?a=b//c")
);
}
@ParameterizedTest
@MethodSource("scenarios")
public void testCompactPathRule(String inputPath, String inputQuery, String expectedPath, String expectedQuery, String expectedPathQuery) throws Exception
{
httpConfig.setUriCompliance(UriCompliance.UNSAFE);
CompactPathRule rule = new CompactPathRule();
_rewriteHandler.addRule(rule);
start(new Handler.Processor()
{
@Override
public void process(Request request, Response response, Callback callback)
{
Content.Sink.write(response, true, request.getHttpURI().getPathQuery(), callback);
}
});
String request = """
GET %s HTTP/1.1
Host: localhost
""".formatted(HttpURI.build().path(inputPath).query(inputQuery));
HttpTester.Response response = HttpTester.parseResponse(_connector.getResponse(request));
System.err.println(response.getReason());
assertEquals(HttpStatus.OK_200, response.getStatus());
HttpURI.Mutable result = HttpURI.build(response.getContent());
assertEquals(expectedPath, result.getPath());
assertEquals(expectedQuery, result.getQuery());
assertEquals(expectedPathQuery, result.getPathQuery());
}
}

View File

@ -16,6 +16,8 @@ package org.eclipse.jetty.ee9.servlets;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(WorkDirExtension.class)
@ -28,4 +30,12 @@ public class CloseableDoSFilterTest extends AbstractDoSFilterTest
{
startServer(workDir, CloseableDoSFilter.class);
}
@Override
@Test
@Tag("flaky")
public void testUnavailableIP() throws Exception
{
super.testUnavailableIP();
}
}

18
pom.xml
View File

@ -28,13 +28,15 @@
<apache.httpclient.version>4.5.13</apache.httpclient.version>
<apache.httpcore.version>4.4.15</apache.httpcore.version>
<asciidoctorj-diagram.version>2.2.3</asciidoctorj-diagram.version>
<asciidoctorj.version>2.5.5</asciidoctorj.version>
<asm.version>9.3</asm.version>
<asciidoctorj.version>2.5.6</asciidoctorj.version>
<asm.version>9.4</asm.version>
<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.3</checkstyle.version>
<commons-codec.version>1.15</commons-codec.version>
<commons.compress.version>1.22</commons.compress.version>
<commons.io.version>2.11.0</commons.io.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<conscrypt.version>2.5.2</conscrypt.version>
<disruptor.version>3.4.2</disruptor.version>
@ -54,7 +56,7 @@
<jboss.logging.annotations.version>2.2.1.Final</jboss.logging.annotations.version>
<jboss.logging.processor.version>2.2.1.Final</jboss.logging.processor.version>
<jboss.logging.version>3.5.0.Final</jboss.logging.version>
<jboss-logmanager.version>2.1.18.Final</jboss-logmanager.version>
<jboss-logmanager.version>2.1.19.Final</jboss-logmanager.version>
<jboss-threads.version>3.5.0.Final</jboss-threads.version>
<jetty-assembly-descriptors.version>1.1</jetty-assembly-descriptors.version>
<jetty.perf-helper.version>1.0.7</jetty.perf-helper.version>
@ -74,8 +76,8 @@
<junit.version>5.9.0</junit.version>
<kerb-simplekdc.version>2.0.2</kerb-simplekdc.version>
<log4j2.version>2.19.0</log4j2.version>
<logback.version>1.4.1</logback.version>
<mariadb.version>3.0.7</mariadb.version>
<logback.version>1.4.4</logback.version>
<mariadb.version>3.0.8</mariadb.version>
<mariadb.docker.version>10.3.6</mariadb.docker.version>
<maven.deps.version>3.8.4</maven.deps.version>
<maven-artifact-transfer.version>0.13.1</maven-artifact-transfer.version>
@ -88,10 +90,10 @@
<org.osgi.util.function.version>1.2.0</org.osgi.util.function.version>
<org.osgi.util.promise.version>1.2.0</org.osgi.util.promise.version>
<plexus-component-annotations.version>2.1.1</plexus-component-annotations.version>
<plexus-utils.version>3.4.2</plexus-utils.version>
<plexus-utils.version>3.5.0</plexus-utils.version>
<slf4j.version>2.0.2</slf4j.version>
<springboot.version>2.1.1.RELEASE</springboot.version>
<testcontainers.version>1.17.3</testcontainers.version>
<testcontainers.version>1.17.5</testcontainers.version>
<weld.version>4.0.3.Final</weld.version>
<wildfly.common.version>1.6.0.Final</wildfly.common.version>
<wildfly.elytron.version>1.20.1.Final</wildfly.elytron.version>
@ -128,7 +130,7 @@
<maven.release.plugin.version>2.5.3</maven.release.plugin.version>
<maven.remote-resources-plugin.version>3.0.0</maven.remote-resources-plugin.version>
<maven.resources.plugin.version>3.3.0</maven.resources.plugin.version>
<maven.shade.plugin.version>3.3.0</maven.shade.plugin.version>
<maven.shade.plugin.version>3.4.1</maven.shade.plugin.version>
<maven.site.plugin.version>3.12.0</maven.site.plugin.version>
<maven.surefire.plugin.version>3.0.0-M7</maven.surefire.plugin.version>
<maven.source.plugin.version>3.2.1</maven.source.plugin.version>

View File

@ -37,6 +37,7 @@
<configuration>
<finalName>${jmhjar.name}</finalName>
<shadeTestJar>true</shadeTestJar>
<createDependencyReducedPom>false</createDependencyReducedPom>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.openjdk.jmh.Main</mainClass>

View File

@ -912,7 +912,7 @@ public class DistributionTests extends AbstractJettyHomeTest
// Override the property on the command line with the correct password.
try (JettyHomeTester.Run run2 = distribution.start(pathProperty + "=cmdline"))
{
assertTrue(run2.awaitConsoleLogsFor("Started oejs.Server@", 5, TimeUnit.SECONDS));
assertTrue(run2.awaitConsoleLogsFor("Started oejs.Server@", START_TIMEOUT, TimeUnit.SECONDS));
assertThat("${jetty.base}/cmdline", jettyBase.resolve("cmdline"), PathMatchers.isRegularFile());
assertThat("${jetty.base}/modbased", jettyBase.resolve("modbased"), not(PathMatchers.exists()));
}