diff --git a/.github/dependabot.yml b/.github/dependabot.yml index cfc1e89fc51e..97a3c2ebd89c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -24,6 +24,8 @@ updates: versions: [ ">=1.1.0" ] - dependency-name: "org.infinispan:*" versions: [ ">=12" ] + - dependency-name: "org.jboss.weld.servlet:*" + versions: [ ">=4.0.0" ] - package-ecosystem: "maven" directory: "/" diff --git a/.mvn/jvm.config b/.mvn/jvm.config new file mode 100644 index 000000000000..32599cefea51 --- /dev/null +++ b/.mvn/jvm.config @@ -0,0 +1,10 @@ +--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED +--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED +--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED diff --git a/Jenkinsfile b/Jenkinsfile index a8c14a84e1b9..28b4bd5ef3a8 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -12,7 +12,7 @@ pipeline { steps { container('jetty-build') { timeout( time: 120, unit: 'MINUTES' ) { - mavenBuild( "jdk17", "clean install", "maven3") + mavenBuild( "jdk17", "clean install -Perrorprone", "maven3") // Collect up the jacoco execution results (only on main build) jacoco inclusionPattern: '**/org/eclipse/jetty/**/*.class', exclusionPattern: '' + @@ -31,7 +31,7 @@ pipeline { execPattern: '**/target/jacoco.exec', classPattern: '**/target/classes', sourcePattern: '**/src/main/java' - recordIssues id: "jdk17", name: "Static Analysis jdk17", aggregatingResults: true, enabledForFailure: true, tools: [mavenConsole(), java(), checkStyle()] + recordIssues id: "jdk17", name: "Static Analysis jdk17", aggregatingResults: true, enabledForFailure: true, tools: [mavenConsole(), java(), checkStyle(), errorProne(), spotBugs()] } } } @@ -42,8 +42,8 @@ pipeline { steps { container( 'jetty-build' ) { timeout( time: 120, unit: 'MINUTES' ) { - mavenBuild( "jdk11", "clean install -Dspotbugs.skip=true -Djacoco.skip=true -Perrorprone", "maven3") - recordIssues id: "jdk11", name: "Static Analysis jdk11", aggregatingResults: true, enabledForFailure: true, tools: [mavenConsole(), java(), checkStyle(), errorProne()] + mavenBuild( "jdk11", "clean install -Dspotbugs.skip=true -Djacoco.skip=true", "maven3") + recordIssues id: "jdk11", name: "Static Analysis jdk11", aggregatingResults: true, enabledForFailure: true, tools: [mavenConsole(), java(), checkStyle()] } } } diff --git a/javadoc/pom.xml b/javadoc/pom.xml index 878692728cb2..a42680c9b822 100644 --- a/javadoc/pom.xml +++ b/javadoc/pom.xml @@ -384,6 +384,12 @@ org.eclipse.jetty jetty-jspc-maven-plugin provided + + + javax.annotation + javax.annotation-api + + org.eclipse.jetty diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ConnectionPoolTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ConnectionPoolTest.java index ccecb188b969..31838e0eb7a5 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/ConnectionPoolTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ConnectionPoolTest.java @@ -54,6 +54,8 @@ import org.junit.jupiter.params.provider.MethodSource; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -75,11 +77,6 @@ public static Stream pools() return Stream.of(DUPLEX, MULTIPLEX, RANDOM, DUPLEX_MAX_DURATION, ROUND_ROBIN); } - public static Stream poolsNoMaxDuration() - { - return Stream.of(DUPLEX, MULTIPLEX, RANDOM, ROUND_ROBIN); - } - public static Stream poolsNoRoundRobin() { return Stream.of(DUPLEX, MULTIPLEX, RANDOM, DUPLEX_MAX_DURATION); @@ -316,7 +313,10 @@ public void resolve(String host, int port, Promise> prom assertEquals(1, destinations.size()); HttpDestination destination = (HttpDestination)destinations.get(0); AbstractConnectionPool connectionPool = (AbstractConnectionPool)destination.getConnectionPool(); - assertEquals(2, connectionPool.getConnectionCount()); + if (DUPLEX_MAX_DURATION == factory) + assertThat(connectionPool.getConnectionCount(), lessThanOrEqualTo(2)); // The connections can expire upon release. + else + assertThat(connectionPool.getConnectionCount(), is(2)); } @ParameterizedTest @@ -376,8 +376,7 @@ public void resolve(String host, int port, Promise> prom } @ParameterizedTest - // Connection pool aggressively closes expired connections upon release, which interferes with this test's assertion. - @MethodSource("poolsNoMaxDuration") + @MethodSource("pools") public void testConcurrentRequestsAllBlockedOnServerWithLargeConnectionPool(ConnectionPoolFactory factory) throws Exception { int count = 50; @@ -385,8 +384,7 @@ public void testConcurrentRequestsAllBlockedOnServerWithLargeConnectionPool(Conn } @ParameterizedTest - // Connection pool aggressively closes expired connections upon release, which interferes with this test's assertion. - @MethodSource("poolsNoMaxDuration") + @MethodSource("pools") public void testConcurrentRequestsAllBlockedOnServerWithExactConnectionPool(ConnectionPoolFactory factory) throws Exception { int count = 50; @@ -449,9 +447,13 @@ protected void service(String target, org.eclipse.jetty.server.Request jettyRequ assertTrue(latch.await(5, TimeUnit.SECONDS), "server requests " + barrier.getNumberWaiting() + "<" + count + " - client: " + client.dump()); List destinations = client.getDestinations(); assertEquals(1, destinations.size()); - HttpDestination destination = (HttpDestination)destinations.get(0); - AbstractConnectionPool connectionPool = (AbstractConnectionPool)destination.getConnectionPool(); - assertThat(connectionPool.getConnectionCount(), Matchers.greaterThanOrEqualTo(count)); + // The max duration connection pool aggressively closes expired connections upon release, which interferes with this assertion. + if (DUPLEX_MAX_DURATION != factory) + { + HttpDestination destination = (HttpDestination)destinations.get(0); + AbstractConnectionPool connectionPool = (AbstractConnectionPool)destination.getConnectionPool(); + assertThat(connectionPool.getConnectionCount(), Matchers.greaterThanOrEqualTo(count)); + } } @Test @@ -588,8 +590,17 @@ public void testConnectionMaxUsage(ConnectionPoolFactory factory) throws Excepti AbstractConnectionPool connectionPool = (AbstractConnectionPool)destination.getConnectionPool(); assertEquals(0, connectionPool.getActiveConnectionCount()); - assertEquals(1, connectionPool.getIdleConnectionCount()); - assertEquals(1, connectionPool.getConnectionCount()); + if (DUPLEX_MAX_DURATION == factory) + { + // The connections can expire upon release. + assertThat(connectionPool.getIdleConnectionCount(), lessThanOrEqualTo(1)); + assertThat(connectionPool.getConnectionCount(), lessThanOrEqualTo(1)); + } + else + { + assertThat(connectionPool.getIdleConnectionCount(), is(1)); + assertThat(connectionPool.getConnectionCount(), is(1)); + } // Send second request, max usage count will be reached, // the only connection must be closed. @@ -625,7 +636,10 @@ public void testIdleTimeoutNoRequests(ConnectionPoolFactory factory) throws Exce // Trigger the creation of a destination, that will create the connection pool. HttpDestination destination = client.resolveDestination(new Origin("http", "localhost", connector.getLocalPort())); AbstractConnectionPool connectionPool = (AbstractConnectionPool)destination.getConnectionPool(); - assertEquals(1, connectionPool.getConnectionCount()); + if (DUPLEX_MAX_DURATION == factory) + assertThat(connectionPool.getConnectionCount(), lessThanOrEqualTo(1)); // The connections can expire upon release. + else + assertThat(connectionPool.getConnectionCount(), is(1)); // Wait for the pre-created connections to idle timeout. Thread.sleep(idleTimeout + idleTimeout / 2); diff --git a/jetty-gcloud/pom.xml b/jetty-gcloud/pom.xml index dc7479d15a7d..0979e3091de7 100644 --- a/jetty-gcloud/pom.xml +++ b/jetty-gcloud/pom.xml @@ -13,7 +13,7 @@ Jetty :: GCloud - 2.2.4 + 2.2.9 diff --git a/jetty-http3/http3-client/src/main/java/org/eclipse/jetty/http3/client/internal/ClientHTTP3Session.java b/jetty-http3/http3-client/src/main/java/org/eclipse/jetty/http3/client/internal/ClientHTTP3Session.java index be9911a8e80c..9dc44c73d253 100644 --- a/jetty-http3/http3-client/src/main/java/org/eclipse/jetty/http3/client/internal/ClientHTTP3Session.java +++ b/jetty-http3/http3-client/src/main/java/org/eclipse/jetty/http3/client/internal/ClientHTTP3Session.java @@ -92,6 +92,11 @@ public QpackDecoder getQpackDecoder() return decoder; } + public QpackEncoder getQpackEncoder() + { + return encoder; + } + public HTTP3SessionClient getSessionClient() { return session; diff --git a/jetty-http3/http3-client/src/main/java/org/eclipse/jetty/http3/client/internal/HTTP3SessionClient.java b/jetty-http3/http3-client/src/main/java/org/eclipse/jetty/http3/client/internal/HTTP3SessionClient.java index e817564a02d3..92c1f3325d40 100644 --- a/jetty-http3/http3-client/src/main/java/org/eclipse/jetty/http3/client/internal/HTTP3SessionClient.java +++ b/jetty-http3/http3-client/src/main/java/org/eclipse/jetty/http3/client/internal/HTTP3SessionClient.java @@ -146,4 +146,24 @@ protected GoAwayFrame newGoAwayFrame(boolean graceful) return GoAwayFrame.CLIENT_GRACEFUL; return super.newGoAwayFrame(graceful); } + + @Override + protected void onSettingMaxTableCapacity(long value) + { + getProtocolSession().getQpackEncoder().setCapacity((int)value); + } + + @Override + protected void onSettingMaxFieldSectionSize(long value) + { + getProtocolSession().getQpackDecoder().setMaxHeaderSize((int)value); + } + + @Override + protected void onSettingMaxBlockedStreams(long value) + { + ClientHTTP3Session session = getProtocolSession(); + session.getQpackDecoder().setMaxBlockedStreams((int)value); + session.getQpackEncoder().setMaxBlockedStreams((int)value); + } } diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/frames/SettingsFrame.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/frames/SettingsFrame.java index 8e44fbb25313..be8af79c72a4 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/frames/SettingsFrame.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/frames/SettingsFrame.java @@ -17,11 +17,18 @@ public class SettingsFrame extends Frame { + public static final long MAX_TABLE_CAPACITY = 0x01; public static final long MAX_FIELD_SECTION_SIZE = 0x06; + public static final long MAX_BLOCKED_STREAMS = 0x07; public static boolean isReserved(long key) { - return key >= 0 && key <= 5; + if (key == MAX_TABLE_CAPACITY || + key == MAX_FIELD_SECTION_SIZE || + key == MAX_BLOCKED_STREAMS) + return false; + // Other HTTP/2 settings are reserved and must not be sent/received. + return key >= 0x00 && key <= 0x05; } private final Map settings; diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/HTTP3Session.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/HTTP3Session.java index ea5fab8dc145..2ffd225548c0 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/HTTP3Session.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/HTTP3Session.java @@ -371,9 +371,32 @@ public void onSettings(SettingsFrame frame) { if (LOG.isDebugEnabled()) LOG.debug("received {} on {}", frame, this); + + frame.getSettings().forEach((key, value) -> + { + if (key == SettingsFrame.MAX_TABLE_CAPACITY) + onSettingMaxTableCapacity(value); + else if (key == SettingsFrame.MAX_FIELD_SECTION_SIZE) + onSettingMaxFieldSectionSize(value); + else if (key == SettingsFrame.MAX_BLOCKED_STREAMS) + onSettingMaxBlockedStreams(value); + }); + notifySettings(frame); } + protected void onSettingMaxTableCapacity(long value) + { + } + + protected void onSettingMaxFieldSectionSize(long value) + { + } + + protected void onSettingMaxBlockedStreams(long value) + { + } + private void notifySettings(SettingsFrame frame) { try diff --git a/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackDecoder.java b/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackDecoder.java index 31279e3ef48b..4129979ca68b 100644 --- a/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackDecoder.java +++ b/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackDecoder.java @@ -50,7 +50,8 @@ public class QpackDecoder implements Dumpable private final List _encodedFieldSections = new ArrayList<>(); private final NBitIntegerParser _integerDecoder = new NBitIntegerParser(); private final InstructionHandler _instructionHandler = new InstructionHandler(); - private final int _maxHeaderSize; + private int _maxHeaderSize; + private int _maxBlockedStreams; private static class MetaDataNotification { @@ -87,6 +88,27 @@ QpackContext getQpackContext() return _context; } + public int getMaxHeaderSize() + { + return _maxHeaderSize; + } + + public void setMaxHeaderSize(int maxHeaderSize) + { + _maxHeaderSize = maxHeaderSize; + } + + public int getMaxBlockedStreams() + { + // TODO: implement logic about blocked streams by calling this method. + return _maxBlockedStreams; + } + + public void setMaxBlockedStreams(int maxBlockedStreams) + { + _maxBlockedStreams = maxBlockedStreams; + } + public interface Handler { void onMetaData(long streamId, MetaData metadata); @@ -110,7 +132,8 @@ public boolean decode(long streamId, ByteBuffer buffer, Handler handler) throws LOG.debug("Decoding: streamId={}, buffer={}", streamId, BufferUtil.toDetailString(buffer)); // If the buffer is big, don't even think about decoding it - if (buffer.remaining() > _maxHeaderSize) + int maxHeaderSize = getMaxHeaderSize(); + if (buffer.remaining() > maxHeaderSize) throw new QpackException.SessionException(QPACK_DECOMPRESSION_FAILED, "header_too_large"); _integerDecoder.setPrefix(8); @@ -139,7 +162,7 @@ public boolean decode(long streamId, ByteBuffer buffer, Handler handler) throws // Decode it straight away if we can, otherwise add it to the list of EncodedFieldSections. if (requiredInsertCount <= insertCount) { - MetaData metaData = encodedFieldSection.decode(_context, _maxHeaderSize); + MetaData metaData = encodedFieldSection.decode(_context, maxHeaderSize); if (LOG.isDebugEnabled()) LOG.debug("Decoded: streamId={}, metadata={}", streamId, metaData); _metaDataNotifications.add(new MetaDataNotification(streamId, metaData, handler)); diff --git a/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackEncoder.java b/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackEncoder.java index ae7ccd24d285..7105b5257bf9 100644 --- a/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackEncoder.java +++ b/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackEncoder.java @@ -91,7 +91,7 @@ public class QpackEncoder implements Dumpable private final List _instructions = new ArrayList<>(); private final Instruction.Handler _handler; private final QpackContext _context; - private final int _maxBlockedStreams; + private int _maxBlockedStreams; private final Map _streamInfoMap = new HashMap<>(); private final EncoderInstructionParser _parser; private final InstructionHandler _instructionHandler = new InstructionHandler(); @@ -106,6 +106,21 @@ public QpackEncoder(Instruction.Handler handler, int maxBlockedStreams) _parser = new EncoderInstructionParser(_instructionHandler); } + public int getMaxBlockedStreams() + { + return _maxBlockedStreams; + } + + public void setMaxBlockedStreams(int maxBlockedStreams) + { + _maxBlockedStreams = maxBlockedStreams; + } + + public int getCapacity() + { + return _context.getDynamicTable().getCapacity(); + } + /** * Set the capacity of the DynamicTable and send a instruction to set the capacity on the remote Decoder. * @@ -411,7 +426,7 @@ private boolean referenceEntry(Entry entry, StreamInfo streamInfo) return true; } - if (_blockedStreams < _maxBlockedStreams) + if (_blockedStreams < getMaxBlockedStreams()) { _blockedStreams++; sectionInfo.block(); diff --git a/jetty-http3/http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HTTP3SessionServer.java b/jetty-http3/http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HTTP3SessionServer.java index 504688b3648a..02af205db982 100644 --- a/jetty-http3/http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HTTP3SessionServer.java +++ b/jetty-http3/http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HTTP3SessionServer.java @@ -95,6 +95,26 @@ protected GoAwayFrame newGoAwayFrame(boolean graceful) return super.newGoAwayFrame(graceful); } + @Override + protected void onSettingMaxTableCapacity(long value) + { + getProtocolSession().getQpackEncoder().setCapacity((int)value); + } + + @Override + protected void onSettingMaxFieldSectionSize(long value) + { + getProtocolSession().getQpackDecoder().setMaxHeaderSize((int)value); + } + + @Override + protected void onSettingMaxBlockedStreams(long value) + { + ServerHTTP3Session session = getProtocolSession(); + session.getQpackDecoder().setMaxBlockedStreams((int)value); + session.getQpackEncoder().setMaxBlockedStreams((int)value); + } + private void notifyAccept() { Server.Listener listener = getListener(); diff --git a/jetty-http3/http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3Session.java b/jetty-http3/http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3Session.java index ac52b5a42802..c62103d28d59 100644 --- a/jetty-http3/http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3Session.java +++ b/jetty-http3/http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3Session.java @@ -91,6 +91,11 @@ public QpackDecoder getQpackDecoder() return decoder; } + public QpackEncoder getQpackEncoder() + { + return encoder; + } + public HTTP3SessionServer getSessionServer() { return session; diff --git a/jetty-http3/http3-tests/src/test/java/org/eclipse/jetty/http3/tests/ClientServerTest.java b/jetty-http3/http3-tests/src/test/java/org/eclipse/jetty/http3/tests/ClientServerTest.java index b5b7c3c42647..e6a9b7bf687c 100644 --- a/jetty-http3/http3-tests/src/test/java/org/eclipse/jetty/http3/tests/ClientServerTest.java +++ b/jetty-http3/http3-tests/src/test/java/org/eclipse/jetty/http3/tests/ClientServerTest.java @@ -14,6 +14,7 @@ package org.eclipse.jetty.http3.tests; import java.nio.ByteBuffer; +import java.util.AbstractMap; import java.util.Map; import java.util.Random; import java.util.concurrent.CountDownLatch; @@ -34,6 +35,7 @@ import org.eclipse.jetty.http3.internal.HTTP3ErrorCode; import org.eclipse.jetty.http3.internal.HTTP3Session; import org.eclipse.jetty.http3.server.AbstractHTTP3ServerConnectionFactory; +import org.eclipse.jetty.http3.server.internal.HTTP3SessionServer; import org.eclipse.jetty.quic.client.ClientQuicSession; import org.eclipse.jetty.quic.common.QuicSession; import org.junit.jupiter.api.Test; @@ -41,6 +43,7 @@ import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -90,6 +93,59 @@ public void onSettings(Session session, SettingsFrame frame) assertTrue(clientSettingsLatch.await(5, TimeUnit.SECONDS)); } + @Test + public void testSettings() throws Exception + { + Map.Entry maxTableCapacity = new AbstractMap.SimpleEntry<>(SettingsFrame.MAX_TABLE_CAPACITY, 1024L); + Map.Entry maxHeaderSize = new AbstractMap.SimpleEntry<>(SettingsFrame.MAX_FIELD_SECTION_SIZE, 2048L); + Map.Entry maxBlockedStreams = new AbstractMap.SimpleEntry<>(SettingsFrame.MAX_BLOCKED_STREAMS, 16L); + CountDownLatch settingsLatch = new CountDownLatch(2); + AtomicReference serverSessionRef = new AtomicReference<>(); + start(new Session.Server.Listener() + { + @Override + public Map onPreface(Session session) + { + serverSessionRef.set((HTTP3SessionServer)session); + return Map.ofEntries(maxTableCapacity, maxHeaderSize, maxBlockedStreams); + } + + @Override + public void onSettings(Session session, SettingsFrame frame) + { + settingsLatch.countDown(); + } + }); + + HTTP3SessionClient clientSession = (HTTP3SessionClient)newSession(new Session.Client.Listener() + { + @Override + public Map onPreface(Session session) + { + return Map.ofEntries(maxTableCapacity, maxHeaderSize, maxBlockedStreams); + } + + @Override + public void onSettings(Session session, SettingsFrame frame) + { + settingsLatch.countDown(); + } + }); + + assertTrue(settingsLatch.await(5, TimeUnit.SECONDS)); + + HTTP3SessionServer serverSession = serverSessionRef.get(); + assertEquals(maxTableCapacity.getValue(), serverSession.getProtocolSession().getQpackEncoder().getCapacity()); + assertEquals(maxBlockedStreams.getValue(), serverSession.getProtocolSession().getQpackEncoder().getMaxBlockedStreams()); + assertEquals(maxBlockedStreams.getValue(), serverSession.getProtocolSession().getQpackDecoder().getMaxBlockedStreams()); + assertEquals(maxHeaderSize.getValue(), serverSession.getProtocolSession().getQpackDecoder().getMaxHeaderSize()); + + assertEquals(maxTableCapacity.getValue(), clientSession.getProtocolSession().getQpackEncoder().getCapacity()); + assertEquals(maxBlockedStreams.getValue(), clientSession.getProtocolSession().getQpackEncoder().getMaxBlockedStreams()); + assertEquals(maxBlockedStreams.getValue(), clientSession.getProtocolSession().getQpackDecoder().getMaxBlockedStreams()); + assertEquals(maxHeaderSize.getValue(), clientSession.getProtocolSession().getQpackDecoder().getMaxHeaderSize()); + } + @Test public void testGETThenResponseWithoutContent() throws Exception { diff --git a/jetty-http3/http3-tests/src/test/java/org/eclipse/jetty/http3/tests/ExternalServerTest.java b/jetty-http3/http3-tests/src/test/java/org/eclipse/jetty/http3/tests/ExternalServerTest.java index d5fcf90d9244..42737e2bf3f1 100644 --- a/jetty-http3/http3-tests/src/test/java/org/eclipse/jetty/http3/tests/ExternalServerTest.java +++ b/jetty-http3/http3-tests/src/test/java/org/eclipse/jetty/http3/tests/ExternalServerTest.java @@ -44,8 +44,9 @@ public void testExternalServer() throws Exception client.start(); try { + HostPort hostPort = new HostPort("google.com:443"); // HostPort hostPort = new HostPort("nghttp2.org:4433"); - HostPort hostPort = new HostPort("quic.tech:8443"); +// HostPort hostPort = new HostPort("quic.tech:8443"); // HostPort hostPort = new HostPort("h2o.examp1e.net:443"); // HostPort hostPort = new HostPort("test.privateoctopus.com:4433"); Session.Client session = client.connect(new InetSocketAddress(hostPort.getHost(), hostPort.getPort()), new Session.Client.Listener() {}) diff --git a/jetty-jspc-maven-plugin/pom.xml b/jetty-jspc-maven-plugin/pom.xml index d5865a03889d..b436db8b597a 100644 --- a/jetty-jspc-maven-plugin/pom.xml +++ b/jetty-jspc-maven-plugin/pom.xml @@ -15,13 +15,6 @@ - - org.apache.maven.plugins - maven-surefire-plugin - - true - - org.apache.maven.plugins maven-plugin-plugin @@ -69,6 +62,7 @@ org.apache.maven maven-plugin-api + provided javax.annotation @@ -81,9 +75,25 @@ maven-plugin-annotations provided + + org.apache.maven + maven-core + provided + + + org.apache.maven + maven-model + provided + + + org.apache.maven + maven-settings + provided + org.apache.maven maven-artifact + provided org.apache.maven.plugin-tools @@ -110,53 +120,4 @@ ant - - - - org.apache.maven.plugins - maven-project-info-reports-plugin - - false - - - - - team - mailing-lists - ci-management - issue-management - licenses - scm - - - - - - org.apache.maven.plugins - maven-plugin-plugin - - - - - - eclipse-release - - - - org.apache.maven.plugins - maven-site-plugin - - - site-jar - - jar - - package - - - - - - - diff --git a/jetty-maven-plugin/pom.xml b/jetty-maven-plugin/pom.xml index 5f78ceb39b17..ea7da834a71c 100644 --- a/jetty-maven-plugin/pom.xml +++ b/jetty-maven-plugin/pom.xml @@ -122,6 +122,7 @@ org.apache.maven maven-plugin-api + provided javax.annotation @@ -129,13 +130,25 @@ + + org.apache.maven + maven-model + provided + + + org.apache.maven + maven-settings + provided + org.apache.maven maven-artifact + provided org.apache.maven maven-core + provided org.apache.maven.plugin-tools @@ -354,50 +367,4 @@ test - - - - org.apache.maven.plugins - maven-project-info-reports-plugin - - - - team - mailing-lists - ci-management - issue-management - licenses - scm - - - - - - org.apache.maven.plugins - maven-plugin-plugin - - - - - - eclipse-release - - - - org.apache.maven.plugins - maven-site-plugin - - - site-jar - - jar - - package - - - - - - - diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyStopMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyStopMojo.java index e471f03ccd0d..4d329e0c8952 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyStopMojo.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyStopMojo.java @@ -19,13 +19,16 @@ import java.net.ConnectException; import java.net.InetAddress; import java.net.Socket; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; - /** * This goal stops a running instance of jetty. * @@ -72,38 +75,157 @@ public void execute() throws MojoExecutionException, MojoFailureException String command = "forcestop"; - try (Socket s = new Socket(InetAddress.getByName("127.0.0.1"), stopPort);) + if (stopWait > 0) { - OutputStream out = s.getOutputStream(); - out.write((stopKey + "\r\n" + command + "\r\n").getBytes()); - out.flush(); - - if (stopWait > 0) + //try to get the pid of the forked jetty process + Long pid = null; + try + { + String response = send(stopKey + "\r\n" + "pid" + "\r\n", stopWait); + pid = Long.valueOf(response); + } + catch (NumberFormatException e) + { + getLog().info("Server returned bad pid"); + } + catch (ConnectException e) { - s.setSoTimeout(stopWait * 1000); - s.getInputStream(); + //jetty not running, no point continuing + getLog().info("Jetty not running!"); + return; + } + catch (Exception e) + { + //jetty running, try to stop it regardless of error + getLog().error(e); + } - getLog().info("Waiting " + stopWait + " seconds for jetty to stop"); - LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream())); - String response; - boolean stopped = false; - while (!stopped && ((response = lin.readLine()) != null)) + //now send the stop command and wait for confirmation - either an ack from jetty, or + //that the process has stopped + if (pid == null) + { + //no pid, so just wait until jetty reports itself stopped + try { + getLog().info("Waiting " + stopWait + " seconds for jetty to stop"); + String response = send(stopKey + "\r\n" + command + "\r\n", stopWait); + if ("Stopped".equals(response)) - { - stopped = true; getLog().info("Server reports itself as stopped"); - } + else + getLog().info("Couldn't verify server as stopped, received " + response); + } + catch (ConnectException e) + { + getLog().info("Jetty not running!"); } + catch (Exception e) + { + getLog().error(e); + } + } + else + { + //wait for pid to stop + getLog().info("Waiting " + stopWait + " seconds for jetty " + pid + " to stop"); + Optional optional = ProcessHandle.of(pid); + final long remotePid = pid.longValue(); + optional.ifPresentOrElse(p -> + { + try + { + //if running in the same process, just send the stop + //command and wait for the response + if (ProcessHandle.current().pid() == remotePid) + { + send(stopKey + "\r\n" + command + "\r\n", stopWait); + } + else + { + //running forked, so wait for the process + send(stopKey + "\r\n" + command + "\r\n", 0); + CompletableFuture future = p.onExit(); + if (p.isAlive()) + { + p = future.get(stopWait, TimeUnit.SECONDS); + } + + if (p.isAlive()) + getLog().info("Couldn't verify server process stop"); + else + getLog().info("Server process stopped"); + } + } + catch (ConnectException e) + { + //jetty not listening on the given port, don't wait for the process + getLog().info("Jetty not running!"); + } + catch (TimeoutException e) + { + getLog().error("Timeout expired while waiting for server process to stop"); + } + catch (Throwable e) + { + getLog().error(e); + } + }, () -> getLog().info("Process not running")); } } - catch (ConnectException e) + else { - getLog().info("Jetty not running!"); + //send the stop command but don't wait to verify the stop + getLog().info("Stopping jetty"); + try + { + send(stopKey + "\r\n" + command + "\r\n", 0); + } + catch (ConnectException e) + { + getLog().info("Jetty not running!"); + } + catch (Exception e) + { + getLog().error(e); + } } - catch (Exception e) + } + + /** + * Send a command to a jetty process, optionally waiting for a response. + * + * @param command the command to send + * @param wait length of time in sec to wait for a response + * @return the response, if any, to the command + * @throws Exception + */ + private String send(String command, int wait) + throws Exception + { + String response = null; + try (Socket s = new Socket(InetAddress.getByName("127.0.0.1"), stopPort); OutputStream out = s.getOutputStream();) { - getLog().error(e); + out.write(command.getBytes()); + out.flush(); + + if (wait > 0) + { + //Wait for a response + s.setSoTimeout(wait * 1000); + + try (LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream()));) + { + response = lin.readLine(); + } + } + else + { + //Wait only a small amount of time to ensure TCP has sent the message + s.setSoTimeout(1000); + s.getInputStream().read(); + } + + return response; } } } diff --git a/jetty-maven-plugin/src/test/java/org/eclipse/jetty/maven/plugin/MockShutdownMonitor.java b/jetty-maven-plugin/src/test/java/org/eclipse/jetty/maven/plugin/MockShutdownMonitor.java new file mode 100644 index 000000000000..e7f451934844 --- /dev/null +++ b/jetty-maven-plugin/src/test/java/org/eclipse/jetty/maven/plugin/MockShutdownMonitor.java @@ -0,0 +1,74 @@ +// +// ======================================================================== +// 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.maven.plugin; + +import java.net.ServerSocket; + +import org.eclipse.jetty.toolchain.test.IO; + +/** + * MockShutdownMonitor + * A helper class that grabs a ServerSocket, spawns a thread and then + * passes the ServerSocket to the Runnable. This class has a main so + * that it can be used for forking, to mimic the actions of the + * org.eclipse.jetty.server.ShutdownMonitor. + */ +public class MockShutdownMonitor +{ + String key; + MockShutdownMonitorRunnable testerRunnable; + ServerSocket serverSocket; + + public MockShutdownMonitor(String key, MockShutdownMonitorRunnable testerRunnable) + throws Exception + { + this.key = key; + this.testerRunnable = testerRunnable; + listen(); + } + + private ServerSocket listen() + throws Exception + { + serverSocket = new ServerSocket(0); + try + { + serverSocket.setReuseAddress(true); + return serverSocket; + } + catch (Throwable e) + { + IO.close(serverSocket); + throw e; + } + } + + public int getPort() + { + if (serverSocket == null) + return 0; + return serverSocket.getLocalPort(); + } + + public void start() + throws Exception + { + testerRunnable.setServerSocket(serverSocket); + testerRunnable.setKey(key); + Thread thread = new Thread(testerRunnable); + thread.setDaemon(true); + thread.setName("Tester Thread"); + thread.start(); + } +} diff --git a/jetty-maven-plugin/src/test/java/org/eclipse/jetty/maven/plugin/MockShutdownMonitorRunnable.java b/jetty-maven-plugin/src/test/java/org/eclipse/jetty/maven/plugin/MockShutdownMonitorRunnable.java new file mode 100644 index 000000000000..bfdb18baf167 --- /dev/null +++ b/jetty-maven-plugin/src/test/java/org/eclipse/jetty/maven/plugin/MockShutdownMonitorRunnable.java @@ -0,0 +1,111 @@ +// +// ======================================================================== +// 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.maven.plugin; + +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.charset.StandardCharsets; + +import org.eclipse.jetty.toolchain.test.IO; + +/** + * MockShutdownMonitorRunnable + * + * Mimics the actions of the org.eclipse.jetty.server.ShutdownMonitor.ShutdownMonitorRunnable + * to aid testing. + */ +public class MockShutdownMonitorRunnable implements Runnable +{ + ServerSocket serverSocket; + String key; + String statusResponse = "OK"; + String pidResponse; + String defaultResponse = "Stopped"; + boolean exit; + + public void setExit(boolean exit) + { + this.exit = exit; + } + + public void setKey(String key) + { + this.key = key; + } + + public void setServerSocket(ServerSocket serverSocket) + { + this.serverSocket = serverSocket; + } + + public void setPidResponse(String pidResponse) + { + this.pidResponse = pidResponse; + } + + public void run() + { + try + { + while (true) + { + try (Socket socket = serverSocket.accept()) + { + LineNumberReader reader = new LineNumberReader(new InputStreamReader(socket.getInputStream())); + String receivedKey = reader.readLine(); + if (!key.equals(receivedKey)) + { + continue; + } + + String cmd = reader.readLine(); + OutputStream out = socket.getOutputStream(); + + if ("status".equalsIgnoreCase(cmd)) + { + out.write((statusResponse + "\r\n").getBytes(StandardCharsets.UTF_8)); + out.flush(); + } + else if ("pid".equalsIgnoreCase(cmd)) + { + out.write((pidResponse + "\r\n").getBytes(StandardCharsets.UTF_8)); + out.flush(); + } + else + { + out.write((defaultResponse + "\r\n").getBytes(StandardCharsets.UTF_8)); + out.flush(); + if (exit) + System.exit(0); + } + } + catch (Throwable x) + { + x.printStackTrace(); + } + } + } + catch (Throwable x) + { + x.printStackTrace(); + } + finally + { + IO.close(serverSocket); + } + } +} diff --git a/jetty-maven-plugin/src/test/java/org/eclipse/jetty/maven/plugin/TestJettyStopMojo.java b/jetty-maven-plugin/src/test/java/org/eclipse/jetty/maven/plugin/TestJettyStopMojo.java new file mode 100644 index 000000000000..39fd9882c34f --- /dev/null +++ b/jetty-maven-plugin/src/test/java/org/eclipse/jetty/maven/plugin/TestJettyStopMojo.java @@ -0,0 +1,304 @@ +// +// ======================================================================== +// 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.maven.plugin; + +import java.io.File; +import java.io.FileReader; +import java.io.LineNumberReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jetty.maven.plugin.AbstractWebAppMojo.DeploymentMode; +import org.eclipse.jetty.server.ShutdownMonitor; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class TestJettyStopMojo +{ + /** + * ShutdownMonitorMain + * Kick off the ShutdownMonitor and wait for it to exit. + */ + public static final class ShutdownMonitorMain + { + public static void main(String[] args) + { + try + { + ShutdownMonitor monitor = ShutdownMonitor.getInstance(); + monitor.setPort(0); + monitor.start(); + monitor.await(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + } + + public static class TestLog implements org.apache.maven.plugin.logging.Log + { + List sink = new ArrayList<>(); + + @Override + public boolean isDebugEnabled() + { + return true; + } + + @Override + public void debug(CharSequence content) + { + sink.add(content.toString()); + } + + @Override + public void debug(CharSequence content, Throwable error) + { + sink.add(content.toString()); + } + + @Override + public void debug(Throwable error) + { + } + + @Override + public boolean isInfoEnabled() + { + return true; + } + + @Override + public void info(CharSequence content) + { + sink.add(content.toString()); + } + + @Override + public void info(CharSequence content, Throwable error) + { + sink.add(content.toString()); + } + + @Override + public void info(Throwable error) + { + } + + @Override + public boolean isWarnEnabled() + { + return true; + } + + @Override + public void warn(CharSequence content) + { + sink.add(content.toString()); + } + + @Override + public void warn(CharSequence content, Throwable error) + { + sink.add(content.toString()); + } + + @Override + public void warn(Throwable error) + { + } + + @Override + public boolean isErrorEnabled() + { + return true; + } + + @Override + public void error(CharSequence content) + { + sink.add(content.toString()); + } + + @Override + public void error(CharSequence content, Throwable error) + { + sink.add(content.toString()); + } + + @Override + public void error(Throwable error) + { + } + + public void assertContains(String str) + { + assertThat(sink, Matchers.hasItem(str)); + } + + public void dumpStdErr() + { + for (String s : sink) + { + System.err.println(s); + } + } + } + + @Test + public void testStopNoWait() throws Exception + { + //send a stop message and don't wait for the reply or the process to shutdown + String stopKey = "foo"; + MockShutdownMonitorRunnable runnable = new MockShutdownMonitorRunnable(); + runnable.setPidResponse("abcd"); + MockShutdownMonitor monitor = new MockShutdownMonitor(stopKey, runnable); + monitor.start(); + + TestLog log = new TestLog(); + JettyStopMojo mojo = new JettyStopMojo(); + mojo.stopKey = stopKey; + mojo.stopPort = monitor.getPort(); + mojo.setLog(log); + + mojo.execute(); + + log.assertContains("Stopping jetty"); + } + + @Test + public void testStopWaitBadPid() throws Exception + { + //test that even if we receive a bad pid, we still send the stop command and wait to + //receive acknowledgement, but we don't wait for the process to exit + String stopKey = "foo"; + MockShutdownMonitorRunnable runnable = new MockShutdownMonitorRunnable(); + runnable.setPidResponse("abcd"); + MockShutdownMonitor monitor = new MockShutdownMonitor(stopKey, runnable); + monitor.start(); + + TestLog log = new TestLog(); + JettyStopMojo mojo = new JettyStopMojo(); + mojo.stopWait = 5; + mojo.stopKey = stopKey; + mojo.stopPort = monitor.getPort(); + mojo.setLog(log); + + mojo.execute(); + + log.assertContains("Server returned bad pid"); + log.assertContains("Server reports itself as stopped"); + } + + @Test + public void testStopSameProcess() throws Exception + { + //test that if we need to stop a jetty in the same process as us + //we will wait for it to exit + String stopKey = "foo"; + long thisPid = ProcessHandle.current().pid(); + MockShutdownMonitorRunnable runnable = new MockShutdownMonitorRunnable(); + runnable.setPidResponse(Long.toString(thisPid)); + MockShutdownMonitor monitor = new MockShutdownMonitor(stopKey, runnable); + monitor.start(); + + TestLog log = new TestLog(); + JettyStopMojo mojo = new JettyStopMojo(); + mojo.stopWait = 5; + mojo.stopKey = stopKey; + mojo.stopPort = monitor.getPort(); + mojo.setLog(log); + + mojo.execute(); + + log.assertContains("Waiting 5 seconds for jetty " + Long.toString(thisPid) + " to stop"); + } + + @Test + public void testStopWait() throws Exception + { + //test that we will communicate with a remote process and wait for it to exit + String stopKey = "foo"; + List cmd = new ArrayList<>(); + String java = "java"; + String[] javaexes = new String[]{"java", "java.exe"}; + File javaHomeDir = new File(System.getProperty("java.home")); + Path javaHomePath = javaHomeDir.toPath(); + for (String javaexe : javaexes) + { + Path javaBinPath = javaHomePath.resolve(Paths.get("bin", javaexe)); + if (Files.exists(javaBinPath) && !Files.isDirectory(javaBinPath)) + java = javaBinPath.toFile().getAbsolutePath(); + } + + cmd.add(java); + cmd.add("-DSTOP.KEY=" + stopKey); + cmd.add("-DDEBUG=true"); + cmd.add("-cp"); + cmd.add(System.getProperty("java.class.path")); + cmd.add(ShutdownMonitorMain.class.getName()); + + ProcessBuilder command = new ProcessBuilder(cmd); + File file = MavenTestingUtils.getTargetFile("tester.out"); + command.redirectOutput(file); + command.redirectErrorStream(true); + command.directory(MavenTestingUtils.getTargetDir()); + Process fork = command.start(); + + Thread.sleep(500); + while (!file.exists() && file.length() == 0) + { + Thread.sleep(300); + } + + String tmp = ""; + String port = null; + try (LineNumberReader reader = new LineNumberReader(new FileReader(file))) + { + while (port == null && tmp != null) + { + tmp = reader.readLine(); + if (tmp != null) + { + if (tmp.startsWith("STOP.PORT=")) + port = tmp.substring(10); + } + } + } + + assertNotNull(port); + + TestLog log = new TestLog(); + JettyStopMojo mojo = new JettyStopMojo(); + mojo.stopWait = 5; + mojo.stopKey = stopKey; + mojo.stopPort = Integer.parseInt(port); + mojo.setLog(log); + + mojo.execute(); + + log.dumpStdErr(); + log.assertContains("Waiting " + mojo.stopWait + " seconds for jetty " + fork.pid() + " to stop"); + log.assertContains("Server process stopped"); + } +} diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java index cbce1bb634bd..8ab1255c3fba 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java @@ -24,6 +24,7 @@ import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.util.ajax.JSON; +import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.slf4j.Logger; @@ -86,8 +87,13 @@ public OpenIdConfiguration(String issuer, String authorizationEndpoint, String t * @param authMethod Authentication method to use with the Token Endpoint. * @param httpClient The {@link HttpClient} instance to use. */ - public OpenIdConfiguration(String issuer, String authorizationEndpoint, String tokenEndpoint, - String clientId, String clientSecret, String authMethod, HttpClient httpClient) + public OpenIdConfiguration(@Name("issuer") String issuer, + @Name("authorizationEndpoint") String authorizationEndpoint, + @Name("tokenEndpoint") String tokenEndpoint, + @Name("clientId") String clientId, + @Name("clientSecret") String clientSecret, + @Name("authMethod") String authMethod, + @Name("httpClient") HttpClient httpClient) { this.issuer = issuer; this.clientId = clientId; diff --git a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml index 370daec91d3a..2d00c5fc9812 100644 --- a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml +++ b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml @@ -44,6 +44,25 @@ + + + org.codehaus.mojo + build-helper-maven-plugin + + + set-jsp-api-version + validate + + parse-version + + + ${jsp.impl.version} + jspImpl + + + + + org.apache.felix maven-bundle-plugin @@ -73,20 +92,20 @@ javax.servlet.jsp.jstl.fmt;version="1.2";resolution:=optional, javax.servlet.jsp.jstl.sql;version="1.2";resolution:=optional, javax.servlet.jsp.jstl.tlv;version="1.2";resolution:=optional, - org.apache.el;version="[$(version;===;${jsp.impl.version}),$(version;+;${jsp.impl.version}))";resolution:=optional, - org.apache.el.lang;version="[$(version;===;${jsp.impl.version}),$(version;+;${jsp.impl.version}))";resolution:=optional, - org.apache.el.stream;version="[$(version;===;${jsp.impl.version}),$(version;+;${jsp.impl.version}))";resolution:=optional, - org.apache.el.util;version="[$(version;===;${jsp.impl.version}),$(version;+;${jsp.impl.version}))";resolution:=optional, - org.apache.el.parser;version="[$(version;===;${jsp.impl.version}),$(version;+;${jsp.impl.version}))";resolution:=optional, - org.apache.jasper;version="[$(version;===;${jsp.impl.version}),$(version;+;${jsp.impl.version}))";resolution:=optional, - org.apache.jasper.compiler;version="[$(version;===;${jsp.impl.version}),$(version;+;${jsp.impl.version}))";resolution:=optional, - org.apache.jasper.compiler.tagplugin;version="[$(version;===;${jsp.impl.version}),$(version;+;${jsp.impl.version}))";resolution:=optional, - org.apache.jasper.runtime;version="[$(version;===;${jsp.impl.version}),$(version;+;${jsp.impl.version}))";resolution:=optional, - org.apache.jasper.security;version="[$(version;===;${jsp.impl.version}),$(version;+;${jsp.impl.version}))";resolution:=optional, - org.apache.jasper.servlet;version="[$(version;===;${jsp.impl.version}),$(version;+;${jsp.impl.version}))";resolution:=optional, - org.apache.jasper.tagplugins.jstl;version="[$(version;===;${jsp.impl.version}),$(version;+;${jsp.impl.version}))";resolution:=optional, - org.apache.jasper.util;version="[$(version;===;${jsp.impl.version}),$(version;+;${jsp.impl.version}))";resolution:=optional, - org.apache.jasper.xmlparser;version="[$(version;===;${jsp.impl.version}),$(version;+;${jsp.impl.version}))";resolution:=optional, + org.apache.el;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.el.lang;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.el.stream;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.el.util;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.el.parser;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper.compiler;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper.compiler.tagplugin;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper.runtime;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper.security;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper.servlet;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper.tagplugins.jstl;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper.util;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper.xmlparser;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, org.apache.taglibs.standard;version="1.2";resolution:=optional, org.apache.taglibs.standard.extra.spath;version="1.2";resolution:=optional, org.apache.taglibs.standard.functions;version="1.2";resolution:=optional, @@ -110,7 +129,7 @@ org.apache.taglibs.standard.tag.rt.xml;version="1.2";resolution:=optional, org.apache.taglibs.standard.tei;version="1.2";resolution:=optional, org.apache.taglibs.standard.tlv;version="1.2";resolution:=optional, - org.apache.tomcat;version="[$(version;===;${jsp.impl.version}),$(version;+;${jsp.impl.version}))";resolution:=optional, + org.apache.tomcat;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, org.eclipse.jetty.jsp;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))";resolution:=optional, org.osgi.*, org.xml.*;resolution:=optional, @@ -121,8 +140,8 @@ javax.xml.parser;resolution:=optional org.eclipse.jetty.jsp.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))", - org.apache.jasper.*;version="[$(version;===;${jsp.impl.version}),$(version;+;${jsp.impl.version}))", - org.apache.el.*;version="[$(version;===;${jsp.impl.version}),$(version;+;${jsp.impl.version}))" + org.apache.jasper.*;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))", + org.apache.el.*;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))" diff --git a/jetty-osgi/pom.xml b/jetty-osgi/pom.xml index f955fde28feb..c75dade73ceb 100644 --- a/jetty-osgi/pom.xml +++ b/jetty-osgi/pom.xml @@ -12,7 +12,7 @@ pom - 3.17.100 + 3.17.200 3.10.200 3.6.100 1.0.0-v20070606 diff --git a/jetty-p2/pom.xml b/jetty-p2/pom.xml index c5d9bb5a4607..28090d0fb5c9 100644 --- a/jetty-p2/pom.xml +++ b/jetty-p2/pom.xml @@ -12,7 +12,7 @@ Generates a (maven based) P2 Updatesite pom - 2.6.0 + 2.7.0 diff --git a/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_h.java b/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_h.java index 8a4fa08d62f2..af8584f18811 100644 --- a/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_h.java +++ b/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_h.java @@ -33,7 +33,7 @@ public class quiche_h { // This interface is a translation of the quiche.h header of a specific version. // It needs to be reviewed each time the native lib version changes. - private static final String EXPECTED_QUICHE_VERSION = "0.11.0"; + private static final String EXPECTED_QUICHE_VERSION = "0.12.0"; public static final byte C_FALSE = 0; public static final byte C_TRUE = 1; diff --git a/jetty-quic/quic-quiche/quic-quiche-jna/src/main/java/org/eclipse/jetty/quic/quiche/jna/LibQuiche.java b/jetty-quic/quic-quiche/quic-quiche-jna/src/main/java/org/eclipse/jetty/quic/quiche/jna/LibQuiche.java index dbeed8c899bb..2ccb3295a960 100644 --- a/jetty-quic/quic-quiche/quic-quiche-jna/src/main/java/org/eclipse/jetty/quic/quiche/jna/LibQuiche.java +++ b/jetty-quic/quic-quiche/quic-quiche-jna/src/main/java/org/eclipse/jetty/quic/quiche/jna/LibQuiche.java @@ -31,7 +31,7 @@ public interface LibQuiche extends Library { // This interface is a translation of the quiche.h header of a specific version. // It needs to be reviewed each time the native lib version changes. - String EXPECTED_QUICHE_VERSION = "0.11.0"; + String EXPECTED_QUICHE_VERSION = "0.12.0"; // The charset used to convert java.lang.String to char * and vice versa. Charset CHARSET = StandardCharsets.UTF_8; diff --git a/jetty-quic/quic-server/src/main/java/org/eclipse/jetty/quic/server/QuicServerConnector.java b/jetty-quic/quic-server/src/main/java/org/eclipse/jetty/quic/server/QuicServerConnector.java index a134dcd1b748..a2d3c1ffe5da 100644 --- a/jetty-quic/quic-server/src/main/java/org/eclipse/jetty/quic/server/QuicServerConnector.java +++ b/jetty-quic/quic-server/src/main/java/org/eclipse/jetty/quic/server/QuicServerConnector.java @@ -89,7 +89,7 @@ public QuicServerConnector(Server server, Executor executor, Scheduler scheduler // One bidirectional stream to simulate the TCP stream, and no unidirectional streams. quicConfiguration.setMaxBidirectionalRemoteStreams(1); quicConfiguration.setMaxUnidirectionalRemoteStreams(0); - quicConfiguration.setVerifyPeerCertificates(true); + quicConfiguration.setVerifyPeerCertificates(false); } public QuicConfiguration getQuicConfiguration() diff --git a/jetty-server/src/main/config/etc/well-known.xml b/jetty-server/src/main/config/etc/well-known.xml index e35118a0df5b..068b676a4abf 100644 --- a/jetty-server/src/main/config/etc/well-known.xml +++ b/jetty-server/src/main/config/etc/well-known.xml @@ -12,7 +12,7 @@ - false + diff --git a/jetty-server/src/main/config/modules/well-known.mod b/jetty-server/src/main/config/modules/well-known.mod index e5ee42fe7f40..4db3029b075c 100644 --- a/jetty-server/src/main/config/modules/well-known.mod +++ b/jetty-server/src/main/config/modules/well-known.mod @@ -20,3 +20,6 @@ etc/well-known.xml ## Well Known Directory (relative to $JETTY_BASE if relative path, otherwise it is an absolute path). # jetty.wellknown.dir=.well-known # end::documentation[] + +## Allow contents of the well-known directory to be listed. +# jetty.wellknown.listDirectories=false diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java index da8f3242ad38..bdfcd2a770c2 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ShutdownMonitor.java @@ -205,7 +205,7 @@ public void setPort(int port) } } - protected void start() throws Exception + public void start() throws Exception { try (AutoLock l = _lock.lock()) { @@ -236,7 +236,7 @@ private void stop() } // For test purposes only. - void await() throws InterruptedException + public void await() throws InterruptedException { try (AutoLock.WithCondition l = _lock.lock()) { @@ -407,6 +407,10 @@ else if ("status".equalsIgnoreCase(cmd)) // Reply to client informClient(out, "OK\r\n"); } + else if ("pid".equalsIgnoreCase(cmd)) + { + informClient(out, Long.toString(ProcessHandle.current().pid())); + } } catch (Throwable x) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java index be3ade5225cb..1a739fe97293 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java @@ -694,7 +694,7 @@ else if (COMMA_GZIP.matcher(field.getValue()).matches()) String mimeType = context == null ? MimeTypes.getDefaultMimeByExtension(path) : context.getMimeType(path); if (mimeType != null) { - mimeType = MimeTypes.getContentTypeWithoutCharset(mimeType); + mimeType = HttpField.valueParameters(mimeType, null); if (!isMimeTypeGzipable(mimeType)) { LOG.debug("{} excluded by path suffix mime type {}", this, request); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java index 996638f9feae..95cc2bf206d8 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java @@ -23,7 +23,6 @@ import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.HttpOutput; @@ -31,7 +30,6 @@ import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IteratingNestedCallback; -import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.compression.DeflaterPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -160,8 +158,8 @@ protected void commit(ByteBuffer content, boolean complete, Callback callback) String ct = response.getContentType(); if (ct != null) { - ct = MimeTypes.getContentTypeWithoutCharset(ct); - if (!_factory.isMimeTypeGzipable(StringUtil.asciiToLowerCase(ct))) + String baseType = HttpField.valueParameters(ct, null); + if (!_factory.isMimeTypeGzipable(baseType)) { LOG.debug("{} exclude by mimeType {}", this, ct); noCompression(); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java index 0bb5904eaea2..b6704fb94366 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java @@ -25,6 +25,7 @@ import java.util.Arrays; import java.util.concurrent.Exchanger; import java.util.concurrent.TimeUnit; +import javax.net.ssl.SSLHandshakeException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -535,6 +536,10 @@ public void testMaxIdleNothingSent() throws Exception assertThat(response, is("")); assertEquals(-1, is.read()); } + catch (SSLHandshakeException e) + { + LOG.debug("Legit possible SSL result", e); + } catch (IOException e) { LOG.warn("Unable to read stream", e); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java index 21a41ea964a4..73269c2d7384 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java @@ -34,6 +34,7 @@ import java.util.Enumeration; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -253,6 +254,64 @@ public void testParamExtraction() throws Exception assertTrue(responses.startsWith("HTTP/1.1 200")); } + @Test + public void testParameterExtractionKeepOrderingIntact() throws Exception + { + AtomicReference> reference = new AtomicReference<>(); + _handler._checker = new RequestTester() + { + @Override + public boolean check(HttpServletRequest request, HttpServletResponse response) + { + reference.set(request.getParameterMap()); + return true; + } + }; + + String request = "POST /?first=1&second=2&third=3&fourth=4 HTTP/1.1\r\n" + + "Host: whatever\r\n" + + "Content-Type: application/x-www-form-urlencoded\n" + + "Connection: close\n" + + "Content-Length: 34\n" + + "\n" + + "fifth=5&sixth=6&seventh=7&eighth=8"; + + String responses = _connector.getResponse(request); + assertTrue(responses.startsWith("HTTP/1.1 200")); + assertThat(new ArrayList<>(reference.get().keySet()), is(Arrays.asList("first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth"))); + } + + @Test + public void testParameterExtractionOrderingWithMerge() throws Exception + { + AtomicReference> reference = new AtomicReference<>(); + _handler._checker = new RequestTester() + { + @Override + public boolean check(HttpServletRequest request, HttpServletResponse response) + { + reference.set(request.getParameterMap()); + return true; + } + }; + + String request = "POST /?a=1&b=2&c=3&a=4 HTTP/1.1\r\n" + + "Host: whatever\r\n" + + "Content-Type: application/x-www-form-urlencoded\n" + + "Connection: close\n" + + "Content-Length: 11\n" + + "\n" + + "c=5&b=6&a=7"; + + String responses = _connector.getResponse(request); + Map returnedMap = reference.get(); + assertTrue(responses.startsWith("HTTP/1.1 200")); + assertThat(new ArrayList<>(returnedMap.keySet()), is(Arrays.asList("a", "b", "c"))); + assertTrue(Arrays.equals(returnedMap.get("a"), new String[]{"1", "4", "7"})); + assertTrue(Arrays.equals(returnedMap.get("b"), new String[]{"2", "6"})); + assertTrue(Arrays.equals(returnedMap.get("c"), new String[]{"3", "5"})); + } + @Test public void testParamExtractionBadSequence() throws Exception { diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java index fd0218d56a2f..e93fdb783629 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ShutdownMonitorTest.java @@ -37,6 +37,36 @@ public void dispose() { ShutdownMonitor.reset(); } + + @Test + public void testPid() throws Exception + { + ShutdownMonitor monitor = ShutdownMonitor.getInstance(); + monitor.setPort(0); + monitor.setExitVm(false); + monitor.start(); + String key = monitor.getKey(); + int port = monitor.getPort(); + + // Try more than once to be sure that the ServerSocket has not been closed. + for (int i = 0; i < 2; ++i) + { + try (Socket socket = new Socket("localhost", port)) + { + OutputStream output = socket.getOutputStream(); + String command = "pid"; + output.write((key + "\r\n" + command + "\r\n").getBytes()); + output.flush(); + + BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream())); + String reply = input.readLine(); + String pid = String.valueOf(ProcessHandle.current().pid()); + assertEquals(pid, reply); + // Socket must be closed afterwards. + assertNull(input.readLine()); + } + } + } @Test public void testStatus() throws Exception diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/MultiPartServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/MultiPartServletTest.java index 97070176a571..9568a784cbac 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/MultiPartServletTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/MultiPartServletTest.java @@ -14,9 +14,14 @@ package org.eclipse.jetty.servlet; import java.io.IOException; +import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.zip.GZIPInputStream; import javax.servlet.MultipartConfigElement; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -26,8 +31,13 @@ import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.util.BytesRequestContent; +import org.eclipse.jetty.client.util.InputStreamResponseListener; import org.eclipse.jetty.client.util.MultiPartRequestContent; +import org.eclipse.jetty.client.util.StringRequestContent; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.MimeTypes; @@ -36,6 +46,7 @@ import org.eclipse.jetty.server.MultiPartFormInputStream; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.gzip.GzipHandler; import org.eclipse.jetty.util.IO; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -44,7 +55,9 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.startsWith; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; public class MultiPartServletTest { @@ -77,29 +90,53 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S } } + public static class MultiPartEchoServlet extends HttpServlet + { + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + if (!req.getContentType().contains(MimeTypes.Type.MULTIPART_FORM_DATA.asString())) + { + resp.sendError(400); + return; + } + + resp.setContentType(req.getContentType()); + IO.copy(req.getInputStream(), resp.getOutputStream()); + } + } + @BeforeEach public void start() throws Exception { tmpDir = Files.createTempDirectory(MultiPartServletTest.class.getSimpleName()); + assertNotNull(tmpDir); server = new Server(); connector = new ServerConnector(server); server.addConnector(connector); + MultipartConfigElement config = new MultipartConfigElement(tmpDir.toAbsolutePath().toString(), + MAX_FILE_SIZE, -1, 1); + ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); contextHandler.setContextPath("/"); ServletHolder servletHolder = contextHandler.addServlet(MultiPartServlet.class, "/"); - - MultipartConfigElement config = new MultipartConfigElement(tmpDir.toAbsolutePath().toString(), - MAX_FILE_SIZE, -1, 1); + servletHolder.getRegistration().setMultipartConfig(config); + servletHolder = contextHandler.addServlet(MultiPartEchoServlet.class, "/echo"); servletHolder.getRegistration().setMultipartConfig(config); - server.setHandler(contextHandler); + GzipHandler gzipHandler = new GzipHandler(); + gzipHandler.addIncludedMimeTypes("multipart/form-data"); + gzipHandler.setMinGzipSize(32); + gzipHandler.setHandler(contextHandler); + server.setHandler(gzipHandler); server.start(); client = new HttpClient(); client.start(); + client.getContentDecoderFactories().clear(); } @AfterEach @@ -135,6 +172,44 @@ public void testTempFilesDeletedOnError() throws Exception containsString("Multipart Mime part largePart exceeds max filesize")); } - assertThat(tmpDir.toFile().list().length, is(0)); + String[] fileList = tmpDir.toFile().list(); + assertNotNull(fileList); + assertThat(fileList.length, is(0)); + } + + @Test + public void testMultiPartGzip() throws Exception + { + String contentString = "the quick brown fox jumps over the lazy dog, " + + "the quick brown fox jumps over the lazy dog"; + StringRequestContent content = new StringRequestContent(contentString); + + MultiPartRequestContent multiPart = new MultiPartRequestContent(); + multiPart.addFieldPart("largePart", content, null); + multiPart.close(); + + try (StacklessLogging ignored = new StacklessLogging(HttpChannel.class, MultiPartFormInputStream.class)) + { + InputStreamResponseListener responseStream = new InputStreamResponseListener(); + client.newRequest("localhost", connector.getLocalPort()) + .path("/echo") + .scheme(HttpScheme.HTTP.asString()) + .method(HttpMethod.POST) + .headers(h -> h.add(HttpHeader.ACCEPT_ENCODING, "gzip")) + .body(multiPart) + .send(responseStream); + + Response response = responseStream.get(5, TimeUnit.SECONDS); + HttpFields headers = response.getHeaders(); + assertThat(headers.get(HttpHeader.CONTENT_TYPE), startsWith("multipart/form-data")); + assertThat(headers.get(HttpHeader.CONTENT_ENCODING), is("gzip")); + + InputStream inputStream = new GZIPInputStream(responseStream.getInputStream()); + String contentType = headers.get(HttpHeader.CONTENT_TYPE); + MultiPartFormInputStream mpis = new MultiPartFormInputStream(inputStream, contentType, null, null); + List parts = new ArrayList<>(mpis.getParts()); + assertThat(parts.size(), is(1)); + assertThat(IO.toString(parts.get(0).getInputStream()), is(contentString)); + } } } diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/ModuleGraphWriter.java b/jetty-start/src/main/java/org/eclipse/jetty/start/ModuleGraphWriter.java index 6c9050959a6a..f5a33f454d4d 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/ModuleGraphWriter.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/ModuleGraphWriter.java @@ -29,6 +29,8 @@ public class ModuleGraphWriter { private String colorModuleBg; private String colorEnabledBg; + private String colorEdgeBefore; + private String colorEdgeAfter; private String colorTransitiveBg; private String colorCellBg; private String colorHeaderBg; @@ -38,6 +40,8 @@ public ModuleGraphWriter() { colorModuleBg = "#B8FFB8"; colorEnabledBg = "#66FFCC"; + colorEdgeAfter = "#00CC33"; + colorEdgeAfter = "#33CC00"; colorTransitiveBg = "#66CC66"; colorCellBg = "#FFFFFF80"; colorHeaderBg = "#00000020"; @@ -49,6 +53,8 @@ public void config(Props props) String prefix = "jetty.graph."; colorModuleBg = getProperty(props, prefix + "color.module.bg", colorModuleBg); colorEnabledBg = getProperty(props, prefix + "color.enabled.bg", colorEnabledBg); + colorEdgeBefore = getProperty(props, prefix + "color.edge.before", colorEdgeBefore); + colorEdgeAfter = getProperty(props, prefix + "color.edge.after", colorEdgeAfter); colorTransitiveBg = getProperty(props, prefix + "color.transitive.bg", colorTransitiveBg); colorCellBg = getProperty(props, prefix + "color.cell.bg", colorCellBg); colorHeaderBg = getProperty(props, prefix + "color.header.bg", colorHeaderBg); @@ -73,7 +79,7 @@ private String getProperty(Props props, String key, String defVal) public void write(Modules modules, Path outputFile) throws IOException { try (BufferedWriter writer = Files.newBufferedWriter(outputFile, StandardCharsets.UTF_8, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE); - PrintWriter out = new PrintWriter(writer);) + PrintWriter out = new PrintWriter(writer)) { writeHeaderMessage(out, outputFile); @@ -245,13 +251,13 @@ private void writeRelationships(PrintWriter out, Iterable modules, List< depends = Module.normalizeModuleName(depends); out.printf(" \"%s\" -> \"%s\";%n", module.getName(), depends); } - for (String before : module.getBefore()) + for (String optional : module.getAfter()) { - out.printf(" \"%s\" << \"%s\";%n", module.getName(), before); + out.printf(" \"%s\" -> \"%s\" [ color=\"%s\" ];%n", module.getName(), optional, colorEdgeAfter); } - for (String after : module.getAfter()) + for (String before : module.getBefore()) { - out.printf(" \"%s\" >> \"%s\";%n", module.getName(), after); + out.printf(" \"%s\" -> \"%s\" [ color=\"%s\" ];%n", before, module.getName(), colorEdgeBefore); } } } diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleGraphWriterTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleGraphWriterTest.java index 8482e98ddc3a..431c9c3b1d56 100644 --- a/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleGraphWriterTest.java +++ b/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleGraphWriterTest.java @@ -13,8 +13,12 @@ package org.eclipse.jetty.start; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import java.time.Duration; import org.eclipse.jetty.start.config.CommandLineConfigSource; import org.eclipse.jetty.start.config.ConfigSources; @@ -28,6 +32,8 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertTimeout; +import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(WorkDirExtension.class) public class ModuleGraphWriterTest @@ -58,11 +64,51 @@ public void testGenerateNothingEnabled() throws IOException Modules modules = new Modules(basehome, args); modules.registerAll(); - Path outputFile = basehome.getBasePath("graph.dot"); + Path dotFile = basehome.getBasePath("graph.dot"); ModuleGraphWriter writer = new ModuleGraphWriter(); - writer.write(modules, outputFile); + writer.write(modules, dotFile); - assertThat("Output File Exists", FS.exists(outputFile), is(true)); + assertThat("Output File Exists", FS.exists(dotFile), is(true)); + + assertTimeout(Duration.ofSeconds(3), () -> + { + if (execDotCmd("dot", "-V")) + { + Path outputPng = testdir.getPath().resolve("output.png"); + assertTrue(execDotCmd("dot", "-Tpng", "-o" + outputPng, dotFile.toString())); + + assertThat("PNG File does not exist", FS.exists(outputPng)); + } + }); + } + + private boolean execDotCmd(String... args) + { + try + { + Process p = Runtime.getRuntime().exec(args); + + try (BufferedReader bri = new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8)); + BufferedReader bre = new BufferedReader(new InputStreamReader(p.getErrorStream(), StandardCharsets.UTF_8))) + { + String line; + while ((line = bri.readLine()) != null) + { + System.out.printf("[STDIN] %s%n", line); + } + while ((line = bre.readLine()) != null) + { + System.out.printf("[STDERR] %s%n", line); + } + } + p.waitFor(); + return true; + } + catch (IOException | InterruptedException e) + { + e.printStackTrace(); + return false; + } } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiMap.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiMap.java index fc2d818af9dc..481b28f350d2 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiMap.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiMap.java @@ -15,8 +15,8 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -26,7 +26,7 @@ * @param the entry type for multimap values */ @SuppressWarnings("serial") -public class MultiMap extends HashMap> +public class MultiMap extends LinkedHashMap> { public MultiMap() { @@ -316,13 +316,13 @@ public boolean containsSimpleValue(V value) @Override public String toString() { - Iterator>> iter = entrySet().iterator(); + Iterator>> iter = entrySet().iterator(); StringBuilder sb = new StringBuilder(); sb.append('{'); boolean delim = false; while (iter.hasNext()) { - Entry> e = iter.next(); + Map.Entry> e = iter.next(); if (delim) { sb.append(", "); @@ -350,7 +350,7 @@ public String toString() */ public Map toStringArrayMap() { - HashMap map = new HashMap(size() * 3 / 2) + Map map = new LinkedHashMap(size() * 3 / 2) { @Override public String toString() diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configurations.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configurations.java index 3aba9bf13632..588f3599ecfa 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configurations.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configurations.java @@ -265,6 +265,12 @@ public Configurations(Configurations classlist) .map(c -> c.getClass().getName()) .toArray(String[]::new)); } + + @Override + public boolean add(Configuration configuration) + { + return addConfiguration(configuration); + } public void add(Configuration... configurations) { @@ -428,7 +434,7 @@ public Iterator iterator() return getConfigurations().iterator(); } - private void addConfiguration(Configuration configuration) + private boolean addConfiguration(Configuration configuration) { String name = configuration.getClass().getName(); // Is this configuration known? @@ -462,14 +468,14 @@ private void addConfiguration(Configuration configuration) if (r != null) { if (r.getName().equals(configuration.getClass().getName())) - return; //skip the addition, a replacement is already present + return false; //skip the addition, a replacement is already present } if (c.getClass().getName().equals(configuration.getClass().getName())) - return; //don't add same one twice + return false; //don't add same one twice } - _configurations.add(configuration); + return _configurations.add(configuration); } @Override diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerFactory.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerFactory.java index 3910e04bce32..53ba19bf05ac 100644 --- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerFactory.java +++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerFactory.java @@ -54,8 +54,6 @@ import org.eclipse.jetty.websocket.javax.common.messages.DecodedTextMessageSink; import org.eclipse.jetty.websocket.javax.common.messages.DecodedTextStreamMessageSink; -import static java.nio.charset.StandardCharsets.UTF_8; - public abstract class JavaxWebSocketFrameHandlerFactory { private static final MethodHandle FILTER_RETURN_TYPE_METHOD; @@ -536,49 +534,47 @@ public static MethodHandle bindTemplateVariables(MethodHandle target, String[] n { retHandle = MethodHandles.insertArguments(retHandle, IDX, strValue); } - else if (Integer.TYPE.isAssignableFrom(type)) + else if (Integer.class.isAssignableFrom(type) || Integer.TYPE.isAssignableFrom(type)) { - int intValue = Integer.parseInt(strValue); + Integer intValue = Integer.parseInt(strValue); retHandle = MethodHandles.insertArguments(retHandle, IDX, intValue); } - else if (Long.TYPE.isAssignableFrom(type)) + else if (Long.class.isAssignableFrom(type) || Long.TYPE.isAssignableFrom(type)) { - long longValue = Long.parseLong(strValue); + Long longValue = Long.parseLong(strValue); retHandle = MethodHandles.insertArguments(retHandle, IDX, longValue); } - else if (Short.TYPE.isAssignableFrom(type)) + else if (Short.class.isAssignableFrom(type) || Short.TYPE.isAssignableFrom(type)) { - short shortValue = Short.parseShort(strValue); + Short shortValue = Short.parseShort(strValue); retHandle = MethodHandles.insertArguments(retHandle, IDX, shortValue); } - else if (Float.TYPE.isAssignableFrom(type)) + else if (Float.class.isAssignableFrom(type) || Float.TYPE.isAssignableFrom(type)) { - float floatValue = Float.parseFloat(strValue); + Float floatValue = Float.parseFloat(strValue); retHandle = MethodHandles.insertArguments(retHandle, IDX, floatValue); } - else if (Double.TYPE.isAssignableFrom(type)) + else if (Double.class.isAssignableFrom(type) || Double.TYPE.isAssignableFrom(type)) { - double doubleValue = Double.parseDouble(strValue); + Double doubleValue = Double.parseDouble(strValue); retHandle = MethodHandles.insertArguments(retHandle, IDX, doubleValue); } - else if (Boolean.TYPE.isAssignableFrom(type)) + else if (Boolean.class.isAssignableFrom(type) || Boolean.TYPE.isAssignableFrom(type)) { - boolean boolValue = Boolean.parseBoolean(strValue); + Boolean boolValue = Boolean.parseBoolean(strValue); retHandle = MethodHandles.insertArguments(retHandle, IDX, boolValue); } - else if (Character.TYPE.isAssignableFrom(type)) + else if (Character.class.isAssignableFrom(type) || Character.TYPE.isAssignableFrom(type)) { if (strValue.length() != 1) throw new IllegalArgumentException("Invalid Size"); - char charValue = strValue.charAt(0); + Character charValue = strValue.charAt(0); retHandle = MethodHandles.insertArguments(retHandle, IDX, charValue); } - else if (Byte.TYPE.isAssignableFrom(type)) + else if (Byte.class.isAssignableFrom(type) || Byte.TYPE.isAssignableFrom(type)) { - byte[] buf = strValue.getBytes(UTF_8); - if (buf.length != 1) - throw new IllegalArgumentException("Invalid Size"); - retHandle = MethodHandles.insertArguments(retHandle, IDX, buf[0]); + Byte b = Byte.parseByte(strValue); + retHandle = MethodHandles.insertArguments(retHandle, IDX, b); } else { diff --git a/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/PathParamIdentifier.java b/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/PathParamIdentifier.java index 72424f90e19b..bac060f32240 100644 --- a/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/PathParamIdentifier.java +++ b/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/PathParamIdentifier.java @@ -52,13 +52,21 @@ public InvokerUtils.Arg getParamArg(Method method, Class paramType, int idx) public static void validateType(Class type) { if (!String.class.isAssignableFrom(type) && + !Integer.class.isAssignableFrom(type) && !Integer.TYPE.isAssignableFrom(type) && + !Long.class.isAssignableFrom(type) && !Long.TYPE.isAssignableFrom(type) && + !Short.class.isAssignableFrom(type) && !Short.TYPE.isAssignableFrom(type) && + !Float.class.isAssignableFrom(type) && !Float.TYPE.isAssignableFrom(type) && + !Double.class.isAssignableFrom(type) && !Double.TYPE.isAssignableFrom(type) && + !Boolean.class.isAssignableFrom(type) && !Boolean.TYPE.isAssignableFrom(type) && + !Character.class.isAssignableFrom(type) && !Character.TYPE.isAssignableFrom(type) && + !Byte.class.isAssignableFrom(type) && !Byte.TYPE.isAssignableFrom(type)) throw new InvalidSignatureException("Unsupported PathParam Type: " + type); } diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/PathParamTest.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/PathParamTest.java index 49c078ca0e19..394c8c4369c8 100644 --- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/PathParamTest.java +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/PathParamTest.java @@ -15,6 +15,7 @@ import java.net.URI; import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; import javax.websocket.ContainerProvider; import javax.websocket.OnMessage; import javax.websocket.OnOpen; @@ -27,12 +28,35 @@ import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer; +import org.eclipse.jetty.websocket.javax.server.internal.JavaxWebSocketServerContainer; +import org.eclipse.jetty.websocket.javax.tests.pathparam.BooleanClassSocket; +import org.eclipse.jetty.websocket.javax.tests.pathparam.BooleanTypeSocket; +import org.eclipse.jetty.websocket.javax.tests.pathparam.ByteClassSocket; +import org.eclipse.jetty.websocket.javax.tests.pathparam.ByteTypeSocket; +import org.eclipse.jetty.websocket.javax.tests.pathparam.CharacterClassSocket; +import org.eclipse.jetty.websocket.javax.tests.pathparam.CharacterTypeSocket; +import org.eclipse.jetty.websocket.javax.tests.pathparam.DoubleClassSocket; +import org.eclipse.jetty.websocket.javax.tests.pathparam.DoubleTypeSocket; +import org.eclipse.jetty.websocket.javax.tests.pathparam.FloatClassSocket; +import org.eclipse.jetty.websocket.javax.tests.pathparam.FloatTypeSocket; +import org.eclipse.jetty.websocket.javax.tests.pathparam.IntegerClassSocket; +import org.eclipse.jetty.websocket.javax.tests.pathparam.IntegerTypeSocket; +import org.eclipse.jetty.websocket.javax.tests.pathparam.LongClassSocket; +import org.eclipse.jetty.websocket.javax.tests.pathparam.LongTypeSocket; +import org.eclipse.jetty.websocket.javax.tests.pathparam.ShortClassSocket; +import org.eclipse.jetty.websocket.javax.tests.pathparam.ShortTypeSocket; +import org.eclipse.jetty.websocket.javax.tests.pathparam.StringClassSocket; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; public class PathParamTest { @@ -81,6 +105,48 @@ public void onMessage(String message, @PathParam("name") String name) } } + public static Stream pathParamEndpoints() + { + return Stream.of( + Arguments.of(BooleanClassSocket.class, "false"), + Arguments.of(BooleanTypeSocket.class, "true"), + Arguments.of(ByteClassSocket.class, "32"), + Arguments.of(ByteTypeSocket.class, "51"), + Arguments.of(CharacterClassSocket.class, "q"), + Arguments.of(CharacterTypeSocket.class, "&"), + Arguments.of(DoubleClassSocket.class, Double.toString(Double.MAX_VALUE)), + Arguments.of(DoubleTypeSocket.class, Double.toString(Double.MIN_VALUE)), + Arguments.of(FloatClassSocket.class, "0.00235"), + Arguments.of(FloatTypeSocket.class, "123.456"), + Arguments.of(IntegerClassSocket.class, Integer.toString(Integer.MIN_VALUE)), + Arguments.of(IntegerTypeSocket.class, Integer.toString(Integer.MAX_VALUE)), + Arguments.of(LongClassSocket.class, Long.toString(Long.MAX_VALUE)), + Arguments.of(LongTypeSocket.class, Long.toString(Long.MIN_VALUE)), + Arguments.of(ShortClassSocket.class, Short.toString(Short.MAX_VALUE)), + Arguments.of(ShortTypeSocket.class, Short.toString(Short.MIN_VALUE)), + Arguments.of(StringClassSocket.class, "this_is_a_String_ID") + ); + } + + @ParameterizedTest + @MethodSource("pathParamEndpoints") + public void testPathParamSignatures(Class endpointClass, String id) throws Exception + { + JavaxWebSocketServerContainer serverContainer = JavaxWebSocketServerContainer.getContainer(_context.getServletContext()); + assertNotNull(serverContainer); + serverContainer.addEndpoint(endpointClass); + + WebSocketContainer container = ContainerProvider.getWebSocketContainer(); + EventSocket clientEndpoint = new EventSocket(); + + URI serverUri = URI.create("ws://localhost:" + _connector.getLocalPort() + "/context/pathParam/id/" + id); + container.connectToServer(clientEndpoint, serverUri); + + assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS)); + String resp = clientEndpoint.textMessages.poll(1, TimeUnit.SECONDS); + assertThat(resp, is("id: " + id)); + } + @Test public void testBasicPathParamSocket() throws Exception { diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/BooleanClassSocket.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/BooleanClassSocket.java new file mode 100644 index 000000000000..f414401c6d2f --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/BooleanClassSocket.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.websocket.javax.tests.pathparam; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/pathParam/id/{id}") +public class BooleanClassSocket +{ + @OnOpen + public void onOpen(Session session, @PathParam("id") Boolean id) throws Exception + { + session.getBasicRemote().sendText("id: " + id); + session.close(); + } +} diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/BooleanTypeSocket.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/BooleanTypeSocket.java new file mode 100644 index 000000000000..e211383f1337 --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/BooleanTypeSocket.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.websocket.javax.tests.pathparam; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/pathParam/id/{id}") +public class BooleanTypeSocket +{ + @OnOpen + public void onOpen(Session session, @PathParam("id") boolean id) throws Exception + { + session.getBasicRemote().sendText("id: " + id); + session.close(); + } +} diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/ByteClassSocket.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/ByteClassSocket.java new file mode 100644 index 000000000000..9608e0d56eff --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/ByteClassSocket.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.websocket.javax.tests.pathparam; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/pathParam/id/{id}") +public class ByteClassSocket +{ + @OnOpen + public void onOpen(Session session, @PathParam("id") Byte id) throws Exception + { + session.getBasicRemote().sendText("id: " + id); + session.close(); + } +} diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/ByteTypeSocket.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/ByteTypeSocket.java new file mode 100644 index 000000000000..5c197f876317 --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/ByteTypeSocket.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.websocket.javax.tests.pathparam; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/pathParam/id/{id}") +public class ByteTypeSocket +{ + @OnOpen + public void onOpen(Session session, @PathParam("id") byte id) throws Exception + { + session.getBasicRemote().sendText("id: " + id); + session.close(); + } +} diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/CharacterClassSocket.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/CharacterClassSocket.java new file mode 100644 index 000000000000..d22f058915f1 --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/CharacterClassSocket.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.websocket.javax.tests.pathparam; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/pathParam/id/{id}") +public class CharacterClassSocket +{ + @OnOpen + public void onOpen(Session session, @PathParam("id") Character id) throws Exception + { + session.getBasicRemote().sendText("id: " + id); + session.close(); + } +} diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/CharacterTypeSocket.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/CharacterTypeSocket.java new file mode 100644 index 000000000000..e03191316aff --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/CharacterTypeSocket.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.websocket.javax.tests.pathparam; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/pathParam/id/{id}") +public class CharacterTypeSocket +{ + @OnOpen + public void onOpen(Session session, @PathParam("id") char id) throws Exception + { + session.getBasicRemote().sendText("id: " + id); + session.close(); + } +} diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/DoubleClassSocket.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/DoubleClassSocket.java new file mode 100644 index 000000000000..60f47756361e --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/DoubleClassSocket.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.websocket.javax.tests.pathparam; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/pathParam/id/{id}") +public class DoubleClassSocket +{ + @OnOpen + public void onOpen(Session session, @PathParam("id") Double id) throws Exception + { + session.getBasicRemote().sendText("id: " + id); + session.close(); + } +} diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/DoubleTypeSocket.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/DoubleTypeSocket.java new file mode 100644 index 000000000000..5a64664000e3 --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/DoubleTypeSocket.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.websocket.javax.tests.pathparam; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/pathParam/id/{id}") +public class DoubleTypeSocket +{ + @OnOpen + public void onOpen(Session session, @PathParam("id") double id) throws Exception + { + session.getBasicRemote().sendText("id: " + id); + session.close(); + } +} diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/FloatClassSocket.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/FloatClassSocket.java new file mode 100644 index 000000000000..b59fc4c0b677 --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/FloatClassSocket.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.websocket.javax.tests.pathparam; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/pathParam/id/{id}") +public class FloatClassSocket +{ + @OnOpen + public void onOpen(Session session, @PathParam("id") Float id) throws Exception + { + session.getBasicRemote().sendText("id: " + id); + session.close(); + } +} diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/FloatTypeSocket.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/FloatTypeSocket.java new file mode 100644 index 000000000000..6e8b290aacd0 --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/FloatTypeSocket.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.websocket.javax.tests.pathparam; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/pathParam/id/{id}") +public class FloatTypeSocket +{ + @OnOpen + public void onOpen(Session session, @PathParam("id") float id) throws Exception + { + session.getBasicRemote().sendText("id: " + id); + session.close(); + } +} diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/IntegerClassSocket.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/IntegerClassSocket.java new file mode 100644 index 000000000000..9a750baa2d79 --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/IntegerClassSocket.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.websocket.javax.tests.pathparam; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/pathParam/id/{id}") +public class IntegerClassSocket +{ + @OnOpen + public void onOpen(Session session, @PathParam("id") Integer id) throws Exception + { + session.getBasicRemote().sendText("id: " + id); + session.close(); + } +} diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/IntegerTypeSocket.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/IntegerTypeSocket.java new file mode 100644 index 000000000000..4b15e24ee916 --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/IntegerTypeSocket.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.websocket.javax.tests.pathparam; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/pathParam/id/{id}") +public class IntegerTypeSocket +{ + @OnOpen + public void onOpen(Session session, @PathParam("id") int id) throws Exception + { + session.getBasicRemote().sendText("id: " + id); + session.close(); + } +} diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/LongClassSocket.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/LongClassSocket.java new file mode 100644 index 000000000000..cbf270915e85 --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/LongClassSocket.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.websocket.javax.tests.pathparam; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/pathParam/id/{id}") +public class LongClassSocket +{ + @OnOpen + public void onOpen(Session session, @PathParam("id") Long id) throws Exception + { + session.getBasicRemote().sendText("id: " + id); + session.close(); + } +} diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/LongTypeSocket.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/LongTypeSocket.java new file mode 100644 index 000000000000..25a8c2dab067 --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/LongTypeSocket.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.websocket.javax.tests.pathparam; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/pathParam/id/{id}") +public class LongTypeSocket +{ + @OnOpen + public void onOpen(Session session, @PathParam("id") long id) throws Exception + { + session.getBasicRemote().sendText("id: " + id); + session.close(); + } +} diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/ShortClassSocket.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/ShortClassSocket.java new file mode 100644 index 000000000000..0f88eb6b60af --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/ShortClassSocket.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.websocket.javax.tests.pathparam; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/pathParam/id/{id}") +public class ShortClassSocket +{ + @OnOpen + public void onOpen(Session session, @PathParam("id") Short id) throws Exception + { + session.getBasicRemote().sendText("id: " + id); + session.close(); + } +} diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/ShortTypeSocket.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/ShortTypeSocket.java new file mode 100644 index 000000000000..32747d91b275 --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/ShortTypeSocket.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.websocket.javax.tests.pathparam; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/pathParam/id/{id}") +public class ShortTypeSocket +{ + @OnOpen + public void onOpen(Session session, @PathParam("id") short id) throws Exception + { + session.getBasicRemote().sendText("id: " + id); + session.close(); + } +} diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/StringClassSocket.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/StringClassSocket.java new file mode 100644 index 000000000000..a52616b91612 --- /dev/null +++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/pathparam/StringClassSocket.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.websocket.javax.tests.pathparam; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint("/pathParam/id/{id}") +public class StringClassSocket +{ + @OnOpen + public void onOpen(Session session, @PathParam("id") String id) throws Exception + { + session.getBasicRemote().sendText("id: " + id); + session.close(); + } +} diff --git a/pom.xml b/pom.xml index ac43330a6141..69f00157917d 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ 2.2.1 2.5.3 9.2 - 4.1.1 + 4.2.0 5.3.0 1.5 9.3 @@ -43,18 +43,18 @@ 7.0.3 3.0.2 2.11.0 - 1.44.0 - 2.8.9 - 31.0.1-jre + 1.45.0 + 2.9.0 + 31.1-jre 5.1.0 2.2 2.14.5 4.2.4 4.3.5.Final - 11.0.14.Final - 2.13.1 - 2.13.1 - 2.13.1 + 11.0.15.Final + 2.13.2 + 2.13.2 + 2.13.2 1.2.2 1.3.5 3.0.3 @@ -84,7 +84,7 @@ 3.1.0.Final 1.1 1.0.7 - 0.11.0.2 + 0.12.0 4.0.6 1.2 5.9 @@ -97,12 +97,12 @@ 0.38.17 1.3.3 1.1.1 - 2.4.7 + 2.4.8 9.0.52 5.8.2 2.0.1 - 2.17.1 - 1.3.0-alpha13 + 2.17.2 + 1.3.0-alpha14 3.0.3 10.3.6 0.13.1 @@ -119,9 +119,9 @@ 1.2.5 1.2.5 1.16.3 - 3.1.8.Final - 1.5.4.Final - 1.18.3.Final + 3.1.9.Final + 1.6.0.Final + 1.19.0.Final 2.4.7 @@ -141,8 +141,8 @@ 5.1.4 3.1.0 3.1.2 - 3.8.1 - 3.2.0 + 3.10.1 + 3.3.0 3.0.0-M2 2.10 3.0.0 @@ -152,20 +152,19 @@ 3.2.2 3.2.2 3.3.2 - 3.1.1 + 3.2.0 3.6.4 3.6.4 - 3.2.1 2.5.3 1.7.0 3.2.0 3.2.4 - 3.10.0 + 3.11.0 3.0.0-M5 3.2.1 3.3.2 4.5.3.0 - 2.9.0 + 2.10.0 false @@ -700,11 +699,6 @@ maven-plugin-plugin ${maven-plugin.plugin.version} - - org.apache.maven.plugins - maven-project-info-reports-plugin - ${maven.project-info-reports.plugin.version} - org.apache.maven.plugins maven-release-plugin @@ -1187,6 +1181,11 @@ + + org.apache.maven + maven-model + ${maven.version} + org.apache.maven maven-plugin-api @@ -2059,47 +2058,6 @@ - - checkstyle - - - - org.apache.maven.plugins - maven-project-info-reports-plugin - ${maven.project-info-reports.plugin.version} - - - org.apache.maven.plugins - maven-checkstyle-plugin - - - - checkstyle - - - - - - - - - - org.apache.maven.plugins - maven-site-plugin - ${maven.site.plugin.version} - - - build-site - - site - - package - - - - - - config @@ -2310,7 +2268,6 @@ org.apache.maven.plugins maven-surefire-plugin - ${maven.surefire.plugin.version} external, large-disk-resource @@ -2335,9 +2292,9 @@ ${spotbugs.skip} ${spotbugs.failOnError} true - false ${spotbugs.effort} ${spotbugs.threshold} + false @@ -2345,16 +2302,6 @@ - - jdk17 - - [17,) - - - - true - - update-version diff --git a/tests/test-distribution/pom.xml b/tests/test-distribution/pom.xml index 571f11529927..daba9a6e146b 100644 --- a/tests/test-distribution/pom.xml +++ b/tests/test-distribution/pom.xml @@ -149,6 +149,11 @@ websocket-jetty-api test + + org.eclipse.jetty + jetty-openid + test + org.eclipse.jetty.tests test-felix-webapp diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java index 9879b83005c0..92be92551988 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java @@ -41,6 +41,7 @@ import org.eclipse.jetty.http3.client.http.HttpClientTransportOverHTTP3; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.start.FS; +import org.eclipse.jetty.tests.distribution.openid.OpenIdProvider; import org.eclipse.jetty.toolchain.test.PathAssert; import org.eclipse.jetty.unixsocket.client.HttpClientTransportOverUnixSockets; import org.eclipse.jetty.unixsocket.server.UnixSocketConnector; @@ -1143,4 +1144,89 @@ public void testH3() throws Exception } } } + + @Test + public void testOpenID() throws Exception + { + Path jettyBase = newTestJettyBaseDirectory(); + String jettyVersion = System.getProperty("jettyVersion"); + JettyHomeTester distribution = JettyHomeTester.Builder.newInstance() + .jettyVersion(jettyVersion) + .jettyBase(jettyBase) + .mavenLocalRepository(System.getProperty("mavenRepoPath")) + .build(); + + String[] args1 = { + "--create-startd", + "--approve-all-licenses", + "--add-to-start=http,webapp,deploy,openid" + }; + + String clientId = "clientId123"; + String clientSecret = "clientSecret456"; + OpenIdProvider openIdProvider = new OpenIdProvider(clientId, clientSecret); + try (JettyHomeTester.Run run1 = distribution.start(args1)) + { + assertTrue(run1.awaitFor(10, TimeUnit.SECONDS)); + assertEquals(0, run1.getExitValue()); + + File webApp = distribution.resolveArtifact("org.eclipse.jetty.tests:test-openid-webapp:war:" + jettyVersion); + distribution.installWarFile(webApp, "test"); + + int port = distribution.freePort(); + openIdProvider.addRedirectUri("http://localhost:" + port + "/test/j_security_check"); + openIdProvider.start(); + String[] args2 = { + "jetty.http.port=" + port, + "jetty.ssl.port=" + port, + "jetty.openid.provider=" + openIdProvider.getProvider(), + "jetty.openid.clientId=" + clientId, + "jetty.openid.clientSecret=" + clientSecret, + //"jetty.server.dumpAfterStart=true", + }; + + try (JettyHomeTester.Run run2 = distribution.start(args2)) + { + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); + startHttpClient(false); + String uri = "http://localhost:" + port + "/test"; + openIdProvider.setUser(new OpenIdProvider.User("123456789", "Alice")); + + // Initially not authenticated + ContentResponse response = client.GET(uri + "/"); + assertThat(response.getStatus(), is(HttpStatus.OK_200)); + String content = response.getContentAsString(); + assertThat(content, containsString("not authenticated")); + + // Request to login is success + response = client.GET(uri + "/login"); + assertThat(response.getStatus(), is(HttpStatus.OK_200)); + content = response.getContentAsString(); + assertThat(content, containsString("success")); + + // Now authenticated we can get info + response = client.GET(uri + "/"); + assertThat(response.getStatus(), is(HttpStatus.OK_200)); + content = response.getContentAsString(); + assertThat(content, containsString("userId: 123456789")); + assertThat(content, containsString("name: Alice")); + assertThat(content, containsString("email: Alice@example.com")); + + // Request to admin page gives 403 as we do not have admin role + response = client.GET(uri + "/admin"); + assertThat(response.getStatus(), is(HttpStatus.FORBIDDEN_403)); + + // We are no longer authenticated after logging out + response = client.GET(uri + "/logout"); + assertThat(response.getStatus(), is(HttpStatus.OK_200)); + content = response.getContentAsString(); + assertThat(content, containsString("not authenticated")); + + } + } + finally + { + openIdProvider.stop(); + } + } } diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/openid/JwtEncoder.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/openid/JwtEncoder.java new file mode 100644 index 000000000000..91036b379497 --- /dev/null +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/openid/JwtEncoder.java @@ -0,0 +1,53 @@ +// +// ======================================================================== +// 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.tests.distribution.openid; + +import java.util.Base64; + +/** + * A basic JWT encoder for testing purposes. + */ +public class JwtEncoder +{ + private static final Base64.Encoder ENCODER = Base64.getUrlEncoder(); + private static final String DEFAULT_HEADER = "{\"INFO\": \"this is not used or checked in our implementation\"}"; + private static final String DEFAULT_SIGNATURE = "we do not validate signature as we use the authorization code flow"; + + public static String encode(String idToken) + { + return stripPadding(ENCODER.encodeToString(DEFAULT_HEADER.getBytes())) + "." + + stripPadding(ENCODER.encodeToString(idToken.getBytes())) + "." + + stripPadding(ENCODER.encodeToString(DEFAULT_SIGNATURE.getBytes())); + } + + private static String stripPadding(String paddedBase64) + { + return paddedBase64.split("=")[0]; + } + + /** + * Create a basic JWT for testing using argument supplied attributes. + */ + public static String createIdToken(String provider, String clientId, String subject, String name, long expiry) + { + return "{" + + "\"iss\": \"" + provider + "\"," + + "\"sub\": \"" + subject + "\"," + + "\"aud\": \"" + clientId + "\"," + + "\"exp\": " + expiry + "," + + "\"name\": \"" + name + "\"," + + "\"email\": \"" + name + "@example.com" + "\"" + + "}"; + } +} diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/openid/OpenIdProvider.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/openid/OpenIdProvider.java new file mode 100644 index 000000000000..4eaa11a119a5 --- /dev/null +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/openid/OpenIdProvider.java @@ -0,0 +1,341 @@ +// +// ======================================================================== +// 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.tests.distribution.openid; + +import java.io.IOException; +import java.io.PrintWriter; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.security.openid.OpenIdConfiguration; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class OpenIdProvider extends ContainerLifeCycle +{ + private static final Logger LOG = LoggerFactory.getLogger(OpenIdProvider.class); + + private static final String CONFIG_PATH = "/.well-known/openid-configuration"; + private static final String AUTH_PATH = "/auth"; + private static final String TOKEN_PATH = "/token"; + private final Map issuedAuthCodes = new HashMap<>(); + + protected final String clientId; + protected final String clientSecret; + protected final List redirectUris = new ArrayList<>(); + private final ServerConnector connector; + private final Server server; + private int port = 0; + private String provider; + private User preAuthedUser; + + public static void main(String[] args) throws Exception + { + String clientId = "CLIENT_ID123"; + String clientSecret = "PASSWORD123"; + int port = 5771; + String redirectUri = "http://localhost:8080/openid/auth"; + + OpenIdProvider openIdProvider = new OpenIdProvider(clientId, clientSecret); + openIdProvider.addRedirectUri(redirectUri); + openIdProvider.setPort(port); + openIdProvider.start(); + try + { + openIdProvider.join(); + } + finally + { + openIdProvider.stop(); + } + } + + public OpenIdProvider(String clientId, String clientSecret) + { + this.clientId = clientId; + this.clientSecret = clientSecret; + + server = new Server(); + connector = new ServerConnector(server); + server.addConnector(connector); + + ServletContextHandler contextHandler = new ServletContextHandler(); + contextHandler.setContextPath("/"); + contextHandler.addServlet(new ServletHolder(new OpenIdConfigServlet()), CONFIG_PATH); + contextHandler.addServlet(new ServletHolder(new OpenIdAuthEndpoint()), AUTH_PATH); + contextHandler.addServlet(new ServletHolder(new OpenIdTokenEndpoint()), TOKEN_PATH); + server.setHandler(contextHandler); + + addBean(server); + } + + public void join() throws InterruptedException + { + server.join(); + } + + public OpenIdConfiguration getOpenIdConfiguration() + { + String provider = getProvider(); + String authEndpoint = provider + AUTH_PATH; + String tokenEndpoint = provider + TOKEN_PATH; + return new OpenIdConfiguration(provider, authEndpoint, tokenEndpoint, clientId, clientSecret, null); + } + + @Override + protected void doStart() throws Exception + { + connector.setPort(port); + super.doStart(); + provider = "http://localhost:" + connector.getLocalPort(); + } + + public void setPort(int port) + { + if (isStarted()) + throw new IllegalStateException(); + this.port = port; + } + + public void setUser(User user) + { + this.preAuthedUser = user; + } + + public String getProvider() + { + if (!isStarted() && port == 0) + throw new IllegalStateException("Port of OpenIdProvider not configured"); + return provider; + } + + public void addRedirectUri(String uri) + { + redirectUris.add(uri); + } + + public class OpenIdAuthEndpoint extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + if (!clientId.equals(req.getParameter("client_id"))) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid client_id"); + return; + } + + String redirectUri = req.getParameter("redirect_uri"); + if (!redirectUris.contains(redirectUri)) + { + LOG.warn("invalid redirectUri {}", redirectUri); + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid redirect_uri"); + return; + } + + String scopeString = req.getParameter("scope"); + List scopes = (scopeString == null) ? Collections.emptyList() : Arrays.asList(StringUtil.csvSplit(scopeString)); + if (!scopes.contains("openid")) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no openid scope"); + return; + } + + if (!"code".equals(req.getParameter("response_type"))) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "response_type must be code"); + return; + } + + String state = req.getParameter("state"); + if (state == null) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no state param"); + return; + } + + if (preAuthedUser == null) + { + PrintWriter writer = resp.getWriter(); + resp.setContentType("text/html"); + writer.println("

Login to OpenID Connect Provider

"); + writer.println("
"); + writer.println(""); + writer.println(""); + writer.println(""); + writer.println(""); + writer.println("
"); + } + else + { + redirectUser(req, preAuthedUser, redirectUri, state); + } + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + String redirectUri = req.getParameter("redirectUri"); + if (!redirectUris.contains(redirectUri)) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid redirect_uri"); + return; + } + + String state = req.getParameter("state"); + if (state == null) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no state param"); + return; + } + + String username = req.getParameter("username"); + if (username == null) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no username"); + return; + } + + User user = new User(username); + redirectUser(req, user, redirectUri, state); + } + + public void redirectUser(HttpServletRequest request, User user, String redirectUri, String state) throws IOException + { + String authCode = UUID.randomUUID().toString().replace("-", ""); + issuedAuthCodes.put(authCode, user); + + try + { + final Request baseRequest = Objects.requireNonNull(Request.getBaseRequest(request)); + final Response baseResponse = baseRequest.getResponse(); + redirectUri += "?code=" + authCode + "&state=" + state; + int redirectCode = (baseRequest.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() + ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER); + baseResponse.sendRedirect(redirectCode, baseResponse.encodeRedirectURL(redirectUri)); + } + catch (Throwable t) + { + issuedAuthCodes.remove(authCode); + throw t; + } + } + } + + public class OpenIdTokenEndpoint extends HttpServlet + { + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + String code = req.getParameter("code"); + + if (!clientId.equals(req.getParameter("client_id")) || + !clientSecret.equals(req.getParameter("client_secret")) || + !redirectUris.contains(req.getParameter("redirect_uri")) || + !"authorization_code".equals(req.getParameter("grant_type")) || + code == null) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "bad auth request"); + return; + } + + User user = issuedAuthCodes.remove(code); + if (user == null) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid auth code"); + return; + } + + String accessToken = "ABCDEFG"; + long expiry = System.currentTimeMillis() + Duration.ofMinutes(10).toMillis(); + String response = "{" + + "\"access_token\": \"" + accessToken + "\"," + + "\"id_token\": \"" + JwtEncoder.encode(user.getIdToken(provider, clientId)) + "\"," + + "\"expires_in\": " + expiry + "," + + "\"token_type\": \"Bearer\"" + + "}"; + + resp.setContentType("text/plain"); + resp.getWriter().print(response); + } + } + + public class OpenIdConfigServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + String discoveryDocument = "{" + + "\"issuer\": \"" + provider + "\"," + + "\"authorization_endpoint\": \"" + provider + AUTH_PATH + "\"," + + "\"token_endpoint\": \"" + provider + TOKEN_PATH + "\"," + + "}"; + + resp.getWriter().write(discoveryDocument); + } + } + + public static class User + { + private final String subject; + private final String name; + + public User(String name) + { + this(UUID.nameUUIDFromBytes(name.getBytes()).toString(), name); + } + + public User(String subject, String name) + { + this.subject = subject; + this.name = name; + } + + public String getName() + { + return name; + } + + public String getSubject() + { + return subject; + } + + public String getIdToken(String provider, String clientId) + { + long expiry = System.currentTimeMillis() + Duration.ofMinutes(1).toMillis(); + return JwtEncoder.createIdToken(provider, clientId, subject, name, expiry); + } + } +} diff --git a/tests/test-webapps/pom.xml b/tests/test-webapps/pom.xml index 1a56568e890b..d7a97ab965f9 100644 --- a/tests/test-webapps/pom.xml +++ b/tests/test-webapps/pom.xml @@ -10,17 +10,10 @@ test-webapps-parent Jetty Tests :: WebApps :: Parent pom - - - - org.jacoco - jacoco-maven-plugin - - true - - - - + + true + true + test-webapp-rfc2616 test-http2-webapp @@ -33,5 +26,6 @@ test-bad-websocket-webapp test-websocket-client-webapp test-websocket-client-provided-webapp + test-openid-webapp diff --git a/tests/test-webapps/test-openid-webapp/pom.xml b/tests/test-webapps/test-openid-webapp/pom.xml new file mode 100644 index 000000000000..49fd13757e65 --- /dev/null +++ b/tests/test-webapps/test-openid-webapp/pom.xml @@ -0,0 +1,31 @@ + + + + org.eclipse.jetty.tests + test-webapps-parent + 10.0.9-SNAPSHOT + + + 4.0.0 + test-openid-webapp + war + + Test :: Jetty OpenId Webapp + + + + org.slf4j + slf4j-api + + + org.eclipse.jetty + jetty-slf4j-impl + compile + + + org.eclipse.jetty.toolchain + jetty-servlet-api + provided + + + diff --git a/tests/test-webapps/test-openid-webapp/src/main/java/org/eclipse/jetty/test/openid/AdminPage.java b/tests/test-webapps/test-openid-webapp/src/main/java/org/eclipse/jetty/test/openid/AdminPage.java new file mode 100644 index 000000000000..6957b1bb5e75 --- /dev/null +++ b/tests/test-webapps/test-openid-webapp/src/main/java/org/eclipse/jetty/test/openid/AdminPage.java @@ -0,0 +1,31 @@ +// +// ======================================================================== +// 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.test.openid; + +import java.io.IOException; +import java.util.Map; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class AdminPage extends HttpServlet +{ + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + @SuppressWarnings("unchecked") + Map userInfo = (Map)request.getSession().getAttribute("org.eclipse.jetty.security.openid.claims"); + response.getWriter().println(userInfo.get("sub") + ": success"); + } +} diff --git a/tests/test-webapps/test-openid-webapp/src/main/java/org/eclipse/jetty/test/openid/ErrorPage.java b/tests/test-webapps/test-openid-webapp/src/main/java/org/eclipse/jetty/test/openid/ErrorPage.java new file mode 100644 index 000000000000..6869e21ce33e --- /dev/null +++ b/tests/test-webapps/test-openid-webapp/src/main/java/org/eclipse/jetty/test/openid/ErrorPage.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.test.openid; + +import java.io.IOException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class ErrorPage extends HttpServlet +{ + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + response.setContentType("text/html"); + response.getWriter().println("not authorized"); + response.getWriter().println("
Home"); + } +} diff --git a/tests/test-webapps/test-openid-webapp/src/main/java/org/eclipse/jetty/test/openid/HomePage.java b/tests/test-webapps/test-openid-webapp/src/main/java/org/eclipse/jetty/test/openid/HomePage.java new file mode 100644 index 000000000000..5ecdb0aedff8 --- /dev/null +++ b/tests/test-webapps/test-openid-webapp/src/main/java/org/eclipse/jetty/test/openid/HomePage.java @@ -0,0 +1,45 @@ +// +// ======================================================================== +// 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.test.openid; + +import java.io.IOException; +import java.security.Principal; +import java.util.Map; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class HomePage extends HttpServlet +{ + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + response.setContentType("text/html"); + Principal userPrincipal = request.getUserPrincipal(); + if (userPrincipal != null) + { + @SuppressWarnings("unchecked") + Map userInfo = (Map)request.getSession().getAttribute("org.eclipse.jetty.security.openid.claims"); + response.getWriter().println("userId: " + userInfo.get("sub") + "
"); + response.getWriter().println("name: " + userInfo.get("name") + "
"); + response.getWriter().println("email: " + userInfo.get("email") + "
"); + response.getWriter().println("
Logout"); + } + else + { + response.getWriter().println("not authenticated"); + response.getWriter().println("
Login"); + } + } +} diff --git a/tests/test-webapps/test-openid-webapp/src/main/java/org/eclipse/jetty/test/openid/LoginPage.java b/tests/test-webapps/test-openid-webapp/src/main/java/org/eclipse/jetty/test/openid/LoginPage.java new file mode 100644 index 000000000000..0abfbb4eab8c --- /dev/null +++ b/tests/test-webapps/test-openid-webapp/src/main/java/org/eclipse/jetty/test/openid/LoginPage.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// 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.test.openid; + +import java.io.IOException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class LoginPage extends HttpServlet +{ + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + response.setContentType("text/html"); + response.getWriter().println("success"); + response.getWriter().println("
Home"); + } +} diff --git a/tests/test-webapps/test-openid-webapp/src/main/java/org/eclipse/jetty/test/openid/LogoutPage.java b/tests/test-webapps/test-openid-webapp/src/main/java/org/eclipse/jetty/test/openid/LogoutPage.java new file mode 100644 index 000000000000..13a6c044a6d7 --- /dev/null +++ b/tests/test-webapps/test-openid-webapp/src/main/java/org/eclipse/jetty/test/openid/LogoutPage.java @@ -0,0 +1,29 @@ +// +// ======================================================================== +// 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.test.openid; + +import java.io.IOException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class LogoutPage extends HttpServlet +{ + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + request.getSession().invalidate(); + response.sendRedirect(request.getContextPath()); + } +} diff --git a/tests/test-webapps/test-openid-webapp/src/main/webapp/WEB-INF/web.xml b/tests/test-webapps/test-openid-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000000..1464c57accaa --- /dev/null +++ b/tests/test-webapps/test-openid-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,86 @@ + + + + + OPENID + + + + AdminPage + org.eclipse.jetty.test.openid.AdminPage + + + AdminPage + /admin + + + + ErrorPage + org.eclipse.jetty.test.openid.ErrorPage + + + ErrorPage + /error + + + + HomePage + org.eclipse.jetty.test.openid.HomePage + + + HomePage + + + + + LoginPage + org.eclipse.jetty.test.openid.LoginPage + + + LoginPage + /login + + + + LogoutPage + org.eclipse.jetty.test.openid.LogoutPage + + + LogoutPage + /logout + + + + admin + + + ** + + + + + User Pages + /profile + /login + + + ** + + + + + + Admin Page + /admin + + + admin + + + +