From 8dafe0988b17433fb8b2df226fa62f64cea1695d Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 14 Aug 2024 08:41:53 -0500 Subject: [PATCH 1/6] Issue #12163 - Adding more HttpConfiguration output of fields to dump and jmx --- .../jetty/server/HttpConfiguration.java | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java index 70360dac03c4..accc509c6f77 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java @@ -163,6 +163,7 @@ public HttpConfiguration(HttpConfiguration config) _redirectUriCompliance = config._redirectUriCompliance; _serverAuthority = config._serverAuthority; _localAddress = config._localAddress; + _maxUnconsumedRequestContentReads = config._maxUnconsumedRequestContentReads; } /** @@ -499,8 +500,9 @@ public void setFormEncodedMethods(String... methods) /** * @return the set of HTTP methods of requests that can be decoded as - * {@code x-www-form-urlencoded} content. + * {@code application/x-www-form-urlencoded} content. */ + @ManagedAttribute("The methods that support application/x-www-form-urlencoded content") public Set getFormEncodedMethods() { return _formEncodedMethods.keySet(); @@ -585,6 +587,7 @@ public void setMinResponseDataRate(long bytesPerSecond) _minResponseDataRate = bytesPerSecond; } + @ManagedAttribute("The HTTP compliance mode") public HttpCompliance getHttpCompliance() { return _httpCompliance; @@ -595,6 +598,7 @@ public void setHttpCompliance(HttpCompliance httpCompliance) _httpCompliance = httpCompliance; } + @ManagedAttribute("The URI compliance mode") public UriCompliance getUriCompliance() { return _uriCompliance; @@ -622,6 +626,7 @@ public void setRedirectUriCompliance(UriCompliance uriCompliance) * @return The CookieCompliance used for parsing request {@code Cookie} headers. * @see #getResponseCookieCompliance() */ + @ManagedAttribute("The HTTP request cookie compliance mode") public CookieCompliance getRequestCookieCompliance() { return _requestCookieCompliance; @@ -639,6 +644,7 @@ public void setRequestCookieCompliance(CookieCompliance cookieCompliance) * @return The CookieCompliance used for generating response {@code Set-Cookie} headers * @see #getRequestCookieCompliance() */ + @ManagedAttribute("The HTTP response cookie compliance mode") public CookieCompliance getResponseCookieCompliance() { return _responseCookieCompliance; @@ -655,6 +661,7 @@ public void setResponseCookieCompliance(CookieCompliance cookieCompliance) /** * @return the {@link MultiPartCompliance} used for validating multipart form syntax. */ + @ManagedAttribute("The multipart/form-data compliance mode") public MultiPartCompliance getMultiPartCompliance() { return _multiPartCompliance; @@ -765,7 +772,7 @@ public void setLocalAddress(SocketAddress localAddress) * * @return Returns the connection server authority (name/port) or null */ - @ManagedAttribute("The server authority if none provided by requests") + @ManagedAttribute("The server authority override") public HostPort getServerAuthority() { return _serverAuthority; @@ -833,6 +840,7 @@ public void dump(Appendable out, String indent) throws IOException "requestHeaderSize=" + _requestHeaderSize, "responseHeaderSize=" + _responseHeaderSize, "headerCacheSize=" + _headerCacheSize, + "headerCacheCaseSensitive=" + _headerCacheCaseSensitive, "secureScheme=" + _secureScheme, "securePort=" + _securePort, "idleTimeout=" + _idleTimeout, @@ -842,12 +850,21 @@ public void dump(Appendable out, String indent) throws IOException "delayDispatchUntilContent=" + _delayDispatchUntilContent, "persistentConnectionsEnabled=" + _persistentConnectionsEnabled, "maxErrorDispatches=" + _maxErrorDispatches, + "useInputDirectByteBuffers=" + _useInputDirectByteBuffers, + "useOutputDirectByteBuffers=" + _useOutputDirectByteBuffers, "minRequestDataRate=" + _minRequestDataRate, "minResponseDataRate=" + _minResponseDataRate, + "httpCompliance=" + _httpCompliance, + "uriCompliance=" + _uriCompliance, + "redirectUriCompliance=" + _redirectUriCompliance, "requestCookieCompliance=" + _requestCookieCompliance, "responseCookieCompliance=" + _responseCookieCompliance, + "multiPartCompliance=" + _multiPartCompliance, "notifyRemoteAsyncErrors=" + _notifyRemoteAsyncErrors, - "relativeRedirectAllowed=" + _relativeRedirectAllowed + "relativeRedirectAllowed=" + _relativeRedirectAllowed, + "serverAuthority=" + _serverAuthority, + "localAddress=" + _localAddress, + "maxUnconsumedRequestContentReads=" + _maxUnconsumedRequestContentReads ); } From 457a672756458bba2168ceaac2745e2212650521 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 15 Aug 2024 18:58:33 -0500 Subject: [PATCH 2/6] Issue #11408 - Environment property values are not expanded (#12169) If a property belonged to an Environment, then that property value was not expanded. --- .../org/eclipse/jetty/start/StartArgs.java | 5 +- .../org/eclipse/jetty/start/MainTest.java | 10 +- .../org/eclipse/jetty/start/PropertyDump.java | 43 ++--- .../jetty/start/PropertyPassingTest.java | 170 ++++++++++++++++-- 4 files changed, 183 insertions(+), 45 deletions(-) diff --git a/jetty-core/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java b/jetty-core/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java index 6657fe4787da..93e679f6b44b 100644 --- a/jetty-core/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java +++ b/jetty-core/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java @@ -687,8 +687,9 @@ else if (properties.size() > 0) // TODO module path - for (Prop property : environment.getProperties()) - cmd.addArg(property.key, property.value); + Props props = environment.getProperties(); + for (Prop property : props) + cmd.addArg(property.key, props.expand(property.value)); for (Path xmlFile : environment.getXmlFiles()) cmd.addArg(xmlFile.toAbsolutePath().toString()); diff --git a/jetty-core/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java b/jetty-core/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java index 2b8d29468773..eac62584e6c9 100644 --- a/jetty-core/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java +++ b/jetty-core/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java @@ -14,7 +14,6 @@ package org.eclipse.jetty.start; import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.PrintStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -24,7 +23,6 @@ import java.util.List; import org.eclipse.jetty.toolchain.test.MavenPaths; -import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -67,7 +65,7 @@ public void testStopProcessing() throws Exception public void testListConfig() throws Exception { List cmdLineArgs = new ArrayList<>(); - File testJettyHome = MavenTestingUtils.getTestResourceDir("dist-home"); + Path testJettyHome = MavenPaths.findTestResourceDir("dist-home"); cmdLineArgs.add("user.dir=" + testJettyHome); cmdLineArgs.add("-Duser.dir=foo"); // used to test "source" display on "Java Environment" cmdLineArgs.add("jetty.home=" + testJettyHome); @@ -99,8 +97,8 @@ public void testListConfig() throws Exception public void testUnknownDistroCommand() throws Exception { List cmdLineArgs = new ArrayList<>(); - File testJettyHome = MavenTestingUtils.getTestResourceDir("dist-home"); - Path testJettyBase = MavenTestingUtils.getTargetTestingPath("base-example-unknown"); + Path testJettyHome = MavenPaths.findTestResourceDir("dist-home"); + Path testJettyBase = MavenPaths.targetTestDir("base-example-unknown"); FS.ensureDirectoryExists(testJettyBase); Path zedIni = testJettyBase.resolve("start.d/zed.ini"); FS.ensureDirectoryExists(zedIni.getParent()); @@ -159,7 +157,7 @@ public void testJvmArgExpansion() throws Exception { List cmdLineArgs = new ArrayList<>(); - Path homePath = MavenTestingUtils.getTestResourcePathDir("dist-home").toRealPath(); + Path homePath = MavenPaths.findTestResourceDir("dist-home"); cmdLineArgs.add("jetty.home=" + homePath); cmdLineArgs.add("user.dir=" + homePath); diff --git a/jetty-core/jetty-start/src/test/java/org/eclipse/jetty/start/PropertyDump.java b/jetty-core/jetty-start/src/test/java/org/eclipse/jetty/start/PropertyDump.java index b7628290b70b..ab287a43f144 100644 --- a/jetty-core/jetty-start/src/test/java/org/eclipse/jetty/start/PropertyDump.java +++ b/jetty-core/jetty-start/src/test/java/org/eclipse/jetty/start/PropertyDump.java @@ -16,30 +16,35 @@ import java.io.File; import java.io.FileReader; import java.io.IOException; -import java.util.Enumeration; +import java.util.Collections; +import java.util.Objects; import java.util.Properties; +import java.util.function.Predicate; public class PropertyDump { public static void main(String[] args) { System.out.printf("PropertyDump%n"); + + Predicate nameSelectionPredicate = + (name) -> + name.startsWith("test.") || + name.startsWith("jetty."); + // As System Properties Properties props = System.getProperties(); - Enumeration names = props.propertyNames(); - while (names.hasMoreElements()) - { - String name = (String)names.nextElement(); - // only interested in "test." prefixed properties - if (name.startsWith("test.")) - { - System.out.printf("System %s=%s%n", name, props.getProperty(name)); - } - } + props.stringPropertyNames() + .stream() + .filter(nameSelectionPredicate) + .sorted() + .forEach((name) -> + System.out.printf("System %s=%s%n", name, props.getProperty(name))); // As File Argument for (String arg : args) { + System.out.printf("Arg [%s]%n", arg); if (arg.endsWith(".properties")) { Properties aprops = new Properties(); @@ -47,15 +52,13 @@ public static void main(String[] args) try (FileReader reader = new FileReader(propFile)) { aprops.load(reader); - Enumeration anames = aprops.propertyNames(); - while (anames.hasMoreElements()) - { - String name = (String)anames.nextElement(); - if (name.startsWith("test.")) - { - System.out.printf("%s %s=%s%n", propFile.getName(), name, aprops.getProperty(name)); - } - } + Collections.list(aprops.propertyNames()) + .stream() + .map(Objects::toString) + .filter(nameSelectionPredicate) + .sorted() + .forEach((name) -> + System.out.printf("%s %s=%s%n", propFile.getName(), name, aprops.getProperty(name))); } catch (IOException e) { diff --git a/jetty-core/jetty-start/src/test/java/org/eclipse/jetty/start/PropertyPassingTest.java b/jetty-core/jetty-start/src/test/java/org/eclipse/jetty/start/PropertyPassingTest.java index 3e5bf9816d8f..5f609f36b1b6 100644 --- a/jetty-core/jetty-start/src/test/java/org/eclipse/jetty/start/PropertyPassingTest.java +++ b/jetty-core/jetty-start/src/test/java/org/eclipse/jetty/start/PropertyPassingTest.java @@ -20,19 +20,26 @@ import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.eclipse.jetty.toolchain.test.IO; -import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.toolchain.test.MavenPaths; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; +@ExtendWith(WorkDirExtension.class) public class PropertyPassingTest { private static class ConsoleCapture implements Runnable @@ -86,10 +93,12 @@ public ConsoleCapture start() } } + public WorkDir workDir; + @Test public void testAsJvmArg() throws IOException, InterruptedException { - File bogusXml = MavenTestingUtils.getTestResourceFile("bogus.xml"); + Path bogusXml = MavenPaths.findTestResourceFile("bogus.xml"); // Setup command line List commands = new ArrayList<>(); @@ -100,7 +109,7 @@ public void testAsJvmArg() throws IOException, InterruptedException // addDebug(commands); commands.add("-Dtest.foo=bar"); // TESTING THIS commands.add(getStartJarBin()); - commands.add(bogusXml.getAbsolutePath()); + commands.add(bogusXml.toAbsolutePath().toString()); // Run command, collect output String output = collectRunOutput(commands); @@ -112,7 +121,7 @@ public void testAsJvmArg() throws IOException, InterruptedException @Test public void testAsCommandLineArg() throws IOException, InterruptedException { - File bogusXml = MavenTestingUtils.getTestResourceFile("bogus.xml"); + Path bogusXml = MavenPaths.findTestResourceFile("bogus.xml"); // Setup command line List commands = new ArrayList<>(); @@ -123,7 +132,7 @@ public void testAsCommandLineArg() throws IOException, InterruptedException // addDebug(commands); commands.add(getStartJarBin()); commands.add("test.foo=bar"); // TESTING THIS - commands.add(bogusXml.getAbsolutePath()); + commands.add(bogusXml.toAbsolutePath().toString()); // Run command, collect output String output = collectRunOutput(commands); @@ -135,7 +144,7 @@ public void testAsCommandLineArg() throws IOException, InterruptedException @Test public void testAsDashDCommandLineArg() throws IOException, InterruptedException { - File bogusXml = MavenTestingUtils.getTestResourceFile("bogus.xml"); + Path bogusXml = MavenPaths.findTestResourceFile("bogus.xml"); // Setup command line List commands = new ArrayList<>(); @@ -146,7 +155,7 @@ public void testAsDashDCommandLineArg() throws IOException, InterruptedException // addDebug(commands); commands.add(getStartJarBin()); commands.add("-Dtest.foo=bar"); // TESTING THIS - commands.add(bogusXml.getAbsolutePath()); + commands.add(bogusXml.toAbsolutePath().toString()); // Run command, collect output String output = collectRunOutput(commands); @@ -155,16 +164,143 @@ public void testAsDashDCommandLineArg() throws IOException, InterruptedException assertThat(output, containsString("test.foo=bar")); } + @Test + public void testExpandPropertyArg() throws IOException, InterruptedException + { + Path bogusXml = MavenPaths.findTestResourceFile("bogus.xml"); + + // Setup command line + List commands = new ArrayList<>(); + commands.add(getJavaBin()); + commands.add("-Dmain.class=" + PropertyDump.class.getName()); + commands.add("-Dtest.dir=/opt/dists/jetty"); + commands.add("-cp"); + commands.add(getClassPath()); + // addDebug(commands); + commands.add(getStartJarBin()); + commands.add("test.config=${test.dir}/etc/config.ini"); // TESTING THIS + commands.add(bogusXml.toAbsolutePath().toString()); + + // Run command, collect output + String output = collectRunOutput(commands); + + // Test for values + assertThat(output, containsString("test.config=/opt/dists/jetty/etc/config.ini")); + } + + @Test + public void testExpandPropertyDArg() throws IOException, InterruptedException + { + Path bogusXml = MavenPaths.findTestResourceFile("bogus.xml"); + + // Setup command line + List commands = new ArrayList<>(); + commands.add(getJavaBin()); + commands.add("-Dmain.class=" + PropertyDump.class.getName()); + commands.add("-Dtest.dir=/opt/dists/jetty"); + commands.add("-cp"); + commands.add(getClassPath()); + // addDebug(commands); + commands.add(getStartJarBin()); + commands.add("-Dtest.config=${test.dir}/etc/config.ini"); // TESTING THIS + commands.add(bogusXml.toAbsolutePath().toString()); + + // Run command, collect output + String output = collectRunOutput(commands); + + // Test for values + assertThat(output, containsString("test.config=/opt/dists/jetty/etc/config.ini")); + } + + @Test + public void testExpandPropertyStartIni() throws IOException, InterruptedException + { + Path bogusXml = MavenPaths.findTestResourceFile("bogus.xml"); + Path base = workDir.getEmptyPathDir(); + Path ini = base.resolve("start.d/config.ini"); + FS.ensureDirectoryExists(ini.getParent()); + String iniBody = """ + # Enabling a single module (that does nothing) to let start.jar run + --module=empty + # TESTING THIS (it should expand the ${jetty.base} portion + test.config=${jetty.base}/etc/config.ini + """; + Files.writeString(ini, iniBody, StandardCharsets.UTF_8); + + // Setup command line + List commands = new ArrayList<>(); + commands.add(getJavaBin()); + commands.add("-Dmain.class=" + PropertyDump.class.getName()); + commands.add("-Djetty.base=" + base); + commands.add("-cp"); + commands.add(getClassPath()); + // addDebug(commands); + commands.add(getStartJarBin()); + commands.add(bogusXml.toAbsolutePath().toString()); + + // Run command, collect output + String output = collectRunOutput(commands); + + // Test for values + Path expectedPath = base.resolve("etc/config.ini"); + assertThat(output, containsString("test.config=" + expectedPath)); + } + + @Test + public void testExpandEnvProperty() throws IOException, InterruptedException + { + Path bogusXml = MavenPaths.findTestResourceFile("bogus.xml"); + Path base = workDir.getEmptyPathDir(); + Path module = base.resolve("modules/env-config.mod"); + FS.ensureDirectoryExists(module.getParent()); + String moduleBody = """ + [environment] + eex + + [ini-template] + # configuration option + # test.config=${jetty.home}/etc/eex-config.ini + """; + Files.writeString(module, moduleBody, StandardCharsets.UTF_8); + Path ini = base.resolve("start.d/config.ini"); + FS.ensureDirectoryExists(ini.getParent()); + String iniBody = """ + # Enabling a single module (that does nothing) to let start.jar run + --module=env-config + # TESTING THIS (it should expand the ${jetty.base} portion + test.config=${jetty.base}/etc/config.ini + """; + Files.writeString(ini, iniBody, StandardCharsets.UTF_8); + + // Setup command line + List commands = new ArrayList<>(); + commands.add(getJavaBin()); + commands.add("-Dmain.class=" + PropertyDump.class.getName()); + commands.add("-Djetty.base=" + base); + commands.add("-cp"); + commands.add(getClassPath()); + // addDebug(commands); + commands.add(getStartJarBin()); + commands.add(bogusXml.toAbsolutePath().toString()); + + // Run command, collect output + String output = collectRunOutput(commands); + + // Test for values + Path expectedPath = base.resolve("etc/config.ini"); + assertThat(output, containsString("test.config=" + expectedPath)); + } + private String getClassPath() { - StringBuilder cp = new StringBuilder(); - String pathSep = System.getProperty("path.separator"); - cp.append(MavenTestingUtils.getProjectDir("target/classes")); - cp.append(pathSep); - cp.append(MavenTestingUtils.getProjectDir("target/test-classes")); - cp.append(pathSep); - cp.append(MavenTestingUtils.getProjectDir("target/jetty-util")); - return cp.toString(); + return String.join( + File.pathSeparator, + List.of( + MavenPaths.projectBase().resolve("target/classes").toString(), + MavenPaths.projectBase().resolve("target/test-classes").toString(), + MavenPaths.projectBase().resolve("target/jetty-util").toString() + ) + ); } protected void addDebug(List commands) @@ -180,11 +316,10 @@ private String collectRunOutput(List commands) throws IOException, Inter { cline.append(command).append(" "); } - System.out.println("Command line: " + cline); ProcessBuilder builder = new ProcessBuilder(commands); // Set PWD - builder.directory(MavenTestingUtils.getTestResourceDir("empty.home")); + builder.directory(MavenPaths.findTestResourceDir("empty.home").toFile()); Process pid = builder.start(); ConsoleCapture stdOutPump = new ConsoleCapture("STDOUT", pid.getInputStream()).start(); @@ -193,6 +328,7 @@ private String collectRunOutput(List commands) throws IOException, Inter int exitCode = pid.waitFor(); if (exitCode != 0) { + System.out.println("Command line: " + cline); System.out.printf("STDERR: [" + stdErrPump.getConsoleOutput() + "]%n"); System.out.printf("STDOUT: [" + stdOutPump.getConsoleOutput() + "]%n"); assertThat("Exit code", exitCode, is(0)); From 8b4e13dbeaada1d578358c9ac5f4dd3ae0faa35c Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Tue, 20 Aug 2024 15:14:37 +1000 Subject: [PATCH 3/6] Jetty 12.1.x 12088 core requested session ID source (#12145) add isRequestedSessionIdFromCookie/URL for core request Co-authored-by: Jan Bartel --- .../server/session/SessionDocs.java | 2 +- .../security/openid/OpenIdAuthenticator.java | 10 +- .../jaas/JAASLdapLoginServiceTest.java | 1 - .../jetty/session/AbstractSessionManager.java | 126 +++++++++++++----- .../eclipse/jetty/session/SessionHandler.java | 20 ++- .../jetty/session/SessionHandlerTest.java | 20 ++- .../org/eclipse/jetty/util/Attributes.java | 9 +- .../jetty/ee10/servlet/ServletApiRequest.java | 14 +- .../ee10/servlet/ServletContextRequest.java | 2 + .../jetty/ee10/servlet/SessionHandler.java | 22 +-- .../jetty/ee11/servlet/ServletApiRequest.java | 14 +- .../ee11/servlet/ServletContextRequest.java | 2 + .../jetty/ee11/servlet/SessionHandler.java | 22 +-- .../jetty/ee9/nested/ContextHandler.java | 14 +- .../org/eclipse/jetty/ee9/nested/Request.java | 12 +- .../jetty/ee9/nested/SessionHandler.java | 2 +- .../jetty/ee9/nested/ResponseTest.java | 8 +- 17 files changed, 205 insertions(+), 95 deletions(-) diff --git a/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/server/session/SessionDocs.java b/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/server/session/SessionDocs.java index 5f6da14053d5..51264ad11807 100644 --- a/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/server/session/SessionDocs.java +++ b/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/server/session/SessionDocs.java @@ -95,7 +95,7 @@ public void coreSessionHandler() org.eclipse.jetty.session.SessionHandler sessionHandler = new org.eclipse.jetty.session.SessionHandler(); sessionHandler.setSessionCookie("SIMPLE"); sessionHandler.setUsingCookies(true); - sessionHandler.setUsingURLs(false); + sessionHandler.setUsingUriParameters(false); sessionHandler.setSessionPath("/"); server.setHandler(sessionHandler); sessionHandler.setHandler(new Handler.Abstract() diff --git a/jetty-core/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java b/jetty-core/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java index 16fab5da1106..ed9a6026a868 100644 --- a/jetty-core/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java +++ b/jetty-core/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java @@ -444,14 +444,12 @@ public AuthenticationState validateRequest(Request request, Response response, C return AuthenticationState.SEND_FAILURE; } - // TODO: No session API to work this out? - /* - if (request.isRequestedSessionIdFromURL()) + String sessionIdFrom = (String)request.getAttribute("org.eclipse.jetty.session.RequestedSession.sessionIdFrom"); + if (sessionIdFrom != null && !sessionIdFrom.startsWith("cookie")) { - sendError(req, res, cb, "Session ID must be a cookie to support OpenID authentication"); - return Authentication.SEND_FAILURE; + sendError(request, response, cb, "Session ID must be a cookie to support OpenID authentication"); + return AuthenticationState.SEND_FAILURE; } - */ // Handle a request for authentication. if (isJSecurityCheck(uri)) diff --git a/jetty-core/jetty-security/src/test/java/org/eclipse/jetty/security/jaas/JAASLdapLoginServiceTest.java b/jetty-core/jetty-security/src/test/java/org/eclipse/jetty/security/jaas/JAASLdapLoginServiceTest.java index e2692df08b5b..78c731529146 100644 --- a/jetty-core/jetty-security/src/test/java/org/eclipse/jetty/security/jaas/JAASLdapLoginServiceTest.java +++ b/jetty-core/jetty-security/src/test/java/org/eclipse/jetty/security/jaas/JAASLdapLoginServiceTest.java @@ -396,5 +396,4 @@ public Session getSession(boolean create) return null; } } - } diff --git a/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/AbstractSessionManager.java b/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/AbstractSessionManager.java index 7f2834fb2859..d4e894c3ea7d 100644 --- a/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/AbstractSessionManager.java +++ b/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/AbstractSessionManager.java @@ -40,6 +40,7 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Session; import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; @@ -981,24 +982,6 @@ public void setUsingUriParameters(boolean usingUriParameters) _usingUriParameters = usingUriParameters; } - /** - * @deprecated use {@link #isUsingUriParameters()} instead, will be removed in Jetty 12.1.0 - */ - @Deprecated(since = "12.0.1", forRemoval = true) - public boolean isUsingURLs() - { - return isUsingUriParameters(); - } - - /** - * @deprecated use {@link #setUsingUriParameters(boolean)} instead, will be removed in Jetty 12.1.0 - */ - @Deprecated(since = "12.0.1", forRemoval = true) - public void setUsingURLs(boolean usingURLs) - { - setUsingUriParameters(usingURLs); - } - /** * Create a new Session, using the requested session id if possible. * @param request the inbound request @@ -1229,7 +1212,7 @@ protected RequestedSession resolveRequestedSessionId(Request request) { //Cookie[] cookies = request.getCookies(); List cookies = Request.getCookies(request); - if (cookies != null && cookies.size() > 0) + if (!cookies.isEmpty()) { final String sessionCookie = getSessionCookie(); for (HttpCookie cookie : cookies) @@ -1279,7 +1262,7 @@ protected RequestedSession resolveRequestedSessionId(Request request) } if (ids == null) - return NO_REQUESTED_SESSION; + return RequestedSession.NO_REQUESTED_SESSION; if (LOG.isDebugEnabled()) LOG.debug("Got Session IDs {} from cookies {}", ids, cookieIds); @@ -1319,8 +1302,7 @@ else if (session.getId().equals(getSessionIdManager().getId(id))) { //we already have a valid session and now have a duplicate ID for it if (LOG.isDebugEnabled()) - LOG.debug(duplicateSession( - requestedSessionId, true, requestedSessionIdFromCookie, + LOG.debug(duplicateSession(requestedSessionId, requestedSessionIdFromCookie, id, false, i < cookieIds)); } else @@ -1350,26 +1332,27 @@ else if (session.getId().equals(getSessionIdManager().getId(id))) } throw new BadMessageException(duplicateSession( - requestedSessionId, true, requestedSessionIdFromCookie, + requestedSessionId, requestedSessionIdFromCookie, id, true, i < cookieIds)); } else if (LOG.isDebugEnabled()) { LOG.debug(duplicateSession( - requestedSessionId, true, requestedSessionIdFromCookie, + requestedSessionId, requestedSessionIdFromCookie, id, false, i < cookieIds)); } } } - return new RequestedSession((session != null && session.isValid()) ? session : null, requestedSessionId, requestedSessionIdFromCookie); + return new RequestedSession((session != null && session.isValid()) ? session : null, requestedSessionId, + requestedSessionIdFromCookie ? RequestedSession.ID_FROM_COOKIE : RequestedSession.ID_FROM_URI_PARAMETER); } - private static String duplicateSession(String id0, boolean valid0, boolean cookie0, String id1, boolean valid1, boolean cookie1) + private static String duplicateSession(String id0, boolean fromCookie0, String id1, boolean valid1, boolean fromCookie1) { return "Duplicate sessions: %s[%s,%s] & %s[%s,%s]".formatted( - id0, valid0 ? "valid" : "unknown", cookie0 ? "cookie" : "param", - id1, valid1 ? "valid" : "unknown", cookie1 ? "cookie" : "param"); + id0, "valid", fromCookie0 ? RequestedSession.ID_FROM_COOKIE : RequestedSession.ID_FROM_URI_PARAMETER, + id1, valid1 ? "valid" : "unknown", fromCookie1 ? RequestedSession.ID_FROM_COOKIE : RequestedSession.ID_FROM_URI_PARAMETER); } /** @@ -1379,12 +1362,89 @@ private void shutdownSessions() { _sessionCache.shutdown(); } - - public record RequestedSession(ManagedSession session, String sessionId, boolean sessionIdFromCookie) - { - } - private static final RequestedSession NO_REQUESTED_SESSION = new RequestedSession(null, null, false); + /** + * Details of the requested session. + * Session implementations should make an instance of this record available as a hidden (not in name set) request + * attribute for the name "org.eclipse.jetty.session.AbstractSessionManager$RequestedSession" + * @param session The {@link Session} associated with the ID, which may have been invalidated or changed ID since the + * request was received; or {@code null} if no session existed matching the requested ID. + * @param sessionId The requested session ID. + * @param sessionIdFrom A {@link String} representing the source of the session ID. Common values include: + * {@link #ID_FROM_COOKIE} or {@link #ID_FROM_URI_PARAMETER} if there is no ID. + */ + public record RequestedSession(ManagedSession session, String sessionId, String sessionIdFrom) + { + public static final RequestedSession NO_REQUESTED_SESSION = new RequestedSession(null, null, null); + public static final String ATTRIBUTE = "org.eclipse.jetty.session.RequestedSession"; + public static final String ID_FROM_COOKIE = "cookie"; + public static final String ID_FROM_URI_PARAMETER = "uri"; + + /** + * Get the {@code RequestedSession} by attribute + * @param request The attributes to query + * @return The found {@code RequestedSession} or {@link #NO_REQUESTED_SESSION} if none found. Never {@code null}. + */ + public static RequestedSession byAttribute(Attributes request) + { + RequestedSession requestedSession = (RequestedSession)request.getAttribute(ATTRIBUTE); + return requestedSession == null ? NO_REQUESTED_SESSION : requestedSession; + } + + /** + * @param name An attribute name + * @return {@code true} if the attribute name is applicable to a requested session. + * @see #getAttribute(String) + */ + public static boolean isApplicableAttribute(String name) + { + return name != null && name.startsWith(ATTRIBUTE); + } + + /** + * Get attributes asssociated with this requested session: + *
    + *
  • `org.eclipse.jetty.session.RequestedSession` this instance.
  • + *
  • `org.eclipse.jetty.session.RequestedSession.session` the {@link #session()}.
  • + *
  • `org.eclipse.jetty.session.RequestedSession.sessionId` the {@link #sessionId()}.
  • + *
  • `org.eclipse.jetty.session.RequestedSession.sessionIdFrom` the {@link #sessionIdFrom()}.
  • + *
+ * @param name An attributed name + * @return the attribute value or {@code null} + */ + public Object getAttribute(String name) + { + if (name == null || name.length() < ATTRIBUTE.length()) + return null; + + if (ATTRIBUTE.equals(name)) + return this; + + if (name.startsWith(ATTRIBUTE) && name.charAt(ATTRIBUTE.length()) == '.') + { + return switch (name.substring(ATTRIBUTE.length() + 1)) + { + case "session" -> session(); + case "sessionId" -> sessionId(); + case "sessionIdFrom" -> sessionIdFrom(); + default -> null; + }; + } + + return null; + } + + /** + * Test if this {@code RequestedSession} ID is from a particular session source + * @param source A {@link String} representing the source of the session ID. Common values include: + * {@link #ID_FROM_COOKIE} or {@link #ID_FROM_URI_PARAMETER} if there is no ID. + * @return {@code True} iff this {@code RequestedSession} ID is from the source. + */ + public boolean isSessionIdFrom(String source) + { + return source != null && source.equals(sessionIdFrom); + } + } /** * A session cookie is marked as secure IFF any of the following conditions are true: diff --git a/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/SessionHandler.java b/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/SessionHandler.java index c847d7c94742..3e71cc1f05f8 100644 --- a/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/SessionHandler.java +++ b/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/SessionHandler.java @@ -82,10 +82,10 @@ public Session.API newSessionAPIWrapper(ManagedSession session) return null; } - private class SessionRequest extends Request.Wrapper + public class SessionRequest extends Request.Wrapper { private final AtomicReference _session = new AtomicReference<>(); - private String _requestedSessionId; + RequestedSession _requestedSession; private Response _response; public SessionRequest(Request request) @@ -103,6 +103,14 @@ ManagedSession getManagedSession() return _session.get(); } + @Override + public Object getAttribute(String name) + { + if (RequestedSession.isApplicableAttribute(name)) + return _requestedSession.getAttribute(name); + return super.getAttribute(name); + } + @Override public Session getSession(boolean create) { @@ -113,7 +121,7 @@ public Session getSession(boolean create) if (session == null && create) { - newSession(this, _requestedSessionId, this::setManagedSession); + newSession(this, _requestedSession.sessionId(), this::setManagedSession); session = _session.get(); HttpCookie cookie = getSessionCookie(session, getConnectionMetaData().isSecure()); if (cookie != null) @@ -126,10 +134,8 @@ public Session getSession(boolean create) public boolean process(Handler handler, Response response, Callback callback) throws Exception { _response = response; - - RequestedSession requestedSession = resolveRequestedSessionId(this); - _requestedSessionId = requestedSession.sessionId(); - ManagedSession session = requestedSession.session(); + _requestedSession = resolveRequestedSessionId(this); + ManagedSession session = _requestedSession.session(); if (session != null) { diff --git a/jetty-core/jetty-session/src/test/java/org/eclipse/jetty/session/SessionHandlerTest.java b/jetty-core/jetty-session/src/test/java/org/eclipse/jetty/session/SessionHandlerTest.java index b4ebae94116e..9092c64630ce 100644 --- a/jetty-core/jetty-session/src/test/java/org/eclipse/jetty/session/SessionHandlerTest.java +++ b/jetty-core/jetty-session/src/test/java/org/eclipse/jetty/session/SessionHandlerTest.java @@ -25,6 +25,7 @@ import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Session; +import org.eclipse.jetty.session.AbstractSessionManager.RequestedSession; import org.eclipse.jetty.util.Callback; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -148,8 +149,21 @@ public boolean handle(Request request, Response response, Callback callback) { if (session.isNew()) out.append("New\n"); + + RequestedSession requestedSession = RequestedSession.byAttribute(request); + + out.append("RequestedSessionIdFromCookie: ") + .append(requestedSession.isSessionIdFrom(RequestedSession.ID_FROM_COOKIE)) + .append('\n'); + out.append("RequestedSessionIdFromURL: ") + .append(requestedSession.isSessionIdFrom(RequestedSession.ID_FROM_URI_PARAMETER)) + .append('\n'); for (String name : session.getAttributeNameSet()) - out.append("Attribute ").append(name).append(" = ").append(session.getAttribute(name)).append('\n'); + out.append("Attribute ") + .append(name) + .append(" = ") + .append(session.getAttribute(name)) + .append('\n'); out.append("URI [") .append(session.encodeURI(request, "/some/path", request.getHeaders().contains(HttpHeader.COOKIE))) .append("]"); @@ -499,6 +513,8 @@ public void testCookieAndURI() throws Exception assertThat(response.getStatus(), equalTo(200)); content = response.getContent(); assertThat(content, containsString("Session=" + id.substring(0, id.indexOf(".node0")))); + assertThat(content, containsString("RequestedSessionIdFromCookie: true")); + assertThat(content, containsString("RequestedSessionIdFromURL: false")); assertThat(content, containsString("URI [/some/path]")); // Cookies known to be in use // Get with parameter @@ -513,6 +529,8 @@ public void testCookieAndURI() throws Exception assertThat(response.getStatus(), equalTo(200)); content = response.getContent(); assertThat(content, containsString("Session=" + id.substring(0, id.indexOf(".node0")))); + assertThat(content, containsString("RequestedSessionIdFromCookie: false")); + assertThat(content, containsString("RequestedSessionIdFromURL: true")); assertThat(content, containsString("URI [/some/path;session_id=%s]".formatted(id))); // Cookies not in use // Get with both, but param wrong diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Attributes.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Attributes.java index ba5c7f8db6aa..6db7c90e6148 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Attributes.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Attributes.java @@ -32,6 +32,10 @@ /** * Attributes. * Interface commonly used for storing attributes. + *

+ * Some attributes may be "hidden" attributes, in that they are only found by an explicit call to + * {@link #getAttribute(String)} and they do not otherwise appear in {@link #getAttributeNameSet()} + * or {@link #asAttributeMap()}. */ public interface Attributes { @@ -51,7 +55,10 @@ public interface Attributes Object setAttribute(String name, Object attribute); /** - * Get an attribute + * Get an attribute by name. + * Some attributes may be "hidden" attributes, in that they are only found by an explicit call to + * {@code getAttribute(String)} and they do not otherwise appear in {@link #getAttributeNameSet()} + * or {@link #asAttributeMap()}. * @param name the attribute to get * @return the value of the attribute, or {@code null} if no such attribute exists */ diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiRequest.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiRequest.java index 24b387879cad..38e93d25c1d2 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiRequest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiRequest.java @@ -83,7 +83,7 @@ import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.Session; -import org.eclipse.jetty.session.AbstractSessionManager; +import org.eclipse.jetty.session.AbstractSessionManager.RequestedSession; import org.eclipse.jetty.session.ManagedSession; import org.eclipse.jetty.session.SessionManager; import org.eclipse.jetty.util.Callback; @@ -492,7 +492,7 @@ public Principal getUserPrincipal() @Override public String getRequestedSessionId() { - AbstractSessionManager.RequestedSession requestedSession = getServletRequestInfo().getRequestedSession(); + RequestedSession requestedSession = getServletRequestInfo().getRequestedSession(); return requestedSession == null ? null : requestedSession.sessionId(); } @@ -551,7 +551,7 @@ public String changeSessionId() @Override public boolean isRequestedSessionIdValid() { - AbstractSessionManager.RequestedSession requestedSession = getServletRequestInfo().getRequestedSession(); + RequestedSession requestedSession = getServletRequestInfo().getRequestedSession(); HttpSession session = getSession(false); SessionManager manager = getServletRequestInfo().getSessionManager(); return requestedSession != null && @@ -565,15 +565,15 @@ public boolean isRequestedSessionIdValid() @Override public boolean isRequestedSessionIdFromCookie() { - AbstractSessionManager.RequestedSession requestedSession = getServletRequestInfo().getRequestedSession(); - return requestedSession != null && requestedSession.sessionId() != null && requestedSession.sessionIdFromCookie(); + RequestedSession requestedSession = getServletRequestInfo().getRequestedSession(); + return requestedSession != null && requestedSession.sessionId() != null && requestedSession.isSessionIdFrom(RequestedSession.ID_FROM_COOKIE); } @Override public boolean isRequestedSessionIdFromURL() { - AbstractSessionManager.RequestedSession requestedSession = getServletRequestInfo().getRequestedSession(); - return requestedSession != null && requestedSession.sessionId() != null && !requestedSession.sessionIdFromCookie(); + RequestedSession requestedSession = getServletRequestInfo().getRequestedSession(); + return requestedSession != null && requestedSession.sessionId() != null && requestedSession.isSessionIdFrom(RequestedSession.ID_FROM_URI_PARAMETER); } @Override diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextRequest.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextRequest.java index 481612154127..f1d170e347cd 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextRequest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextRequest.java @@ -330,6 +330,8 @@ public Charset getQueryEncoding() @Override public Object getAttribute(String name) { + if (AbstractSessionManager.RequestedSession.isApplicableAttribute(name)) + return _requestedSession.getAttribute(name); return _attributes.getAttribute(name); } diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/SessionHandler.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/SessionHandler.java index 9ed963d9c85d..788f24f68c3d 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/SessionHandler.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/SessionHandler.java @@ -708,27 +708,33 @@ public boolean handle(Request request, Response response, Callback callback) thr private class NonServletSessionRequest extends Request.Wrapper { private final Response _response; - private RequestedSession _session; + private RequestedSession _requestedSession; public NonServletSessionRequest(Request request, Response response, RequestedSession requestedSession) { super(request); _response = response; - _session = requestedSession; + _requestedSession = requestedSession; + } + + @Override + public Object getAttribute(String name) + { + if (AbstractSessionManager.RequestedSession.isApplicableAttribute(name)) + return _requestedSession.getAttribute(name); + return super.getAttribute(name); } @Override public Session getSession(boolean create) { - ManagedSession session = _session.session(); + ManagedSession session = _requestedSession.session(); if (session != null || !create) return session; - newSession(getWrapped(), _session.sessionId(), ms -> - _session = new RequestedSession(ms, _session.sessionId(), true)); - - session = _session.session(); + newSession(getWrapped(), _requestedSession.sessionId(), ms -> _requestedSession = new RequestedSession(ms, _requestedSession.sessionId(), _requestedSession.sessionIdFrom())); + session = _requestedSession.session(); if (session == null) throw new IllegalStateException("Create session failed"); @@ -740,7 +746,7 @@ public Session getSession(boolean create) ManagedSession getManagedSession() { - return _session.session(); + return _requestedSession.session(); } } } diff --git a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletApiRequest.java b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletApiRequest.java index 770d5fa59000..1981b89a8741 100644 --- a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletApiRequest.java +++ b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletApiRequest.java @@ -83,7 +83,7 @@ import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.Session; -import org.eclipse.jetty.session.AbstractSessionManager; +import org.eclipse.jetty.session.AbstractSessionManager.RequestedSession; import org.eclipse.jetty.session.ManagedSession; import org.eclipse.jetty.session.SessionManager; import org.eclipse.jetty.util.Callback; @@ -492,7 +492,7 @@ public Principal getUserPrincipal() @Override public String getRequestedSessionId() { - AbstractSessionManager.RequestedSession requestedSession = getServletRequestInfo().getRequestedSession(); + RequestedSession requestedSession = getServletRequestInfo().getRequestedSession(); return requestedSession == null ? null : requestedSession.sessionId(); } @@ -551,7 +551,7 @@ public String changeSessionId() @Override public boolean isRequestedSessionIdValid() { - AbstractSessionManager.RequestedSession requestedSession = getServletRequestInfo().getRequestedSession(); + RequestedSession requestedSession = getServletRequestInfo().getRequestedSession(); HttpSession session = getSession(false); SessionManager manager = getServletRequestInfo().getSessionManager(); return requestedSession != null && @@ -565,15 +565,15 @@ public boolean isRequestedSessionIdValid() @Override public boolean isRequestedSessionIdFromCookie() { - AbstractSessionManager.RequestedSession requestedSession = getServletRequestInfo().getRequestedSession(); - return requestedSession != null && requestedSession.sessionId() != null && requestedSession.sessionIdFromCookie(); + RequestedSession requestedSession = getServletRequestInfo().getRequestedSession(); + return requestedSession != null && requestedSession.sessionId() != null && requestedSession.isSessionIdFrom(RequestedSession.ID_FROM_COOKIE); } @Override public boolean isRequestedSessionIdFromURL() { - AbstractSessionManager.RequestedSession requestedSession = getServletRequestInfo().getRequestedSession(); - return requestedSession != null && requestedSession.sessionId() != null && !requestedSession.sessionIdFromCookie(); + RequestedSession requestedSession = getServletRequestInfo().getRequestedSession(); + return requestedSession != null && requestedSession.sessionId() != null && requestedSession.isSessionIdFrom(RequestedSession.ID_FROM_URI_PARAMETER); } @Override diff --git a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletContextRequest.java b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletContextRequest.java index 047d975fb75a..b910fa67e4e3 100644 --- a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletContextRequest.java +++ b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletContextRequest.java @@ -330,6 +330,8 @@ public Charset getQueryEncoding() @Override public Object getAttribute(String name) { + if (AbstractSessionManager.RequestedSession.class.getName().equals(name)) + return _requestedSession; return _attributes.getAttribute(name); } diff --git a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/SessionHandler.java b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/SessionHandler.java index 40b36dcacfaa..9c887e97bc7f 100644 --- a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/SessionHandler.java +++ b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/SessionHandler.java @@ -752,27 +752,33 @@ public boolean handle(Request request, Response response, Callback callback) thr private class NonServletSessionRequest extends Request.Wrapper { private final Response _response; - private RequestedSession _session; + private RequestedSession _requestedSession; public NonServletSessionRequest(Request request, Response response, RequestedSession requestedSession) { super(request); _response = response; - _session = requestedSession; + _requestedSession = requestedSession; + } + + @Override + public Object getAttribute(String name) + { + if (AbstractSessionManager.RequestedSession.isApplicableAttribute(name)) + return _requestedSession.getAttribute(name); + return super.getAttribute(name); } @Override public Session getSession(boolean create) { - ManagedSession session = _session.session(); + ManagedSession session = _requestedSession.session(); if (session != null || !create) return session; - newSession(getWrapped(), _session.sessionId(), ms -> - _session = new RequestedSession(ms, _session.sessionId(), true)); - - session = _session.session(); + newSession(getWrapped(), _requestedSession.sessionId(), ms -> _requestedSession = new RequestedSession(ms, _requestedSession.sessionId(), _requestedSession.sessionIdFrom())); + session = _requestedSession.session(); if (session == null) throw new IllegalStateException("Create session failed"); @@ -784,7 +790,7 @@ public Session getSession(boolean create) ManagedSession getManagedSession() { - return _session.session(); + return _requestedSession.session(); } } } diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ContextHandler.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ContextHandler.java index e01ad6a661b0..dfe73d441128 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ContextHandler.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ContextHandler.java @@ -2466,7 +2466,7 @@ public static class CoreContextRequest extends ContextRequest private ManagedSession _managedSession; private List _managedSessions; - AbstractSessionManager.RequestedSession _requestedSession; + AbstractSessionManager.RequestedSession _requestedSession = AbstractSessionManager.RequestedSession.NO_REQUESTED_SESSION; protected CoreContextRequest(org.eclipse.jetty.server.Request wrapped, ScopedContext context, @@ -2566,7 +2566,15 @@ public SessionManager getSessionManager() */ public void setRequestedSession(AbstractSessionManager.RequestedSession requestedSession) { - _requestedSession = requestedSession; + _requestedSession = requestedSession == null ? AbstractSessionManager.RequestedSession.NO_REQUESTED_SESSION : requestedSession; + } + + @Override + public Object getAttribute(String name) + { + if (AbstractSessionManager.RequestedSession.class.getName().equals(name)) + return _requestedSession; + return super.getAttribute(name); } /** @@ -2653,7 +2661,7 @@ public Session getSession(boolean create) if (_sessionManager == null) throw new IllegalStateException("No SessionManager"); - _sessionManager.newSession(this, _requestedSession == null ? null : _requestedSession.sessionId(), this::setManagedSession); + _sessionManager.newSession(this, _requestedSession.sessionId(), this::setManagedSession); if (_managedSession == null) throw new IllegalStateException("Create session failed"); diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/Request.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/Request.java index dbfd90c7f4aa..cd87e9962795 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/Request.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/Request.java @@ -85,7 +85,7 @@ import org.eclipse.jetty.server.HttpCookieUtils; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Session; -import org.eclipse.jetty.session.AbstractSessionManager; +import org.eclipse.jetty.session.AbstractSessionManager.RequestedSession; import org.eclipse.jetty.session.ManagedSession; import org.eclipse.jetty.session.SessionManager; import org.eclipse.jetty.util.Attributes; @@ -1245,7 +1245,7 @@ public RequestDispatcher getRequestDispatcher(String path) @Override public String getRequestedSessionId() { - AbstractSessionManager.RequestedSession requestedSession = _coreRequest.getRequestedSession(); + RequestedSession requestedSession = _coreRequest.getRequestedSession(); return requestedSession == null ? null : requestedSession.sessionId(); } @@ -1522,8 +1522,7 @@ public boolean isAsyncSupported() @Override public boolean isRequestedSessionIdFromCookie() { - AbstractSessionManager.RequestedSession requestedSession = _coreRequest.getRequestedSession(); - return requestedSession != null && requestedSession.sessionId() != null && requestedSession.sessionIdFromCookie(); + return _coreRequest.getRequestedSession().isSessionIdFrom(RequestedSession.ID_FROM_COOKIE); } @Override @@ -1536,14 +1535,13 @@ public boolean isRequestedSessionIdFromUrl() @Override public boolean isRequestedSessionIdFromURL() { - AbstractSessionManager.RequestedSession requestedSession = _coreRequest.getRequestedSession(); - return requestedSession != null && requestedSession.sessionId() != null && !requestedSession.sessionIdFromCookie(); + return _coreRequest.getRequestedSession().isSessionIdFrom(RequestedSession.ID_FROM_URI_PARAMETER); } @Override public boolean isRequestedSessionIdValid() { - AbstractSessionManager.RequestedSession requestedSession = _coreRequest.getRequestedSession(); + RequestedSession requestedSession = _coreRequest.getRequestedSession(); SessionManager sessionManager = _coreRequest.getSessionManager(); ManagedSession managedSession = _coreRequest.getManagedSession(); return requestedSession != null && diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/SessionHandler.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/SessionHandler.java index aae6b5596b6b..c178d0bc9e0b 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/SessionHandler.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/SessionHandler.java @@ -556,7 +556,7 @@ public void doScope(String target, Request baseRequest, HttpServletRequest reque currentSession = currentRequestedSession.session(); } else - currentRequestedSession = new AbstractSessionManager.RequestedSession(currentSession, currentSession.getId(), false /*TODO!!!*/); + currentRequestedSession = new AbstractSessionManager.RequestedSession(currentSession, currentSession.getId(), null /*TODO!!!*/); coreRequest.setManagedSession(currentSession); coreRequest.setRequestedSession(currentRequestedSession); diff --git a/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/ResponseTest.java b/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/ResponseTest.java index ea48630b44de..927a10d67aa0 100644 --- a/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/ResponseTest.java +++ b/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/ResponseTest.java @@ -67,7 +67,7 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Session; import org.eclipse.jetty.server.TunnelSupport; -import org.eclipse.jetty.session.AbstractSessionManager; +import org.eclipse.jetty.session.AbstractSessionManager.RequestedSession; import org.eclipse.jetty.session.DefaultSessionCache; import org.eclipse.jetty.session.DefaultSessionIdManager; import org.eclipse.jetty.session.ManagedSession; @@ -1613,7 +1613,7 @@ public void testEncodeRedirect() throws Exception ContextHandler.CoreContextRequest coreRequest = response.getHttpChannel().getCoreRequest(); coreRequest.setSessionManager(sessionHandler.getSessionManager()); - coreRequest.setRequestedSession(new AbstractSessionManager.RequestedSession(null, "12345", false)); + coreRequest.setRequestedSession(new RequestedSession(null, "12345", RequestedSession.ID_FROM_URI_PARAMETER)); assertNotNull(request.getSession(true)); assertThat(request.getSession(false).getId(), is("12345")); @@ -1724,7 +1724,7 @@ public void testSendRedirect(String destination, String expected, boolean cookie ContextHandler.CoreContextRequest coreRequest = response.getHttpChannel().getCoreRequest(); coreRequest.setSessionManager(sessionHandler.getSessionManager()); ManagedSession session = sessionHandler.getSessionManager().getManagedSession("12345"); - coreRequest.setRequestedSession(new AbstractSessionManager.RequestedSession(session, "12345", cookie)); + coreRequest.setRequestedSession(new RequestedSession(session, "12345", cookie ? RequestedSession.ID_FROM_COOKIE : RequestedSession.ID_FROM_URI_PARAMETER)); if (session == null) request.getSession(true); @@ -1793,7 +1793,7 @@ public void testSendRedirectRelative() request.setContext(_context._apiContext, "/info"); ContextHandler.CoreContextRequest coreRequest = response.getHttpChannel().getCoreRequest(); - coreRequest.setRequestedSession(new AbstractSessionManager.RequestedSession(null, "12345", i > 2)); + coreRequest.setRequestedSession(new RequestedSession(null, "12345", i > 2 ? RequestedSession.ID_FROM_COOKIE : RequestedSession.ID_FROM_URI_PARAMETER)); SessionHandler handler = new SessionHandler(); NullSessionDataStore dataStore = new NullSessionDataStore(); From 31f73b75433055fab54d05687984e2bfcce200c6 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Fri, 2 Aug 2024 16:21:10 +0200 Subject: [PATCH 4/6] Introduced the migration guide from 12.0.x to 12.1.x. Signed-off-by: Simone Bordet --- .../jetty/modules/programming-guide/nav.adoc | 1 + .../pages/migration/12.0-to-12.1.adoc | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 documentation/jetty/modules/programming-guide/pages/migration/12.0-to-12.1.adoc diff --git a/documentation/jetty/modules/programming-guide/nav.adoc b/documentation/jetty/modules/programming-guide/nav.adoc index ac50c0d7f133..3e843d0bcd22 100644 --- a/documentation/jetty/modules/programming-guide/nav.adoc +++ b/documentation/jetty/modules/programming-guide/nav.adoc @@ -46,3 +46,4 @@ * Migration Guides ** xref:migration/94-to-10.adoc[] ** xref:migration/11-to-12.adoc[] +** xref:migration/12.0-to-12.1.adoc[] diff --git a/documentation/jetty/modules/programming-guide/pages/migration/12.0-to-12.1.adoc b/documentation/jetty/modules/programming-guide/pages/migration/12.0-to-12.1.adoc new file mode 100644 index 000000000000..701a4eedb4c8 --- /dev/null +++ b/documentation/jetty/modules/programming-guide/pages/migration/12.0-to-12.1.adoc @@ -0,0 +1,27 @@ +// +// ======================================================================== +// Copyright (c) 1995 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 +// ======================================================================== +// + += Migrating from Jetty 12.0.x to Jetty 12.1.x + +[[api-changes]] +== APIs Changes + +=== `IteratingCallback` + +Class `IteratingCallback` underwent refinements that changed the behavior of the `onCompleteFailure(Throwable)` method. + +In Jetty 12.0.x, `IteratingCallback.onCompleteFailure(Throwable)` was called as soon as a failure was reported, without waiting the completion of the asynchronous operation (despite its name containing the word "complete"). + +For example, if a write operation performed with `IteratingCallback` was pending due to TCP congestion, and a timeout happened, `onCompleteFailure(Throwable)` was called as soon as the timeout happened, without waiting for the TCP congestion to resolve. + +In Jetty 12.1.x, the same behavior is achieved by `IteratingCallback.onFailure(Throwable)`, so applications should review their usage of `IteratingCallback` and change the overrides of `onCompleteFailure(Throwable)` to override `onFailure(Throwable)` instead. From 942e77c3c5a0831284b919aaa8a0fde461aa898b Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Tue, 20 Aug 2024 11:19:32 +0300 Subject: [PATCH 5/6] Fixes #6514 - How to warm up SslConnection. (#12151) Implemented "priming" of HTTP/1.1 connections using ConnectionPool.preCreateConnections(int) and HttpClientTransportOverHTTP.setInitializeConnections(true). This sends `OPTIONS * HTTP/1.1` to the server. I tried to implement this feature by forcing a write of 0 bytes from the layer above `SslConnection`, but it did not work when using TLS because in both WriteFlusher and SslConnection the fact that there are 0 bytes left to write is treated specially. Other HTTP versions have no problems because they must initialize the connection by e.g. sending a SETTINGS frame, so they would also initialize TLS. Signed-off-by: Simone Bordet --- .../client/http/HTTPClientDocs.java | 25 +++++ .../programming-guide/pages/client/http.adoc | 30 ++++- .../HttpClientConnectionFactory.java | 35 +++++- .../HttpClientTransportOverHTTP.java | 36 +++++- .../internal/HttpConnectionOverHTTP.java | 39 ++++++- .../jetty/client/ConnectionPoolTest.java | 2 +- .../test/client/transport/AbstractTest.java | 7 +- .../client/transport/ConnectionPoolTest.java | 104 ++++++++++++++++++ 8 files changed, 267 insertions(+), 11 deletions(-) create mode 100644 jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/ConnectionPoolTest.java diff --git a/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java b/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java index 031cd030220f..693e23aa4895 100644 --- a/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java +++ b/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java @@ -1049,6 +1049,31 @@ public void setConnectionPool() throws Exception // end::setConnectionPool[] } + public void preCreateConnections() throws Exception + { + // tag::preCreateConnections[] + HttpClient httpClient = new HttpClient(); + httpClient.start(); + + // For HTTP/1.1, you need to explicitly configure to initialize connections. + if (httpClient.getTransport() instanceof HttpClientTransportOverHTTP http1) + http1.setInitializeConnections(true); + + // Create a dummy request to the server you want to pre-create connections to. + Request request = httpClient.newRequest("https://host/"); + + // Resolve the destination for that request. + Destination destination = httpClient.resolveDestination(request); + + // Pre-create, for example, half of the connections. + int preCreate = httpClient.getMaxConnectionsPerDestination() / 2; + CompletableFuture completable = destination.getConnectionPool().preCreateConnections(preCreate); + + // Wait for the connections to be created. + completable.get(5, TimeUnit.SECONDS); + // end::preCreateConnections[] + } + public void unixDomain() throws Exception { // tag::unixDomain[] diff --git a/documentation/jetty/modules/programming-guide/pages/client/http.adoc b/documentation/jetty/modules/programming-guide/pages/client/http.adoc index bb145a15fc9a..38fef9c2f889 100644 --- a/documentation/jetty/modules/programming-guide/pages/client/http.adoc +++ b/documentation/jetty/modules/programming-guide/pages/client/http.adoc @@ -158,7 +158,7 @@ Jetty's client library provides the following `ConnectionPool` implementations: * `DuplexConnectionPool`, historically the first implementation, only used by the HTTP/1.1 transport. * `MultiplexConnectionPool`, the generic implementation valid for any transport where connections are reused with a most recently used algorithm (that is, the connections most recently returned to the connection pool are the more likely to be used again). * `RoundRobinConnectionPool`, similar to `MultiplexConnectionPool` but where connections are reused with a round-robin algorithm. -* `RandomRobinConnectionPool`, similar to `MultiplexConnectionPool` but where connections are reused with an algorithm that chooses them randomly. +* `RandomConnectionPool`, similar to `MultiplexConnectionPool` but where connections are reused with an algorithm that chooses them randomly. The `ConnectionPool` implementation can be customized for each destination in by setting a `ConnectionPool.Factory` on the `HttpClientTransport`: @@ -167,6 +167,34 @@ The `ConnectionPool` implementation can be customized for each destination in by include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java[tags=setConnectionPool] ---- +[[connection-pool-precreate-connections]] +=== Pre-Creating Connections + +`ConnectionPool` offers the ability to pre-create connections by calling `ConnectionPool.preCreateConnections(int)`. + +Pre-creating the connections saves the time and processing spent to establish the TCP connection, performing the TLS handshake (if necessary) and, for HTTP/2 and HTTP/3, perform the initial protocol setup. +This is particularly important for HTTP/2 because in the initial protocol setup the server informs the client of the maximum number of concurrent requests per connection (otherwise assumed to be just `1` by the client). + +The scenarios where pre-creating connections is useful are, for example: + +* Load testing, where you want to prepare the system with connections already created to avoid paying of cost of connection setup. +* Proxying scenarios, often in conjunction with the use of `RoundRobinConnectionPool` or `RandomConnectionPool`, where the proxy creates early the connections to the backend servers. + +This is an example of how to pre-create connections: + +[,java,indent=0] +---- +include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java[tags=preCreateConnections] +---- + +[NOTE] +==== +Pre-creating connections for secure HTTP/1.1 requires you to call `HttpClientTransportOverHTTP.setInitializeConnections(true)`, otherwise only the TCP connection is established, but the TLS handshake is not initiated. + +To initialize connections for secure HTTP/1.1, the client sends an initial `OPTIONS * HTTP/1.1` request to the server. +The server must be able to handle this request without closing the connection (in particular it must not add the `Connection: close` header in the response). +==== + [[request-processing]] == HttpClient Request Processing diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpClientConnectionFactory.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpClientConnectionFactory.java index c5e9f081a857..e33503fe3f49 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpClientConnectionFactory.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpClientConnectionFactory.java @@ -26,20 +26,49 @@ public class HttpClientConnectionFactory implements ClientConnectionFactory /** *

Representation of the {@code HTTP/1.1} application protocol used by {@link HttpClientTransportDynamic}.

*/ - public static final Info HTTP11 = new HTTP11(new HttpClientConnectionFactory()); + public static final Info HTTP11 = new HTTP11(); + + private boolean initializeConnections; + + /** + * @return whether newly created connections should be initialized with an {@code OPTIONS * HTTP/1.1} request + */ + public boolean isInitializeConnections() + { + return initializeConnections; + } + + /** + * @param initialize whether newly created connections should be initialized with an {@code OPTIONS * HTTP/1.1} request + */ + public void setInitializeConnections(boolean initialize) + { + this.initializeConnections = initialize; + } @Override public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map context) { HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, context); + connection.setInitialize(isInitializeConnections()); return customize(connection, context); } - private static class HTTP11 extends Info + /** + *

Representation of the {@code HTTP/1.1} application protocol used by {@link HttpClientTransportDynamic}.

+ *

Applications should prefer using the constant {@link HttpClientConnectionFactory#HTTP11}, unless they + * need to customize the associated {@link HttpClientConnectionFactory}.

+ */ + public static class HTTP11 extends Info { private static final List protocols = List.of("http/1.1"); - private HTTP11(ClientConnectionFactory factory) + public HTTP11() + { + this(new HttpClientConnectionFactory()); + } + + public HTTP11(ClientConnectionFactory factory) { super(factory); } diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpClientTransportOverHTTP.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpClientTransportOverHTTP.java index bab51d75dcaf..2ed1ad5fba6e 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpClientTransportOverHTTP.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpClientTransportOverHTTP.java @@ -22,7 +22,6 @@ import org.eclipse.jetty.client.DuplexConnectionPool; import org.eclipse.jetty.client.Origin; import org.eclipse.jetty.client.Request; -import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.ProcessorUtils; @@ -37,7 +36,7 @@ public class HttpClientTransportOverHTTP extends AbstractConnectorHttpClientTran public static final Origin.Protocol HTTP11 = new Origin.Protocol(List.of("http/1.1"), false); private static final Logger LOG = LoggerFactory.getLogger(HttpClientTransportOverHTTP.class); - private final ClientConnectionFactory factory = new HttpClientConnectionFactory(); + private final HttpClientConnectionFactory factory = new HttpClientConnectionFactory(); private int headerCacheSize = 1024; private boolean headerCacheCaseSensitive; @@ -79,25 +78,54 @@ public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map context) { @@ -159,12 +162,46 @@ public SendFailure send(HttpExchange exchange) return delegate.send(exchange); } + /** + * @return whether to initialize the connection with an {@code OPTIONS * HTTP/1.1} request. + */ + public boolean isInitialize() + { + return initialize; + } + + /** + * @param initialize whether to initialize the connection with an {@code OPTIONS * HTTP/1.1} request. + */ + public void setInitialize(boolean initialize) + { + this.initialize = initialize; + } + @Override public void onOpen() { super.onOpen(); fillInterested(); - promise.succeeded(this); + boolean initialize = isInitialize(); + if (initialize) + { + Destination destination = getHttpDestination(); + Request request = destination.getHttpClient().newRequest(destination.getOrigin().asString()) + .method(HttpMethod.OPTIONS) + .path("*"); + send(request, result -> + { + if (result.isSucceeded()) + promise.succeeded(this); + else + promise.failed(result.getFailure()); + }); + } + else + { + promise.succeeded(this); + } } @Override diff --git a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/ConnectionPoolTest.java b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/ConnectionPoolTest.java index e03b46f293c4..7e62ae170a4c 100644 --- a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/ConnectionPoolTest.java +++ b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/ConnectionPoolTest.java @@ -703,7 +703,7 @@ public void testCountersSweepToStringThroughLifecycle(ConnectionPoolFactory fact assertThat(connectionPool.toString(), not(nullValue())); } - private static class ConnectionPoolFactory + public static class ConnectionPoolFactory { private final String name; private final ConnectionPool.Factory factory; diff --git a/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/AbstractTest.java b/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/AbstractTest.java index b51f938ea72f..8f4932f39fa8 100644 --- a/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/AbstractTest.java +++ b/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/AbstractTest.java @@ -290,6 +290,12 @@ protected SslContextFactory.Server newSslContextFactoryServer() } protected void startClient(Transport transport) throws Exception + { + prepareClient(transport); + client.start(); + } + + protected void prepareClient(Transport transport) throws Exception { QueuedThreadPool clientThreads = new QueuedThreadPool(); clientThreads.setName("client"); @@ -298,7 +304,6 @@ protected void startClient(Transport transport) throws Exception client.setByteBufferPool(clientBufferPool); client.setExecutor(clientThreads); client.setSocketAddressResolver(new SocketAddressResolver.Sync()); - client.start(); } public AbstractConnector newConnector(Transport transport, Server server) diff --git a/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/ConnectionPoolTest.java b/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/ConnectionPoolTest.java new file mode 100644 index 000000000000..a634883faea3 --- /dev/null +++ b/jetty-core/jetty-tests/jetty-test-client-transports/src/test/java/org/eclipse/jetty/test/client/transport/ConnectionPoolTest.java @@ -0,0 +1,104 @@ +// +// ======================================================================== +// Copyright (c) 1995 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.test.client.transport; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.client.Destination; +import org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP; +import org.eclipse.jetty.fcgi.server.internal.ServerFCGIConnection; +import org.eclipse.jetty.http2.server.internal.HTTP2ServerConnection; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.ssl.SslConnection; +import org.eclipse.jetty.quic.server.ServerQuicConnection; +import org.eclipse.jetty.server.internal.HttpConnection; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.not; + +public class ConnectionPoolTest extends AbstractTest +{ + @ParameterizedTest + @MethodSource("transports") + public void testPreCreateConnections(Transport transport) throws Exception + { + prepareServer(transport, new EmptyServerHandler()); + ConnectionListener serverConnections = new ConnectionListener(); + connector.addBean(serverConnections); + server.start(); + + startClient(transport); + client.setMaxConnectionsPerDestination(8); + if (transport == Transport.HTTPS) + ((HttpClientTransportOverHTTP)client.getTransport()).setInitializeConnections(true); + + var request = client.newRequest(newURI(transport)); + Destination destination = client.resolveDestination(request); + destination.getConnectionPool().preCreateConnections(client.getMaxConnectionsPerDestination()) + .get(5, TimeUnit.SECONDS); + + // Verify that connections have been created. + List connections = switch (transport) + { + case HTTP, HTTPS -> serverConnections.filter(HttpConnection.class); + case H2C, H2 -> serverConnections.filter(HTTP2ServerConnection.class); + case H3 -> serverConnections.filter(ServerQuicConnection.class); + case FCGI -> serverConnections.filter(ServerFCGIConnection.class); + }; + assertThat(connections, not(empty())); + + // Verify that TLS was performed. + List sslConnections = switch (transport) + { + case HTTP, H2C, FCGI, H3 -> null; + case HTTPS, H2 -> serverConnections.filter(SslConnection.class); + }; + if (sslConnections != null) + { + assertThat(sslConnections.size(), greaterThan(0)); + sslConnections.forEach(c -> assertThat(c.getBytesIn(), greaterThan(0L))); + sslConnections.forEach(c -> assertThat(c.getBytesOut(), greaterThan(0L))); + } + } + + private static class ConnectionListener implements Connection.Listener + { + private final List connections = new ArrayList<>(); + + @Override + public void onOpened(Connection connection) + { + connections.add(connection); + } + + @Override + public void onClosed(Connection connection) + { + connections.remove(connection); + } + + private List filter(Class klass) + { + return connections.stream() + .filter(klass::isInstance) + .toList(); + } + } +} From 0cae6351f4fe2f4265d4c650a6db73084ac259ca Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Wed, 21 Aug 2024 09:29:59 +1000 Subject: [PATCH 6/6] Issue #12173 Ingore dependencies of type pom for the webapp classpath (#12177) --- .../ee10/maven/plugin/AbstractUnassembledWebAppMojo.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/AbstractUnassembledWebAppMojo.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/AbstractUnassembledWebAppMojo.java index 7e5683fd16e5..986fd2fcb870 100644 --- a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/AbstractUnassembledWebAppMojo.java +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/AbstractUnassembledWebAppMojo.java @@ -242,6 +242,10 @@ private boolean isArtifactOKForWebInfLib(Artifact artifact) if ("war".equalsIgnoreCase(artifact.getType())) return false; + //The dependency cannot be a pom + if ("pom".equalsIgnoreCase(artifact.getType())) + return false; + //The dependency cannot be scope provided (those should be added to the plugin classpath) if (Artifact.SCOPE_PROVIDED.equals(artifact.getScope())) return false;