Merge remote-tracking branch 'origin/jetty-12.0.x' into jetty-12.0.x-httpcontentFactoryCleanup

This commit is contained in:
Lachlan Roberts 2022-12-19 21:46:05 +11:00
commit 06f9e5ec18
45 changed files with 1143 additions and 1115 deletions

View File

@ -69,11 +69,11 @@ jobs:
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
- name: Set up Maven
uses: stCarolas/setup-maven@v4.5
with:
maven-version: 3.8.6
maven-version: 3.8.6
- name: Set up Maven
run:

View File

@ -4,4 +4,4 @@
extraction:
java:
index:
java_version: "11"
java_version: "17"

View File

@ -72,6 +72,92 @@ jetty-12.0.0.alpha3 - 07 December 2022
without .jar extension
+ 9006 WebSocket Message InputStream read() returns signed byte
jetty-11.0.13 - 07 December 2022
+ 7117 Timeout with Expect 100 continue when using ProxyServlet
+ 7286 WebSocket write can time out even if the frame / callback has not been
failed.
+ 7993 HttpClient idleTimeout configuration being ignored/overridden
+ 8330 Persistent OpenId sessions can throw IllegalStateException
+ 8460 Log or throw exception if DefaultSessionIdManager is used but has not
been started.
+ 8536 HotSwapHandler race condition
+ 8558 Idle timeout occured sometimes on HTTP/2 client with
`InputStreamResponseListener`
+ 8584 org.eclipse.jetty.client.HttpRequest.send() never returns
+ 8591 Indicate units of HttpClient properties
+ 8623 Use AutoLock in InputStreamResponseListener
+ 8628 Pseudo restore `PathMappings.getMatch(String)` for backwards compat
reasons
+ 8678 Jetty client is not responding to GO_AWAY packet received from (Jetty)
Server and continue to send traffic on same connection
+ 8695 Update quiche to 0.16.0
+ 8712 ELContextCleaner no longer needed.
+ 8716 Multiple Host header values handled poorly
+ 8721 jetty:effective-web-xml doesn't generate quickstart information for web
fragment jars that contain META-INF/resources
+ 8723 Provide a thread-safe way to modify HttpClient proxies at runtime
+ 8750 AbstractProxyServlet.onServerResponseHeaders does not support headers
with empty values
+ 8753 Starting HttpClient with destinationIdleTimeout set throws NPE.
+ 8770 Review whether to send request body in redirects
+ 8779 CompactPathRule drops query section on use
+ 8786 KeyStoreScanner is not able to monitor a symlink file and always
resolves to the target.
+ 8810 `ArrayRetainableByteBufferPool` inefficiently calculates bucket indices
+ 8811 HTTP/2 session shutdown race may cause `Server.stop()` to block until
stop timeout
+ 8863 Provide a possibility to name virtual threads
+ 8895 Generate downloadable version of javadocs documentation in website
deploy script
+ 8897 Update Conditional request handling for RFC7232
+ 8905 GzipHandler fails to set Vary header on 304 responses
+ 8913 Review Jetty XML syntax to allow calling JDK methods
+ 8942 Use Logback 1.3.x for Jetty 10.0.x
+ 9006 WebSocket Message InputStream read() returns signed byte
jetty-10.0.13 - 07 December 2022
+ 7117 Timeout with Expect 100 continue when using ProxyServlet
+ 7286 WebSocket write can time out even if the frame / callback has not been
failed.
+ 7993 HttpClient idleTimeout configuration being ignored/overridden
+ 8330 Persistent OpenId sessions can throw IllegalStateException
+ 8460 Log or throw exception if DefaultSessionIdManager is used but has not
been started.
+ 8536 HotSwapHandler race condition
+ 8558 Idle timeout occured sometimes on HTTP/2 client with
`InputStreamResponseListener`
+ 8584 org.eclipse.jetty.client.HttpRequest.send() never returns
+ 8591 Indicate units of HttpClient properties
+ 8623 Use AutoLock in InputStreamResponseListener
+ 8628 Pseudo restore `PathMappings.getMatch(String)` for backwards compat
reasons
+ 8678 Jetty client is not responding to GO_AWAY packet received from (Jetty)
Server and continue to send traffic on same connection
+ 8695 Update quiche to 0.16.0
+ 8712 ELContextCleaner no longer needed.
+ 8716 Multiple Host header values handled poorly
+ 8721 jetty:effective-web-xml doesn't generate quickstart information for web
fragment jars that contain META-INF/resources
+ 8723 Provide a thread-safe way to modify HttpClient proxies at runtime
+ 8750 AbstractProxyServlet.onServerResponseHeaders does not support headers
with empty values
+ 8753 Starting HttpClient with destinationIdleTimeout set throws NPE.
+ 8770 Review whether to send request body in redirects
+ 8779 CompactPathRule drops query section on use
+ 8786 KeyStoreScanner is not able to monitor a symlink file and always
resolves to the target.
+ 8810 `ArrayRetainableByteBufferPool` inefficiently calculates bucket indices
+ 8811 HTTP/2 session shutdown race may cause `Server.stop()` to block until
stop timeout
+ 8863 Provide a possibility to name virtual threads
+ 8895 Generate downloadable version of javadocs documentation in website
deploy script
+ 8897 Update Conditional request handling for RFC7232
+ 8905 GzipHandler fails to set Vary header on 304 responses
+ 8913 Review Jetty XML syntax to allow calling JDK methods
+ 8942 Use Logback 1.3.x for Jetty 10.0.x
+ 9006 WebSocket Message InputStream read() returns signed byte
jetty-12.0.0.alpha1 - 15 September 2022
+ 8474 Jetty 12 : Resource API Review
+ 8493 Review HTTP client feature `setRemoveIdleDestinations`

View File

@ -34,7 +34,6 @@ import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnOs;
@ -47,7 +46,6 @@ public class ConscryptHTTP2ClientTest
{
@Tag("external")
@Test
@Disabled("issue google/conscrypt#667")
public void testConscryptHTTP2Client() throws Exception
{
String host = "webtide.com";

View File

@ -23,7 +23,6 @@ import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http.HttpScheme;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
@ -31,7 +30,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
@Disabled
public class ExternalSiteTest
{
private HttpClient client;

View File

@ -165,7 +165,6 @@ public class BufferedResponseHandler extends Handler.Wrapper
}
}
// Install buffered interceptor and handle.
return (rq, rs, callback) ->
{
BufferedResponse bufferedResponse = new BufferedResponse(rq, rs, callback);

View File

@ -19,109 +19,118 @@ import org.eclipse.jetty.util.thread.AutoLock;
/**
* This specialized callback implements a pattern that allows
* a large job to be broken into smaller tasks using iteration
* rather than recursion.
* a large asynchronous task to be broken into smaller
* asynchronous sub-tasks using iteration rather than recursion.
* <p>
* A typical example is the write of a large content to a socket,
* divided in chunks. Chunk C1 is written by thread T1, which
* also invokes the callback, which writes chunk C2, which invokes
* the callback again, which writes chunk C3, and so forth.
* </p>
* <p>
* The problem with the example is that if the callback thread
* The problem with the example above is that if the callback thread
* is the same that performs the I/O operation, then the process
* is recursive and may result in a stack overflow.
* To avoid the stack overflow, a thread dispatch must be performed,
* causing context switching and cache misses, affecting performance.
* </p>
* <p>
* To avoid this issue, this callback uses an AtomicReference to
* record whether success callback has been called during the processing
* of a sub task, and if so then the processing iterates rather than
* recurring.
* </p>
* To avoid this issue, this callback atomically records whether
* the callback for an asynchronous sub-task has been called
* during the processing of the asynchronous sub-task, and if so
* then the processing of the large asynchronous task iterates
* rather than recursing.
* <p>
* Subclasses must implement method {@link #process()} where the sub
* task is executed and a suitable {@link IteratingCallback.Action} is
* returned to this callback to indicate the overall progress of the job.
* This callback is passed to the asynchronous execution of each sub
* task and a call the {@link #succeeded()} on this callback represents
* the completion of the sub task.
* </p>
* Subclasses must implement method {@link #process()} where the
* asynchronous sub-task is initiated and a suitable {@link Action}
* is returned to this callback to indicate the overall progress of
* the large asynchronous task.
* This callback is passed to the asynchronous sub-task, and a call
* to {@link #succeeded()} on this callback represents the successful
* completion of the asynchronous sub-task, while a call to
* {@link #failed(Throwable)} on this callback represents the
* completion with a failure of the large asynchronous task.
*/
public abstract class IteratingCallback implements Callback
{
/**
* The internal states of this callback
* The internal states of this callback.
*/
private enum State
{
/**
* This callback is IDLE, ready to iterate.
* This callback is idle, ready to iterate.
*/
IDLE,
/**
* This callback is iterating calls to {@link #process()} and is dealing with
* the returns. To get into processing state, it much of held the lock state
* and set iterating to true.
* This callback is just about to call {@link #process()},
* or within it, or just exited from it, either normally
* or by throwing.
*/
PROCESSING,
/**
* Waiting for a schedule callback
* Method {@link #process()} returned {@link Action#SCHEDULED}
* and this callback is waiting for the asynchronous sub-task
* to complete.
*/
PENDING,
/**
* Called by a schedule callback
* The asynchronous sub-task was completed successfully
* via a call to {@link #succeeded()} while in
* {@link #PROCESSING} state.
*/
CALLED,
/**
* The overall job has succeeded as indicated by a {@link Action#SUCCEEDED} return
* from {@link IteratingCallback#process()}
* The iteration terminated successfully as indicated by
* {@link Action#SUCCEEDED} returned from
* {@link IteratingCallback#process()}.
*/
SUCCEEDED,
/**
* The overall job has failed as indicated by a call to {@link IteratingCallback#failed(Throwable)}
* The iteration terminated with a failure via a call
* to {@link IteratingCallback#failed(Throwable)}.
*/
FAILED,
/**
* This callback has been closed and cannot be reset.
* This callback has been {@link #close() closed} and
* cannot be {@link #reset() reset}.
*/
CLOSED
}
/**
* The indication of the overall progress of the overall job that
* implementations of {@link #process()} must return.
* The indication of the overall progress of the iteration
* that implementations of {@link #process()} must return.
*/
protected enum Action
{
/**
* Indicates that {@link #process()} has no more work to do,
* but the overall job is not completed yet, probably waiting
* but the iteration is not completed yet, probably waiting
* for additional events to trigger more work.
*/
IDLE,
/**
* Indicates that {@link #process()} is executing asynchronously
* a sub task, where the execution has started but the callback
* Indicates that {@link #process()} has initiated an asynchronous
* sub-task, where the execution has started but the callback
* that signals the completion of the asynchronous sub-task
* may have not yet been invoked.
*/
SCHEDULED,
/**
* Indicates that {@link #process()} has completed the overall job.
* Indicates that {@link #process()} has completed the whole
* iteration successfully.
*/
SUCCEEDED
}
private final AutoLock _lock = new AutoLock();
private State _state;
private Throwable _failure;
private boolean _iterate;
protected IteratingCallback()
@ -135,11 +144,10 @@ public abstract class IteratingCallback implements Callback
}
/**
* Method called by {@link #iterate()} to process the sub task.
* Method called by {@link #iterate()} to process the asynchronous sub-task.
* <p>
* Implementations must start the asynchronous execution of the sub task
* Implementations must initiate the asynchronous execution of the sub-task
* (if any) and return an appropriate action:
* </p>
* <ul>
* <li>{@link Action#IDLE} when no sub tasks are available for execution
* but the overall job is not completed yet</li>
@ -149,7 +157,7 @@ public abstract class IteratingCallback implements Callback
* </ul>
*
* @return the appropriate Action
* @throws Throwable if the sub task processing throws
* @throws Throwable if the sub-task processing throws
*/
protected abstract Action process() throws Throwable;
@ -174,16 +182,18 @@ public abstract class IteratingCallback implements Callback
/**
* This method must be invoked by applications to start the processing
* of sub tasks. It can be called at any time by any thread, and it's
* contract is that when called, then the {@link #process()} method will
* be called during or soon after, either by the calling thread or by
* another thread.
* of asynchronous sub-tasks.
* <p>
* It can be called at any time by any thread, and its contract is that
* when called, then the {@link #process()} method will be called during
* or soon after, either by the calling thread or by another thread, but
* in either case by one thread only.
*/
public void iterate()
{
boolean process = false;
try (AutoLock lock = _lock.lock())
try (AutoLock ignored = _lock.lock())
{
switch (_state)
{
@ -219,14 +229,15 @@ public abstract class IteratingCallback implements Callback
// This should only ever be called when in processing state, however a failed or close call
// may happen concurrently, so state is not assumed.
boolean onCompleteSuccess = false;
boolean notifyCompleteSuccess = false;
Throwable notifyCompleteFailure = null;
// While we are processing
processing:
while (true)
{
// Call process to get the action that we have to take.
Action action;
Action action = null;
try
{
action = process();
@ -234,52 +245,53 @@ public abstract class IteratingCallback implements Callback
catch (Throwable x)
{
failed(x);
break;
// Fall through to possibly invoke onCompleteFailure().
}
// acted on the action we have just received
try (AutoLock lock = _lock.lock())
try (AutoLock ignored = _lock.lock())
{
switch (_state)
{
case PROCESSING:
{
switch (action)
if (action != null)
{
case IDLE:
switch (action)
{
// Has iterate been called while we were processing?
if (_iterate)
case IDLE:
{
// yes, so skip idle and keep processing
_iterate = false;
_state = State.PROCESSING;
continue processing;
// Has iterate been called while we were processing?
if (_iterate)
{
// yes, so skip idle and keep processing
_iterate = false;
continue;
}
// No, so we can go idle
_state = State.IDLE;
break processing;
}
case SCHEDULED:
{
// we won the race against the callback, so the callback has to process and we can break processing
_state = State.PENDING;
break processing;
}
case SUCCEEDED:
{
// we lost the race against the callback,
_iterate = false;
_state = State.SUCCEEDED;
notifyCompleteSuccess = true;
break processing;
}
default:
{
break;
}
// No, so we can go idle
_state = State.IDLE;
break processing;
}
case SCHEDULED:
{
// we won the race against the callback, so the callback has to process and we can break processing
_state = State.PENDING;
break processing;
}
case SUCCEEDED:
{
// we lost the race against the callback,
_iterate = false;
_state = State.SUCCEEDED;
onCompleteSuccess = true;
break processing;
}
default:
break;
}
throw new IllegalStateException(String.format("%s[action=%s]", this, action));
}
@ -290,12 +302,16 @@ public abstract class IteratingCallback implements Callback
throw new IllegalStateException(String.format("%s[action=%s]", this, action));
// we lost the race, so we have to keep processing
_state = State.PROCESSING;
continue processing;
continue;
}
case SUCCEEDED:
case FAILED:
case CLOSED:
notifyCompleteFailure = _failure;
_failure = null;
break processing;
case SUCCEEDED:
break processing;
case IDLE:
@ -306,20 +322,23 @@ public abstract class IteratingCallback implements Callback
}
}
if (onCompleteSuccess)
if (notifyCompleteSuccess)
onCompleteSuccess();
else if (notifyCompleteFailure != null)
onCompleteFailure(notifyCompleteFailure);
}
/**
* Invoked when the sub task succeeds.
* Subclasses that override this method must always remember to call
* {@code super.succeeded()}.
* Method to invoke when the asynchronous sub-task succeeds.
* <p>
* Subclasses that override this method must always remember
* to call {@code super.succeeded()}.
*/
@Override
public void succeeded()
{
boolean process = false;
try (AutoLock lock = _lock.lock())
try (AutoLock ignored = _lock.lock())
{
switch (_state)
{
@ -351,15 +370,24 @@ public abstract class IteratingCallback implements Callback
}
/**
* Invoked when the sub task fails.
* Subclasses that override this method must always remember to call
* {@code super.failed(Throwable)}.
* Method to invoke when the asynchronous sub-task fails,
* or to fail the overall asynchronous task and therefore
* terminate the iteration.
* <p>
* Subclasses that override this method must always remember
* to call {@code super.failed(Throwable)}.
* <p>
* Eventually, {@link #onCompleteFailure(Throwable)} is
* called, either by the caller thread or by the processing
* thread.
*
* @see #isFailed()
*/
@Override
public void failed(Throwable x)
{
boolean failure = false;
try (AutoLock lock = _lock.lock())
try (AutoLock ignored = _lock.lock())
{
switch (_state)
{
@ -370,12 +398,15 @@ public abstract class IteratingCallback implements Callback
case CALLED:
// too late!.
break;
case PENDING:
{
failure = true;
break;
}
case PROCESSING:
{
_state = State.FAILED;
failure = true;
_failure = x;
break;
}
default:
@ -386,10 +417,19 @@ public abstract class IteratingCallback implements Callback
onCompleteFailure(x);
}
/**
* Method to invoke to forbid further invocations to {@link #iterate()}
* and {@link #reset()}.
* <p>
* When this method is invoked during processing, it behaves like invoking
* {@link #failed(Throwable)}.
*
* @see #isClosed()
*/
public void close()
{
String failure = null;
try (AutoLock lock = _lock.lock())
try (AutoLock ignored = _lock.lock())
{
switch (_state)
{
@ -399,12 +439,18 @@ public abstract class IteratingCallback implements Callback
_state = State.CLOSED;
break;
case PROCESSING:
_failure = new IOException(String.format("Close %s in state %s", this, _state));
_state = State.CLOSED;
break;
case CLOSED:
break;
default:
failure = String.format("Close %s in state %s", this, _state);
_state = State.CLOSED;
break;
}
}
@ -412,43 +458,47 @@ public abstract class IteratingCallback implements Callback
onCompleteFailure(new IOException(failure));
}
/*
* only for testing
* @return whether this callback is idle and {@link #iterate()} needs to be called
/**
* @return whether this callback is idle, and {@link #iterate()} needs to be called
*/
boolean isIdle()
{
try (AutoLock lock = _lock.lock())
try (AutoLock ignored = _lock.lock())
{
return _state == State.IDLE;
}
}
/**
* @return whether this callback has been {@link #close() closed}
*/
public boolean isClosed()
{
try (AutoLock lock = _lock.lock())
try (AutoLock ignored = _lock.lock())
{
return _state == State.CLOSED;
}
}
/**
* @return whether this callback has failed
* @return whether this callback has been {@link #failed(Throwable) failed}
*/
public boolean isFailed()
{
try (AutoLock lock = _lock.lock())
try (AutoLock ignored = _lock.lock())
{
return _state == State.FAILED;
}
}
/**
* @return whether this callback has succeeded
* @return whether this callback and the overall asynchronous task has been succeeded
*
* @see #onCompleteSuccess()
*/
public boolean isSucceeded()
{
try (AutoLock lock = _lock.lock())
try (AutoLock ignored = _lock.lock())
{
return _state == State.SUCCEEDED;
}
@ -457,15 +507,15 @@ public abstract class IteratingCallback implements Callback
/**
* Resets this callback.
* <p>
* A callback can only be reset to IDLE from the
* SUCCEEDED or FAILED states or if it is already IDLE.
* </p>
* A callback can only be reset to the idle state from the
* {@link #isSucceeded() succeeded} or {@link #isFailed() failed} states
* or if it is already idle.
*
* @return true if the reset was successful
*/
public boolean reset()
{
try (AutoLock lock = _lock.lock())
try (AutoLock ignored = _lock.lock())
{
switch (_state)
{
@ -474,8 +524,9 @@ public abstract class IteratingCallback implements Callback
case SUCCEEDED:
case FAILED:
_iterate = false;
_state = State.IDLE;
_failure = null;
_iterate = false;
return true;
default:

View File

@ -0,0 +1 @@
invoker.goals = verify

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty.ee10.its.jetty-start-overlay-it</groupId>
<artifactId>jetty-simple-project</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>jetty-simple-base-webapp</artifactId>
<packaging>war</packaging>
<name>Jetty :: Simple :: Base Webapp</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,5 @@
<html>
<body>
<h1>Simple Base Webapp Index</h1>
</body>
</html>

View File

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty.ee10.its.jetty-start-overlay-it</groupId>
<artifactId>jetty-simple-project</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>jetty-simple-webapp</artifactId>
<packaging>war</packaging>
<name>Jetty :: Simple :: Webapp</name>
<properties>
<jetty.port.file>${project.build.directory}/jetty-start-port.txt</jetty.port.file>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.ee10.its.jetty-start-overlay-it</groupId>
<artifactId>jetty-simple-base-webapp</artifactId>
<type>war</type>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<artifactId>jetty-ee10-servlet</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<artifactId>jetty-ee10-maven-plugin</artifactId>
<classifier>tests</classifier>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-client</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<overlays>
<overlay>
<groupId>org.eclipse.jetty.ee10.its.jetty-start-overlay-it</groupId>
<artifactId>jetty-simple-base-webapp</artifactId>
<includes>
<include>index.html</include>
</includes>
</overlay>
<overlay/>
</overlays>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>run-tests</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<includes>
<include>IntegrationTest*.java</include>
</includes>
<systemPropertyVariables>
<jetty.port.file>${jetty.port.file}</jetty.port.file>
<context.path>/setbycontextxml</context.path>
<contentCheck>Simple Base Webapp Index</contentCheck>
<pathToCheck>/index.html</pathToCheck>
<maven.it.name>${project.groupId}:${project.artifactId}</maven.it.name>
</systemPropertyVariables>
<dependenciesToScan>
<dependency>org.eclipse.jetty.ee10:jetty-ee10-maven-plugin</dependency>
</dependenciesToScan>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.eclipse.jetty.ee10</groupId>
<artifactId>jetty-ee10-maven-plugin</artifactId>
<executions>
<execution>
<id>start-jetty</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
<configuration>
<contextXml>${basedir}/src/config/context.xml</contextXml>
<systemProperties>
<jetty.port.file>${jetty.port.file}</jetty.port.file>
<jetty.deployMode>EMBED</jetty.deployMode>
</systemProperties>
<jettyXmls>
<jettyXml>${basedir}/src/config/jetty.xml</jettyXml>
</jettyXmls>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext">
<Set name="contextPath">/setbycontextxml</Set>
</Configure>

View File

@ -0,0 +1,40 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
<Set name="secureScheme">https</Set>
<Set name="securePort"><Property name="jetty.secure.port" default="8443" /></Set>
<Set name="outputBufferSize">32768</Set>
<Set name="requestHeaderSize">8192</Set>
<Set name="responseHeaderSize">8192</Set>
<Set name="headerCacheSize">1024</Set>
</New>
<Call name="addConnector">
<Arg>
<New class="org.eclipse.jetty.server.ServerConnector">
<Arg name="server"><Ref refid="Server" /></Arg>
<Arg name="factories">
<Array type="org.eclipse.jetty.server.ConnectionFactory">
<Item>
<New class="org.eclipse.jetty.server.HttpConnectionFactory">
<Arg name="config"><Ref refid="httpConfig" /></Arg>
</New>
</Item>
</Array>
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.maven.plugin.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>
</Call>
<Set name="host" property="jetty.host"/>
<Set name="port" property="jetty.port"/>
<Set name="idleTimeout">30000</Set>
</New>
</Arg>
</Call>
</Configure>

View File

@ -0,0 +1,5 @@
<html>
<body>
<h1>Simple WebApp Index</h1>
</body>
</html>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty.ee10.its</groupId>
<artifactId>it-parent-pom</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jetty.ee10.its.jetty-start-overlay-it</groupId>
<artifactId>jetty-simple-project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Jetty :: Simple Overlay</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<jetty.version>@project.version@</jetty.version>
</properties>
<modules>
<module>jetty-simple-base-webapp</module>
<module>jetty-simple-webapp</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.ee10.its.jetty-start-overlay-it</groupId>
<artifactId>jetty-simple-base-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@ -0,0 +1,21 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
File buildLog = new File( basedir, 'build.log' )
assert buildLog.text.contains( 'Started Server' )
assert buildLog.text.contains( 'Running org.eclipse.jetty.ee10.maven.plugin.it.IntegrationTestGetContent')

View File

@ -105,6 +105,8 @@ public class MavenProjectHelper
*/
public MavenProject getMavenProjectFor(Artifact artifact)
{
if (artifact == null)
return null;
return artifactToReactorProjectMap.get(artifact.getId());
}

View File

@ -13,7 +13,6 @@
package org.eclipse.jetty.ee10.servlet;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
@ -21,10 +20,8 @@ import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.Trailers;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.NanoTime;
import org.eclipse.jetty.util.StaticException;
import org.eclipse.jetty.util.component.Destroyable;
import org.eclipse.jetty.util.thread.AutoLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -40,12 +37,9 @@ class AsyncContentProducer implements ContentProducer
final AutoLock _lock;
private final ServletChannel _servletChannel;
private HttpInput.Interceptor _interceptor;
private Content.Chunk _rawChunk;
private Content.Chunk _transformedChunk;
private boolean _error;
private Content.Chunk _chunk;
private long _firstByteNanoTime = Long.MIN_VALUE;
private long _rawBytesArrived;
private long _bytesArrived;
/**
* @param servletChannel The ServletChannel to produce input from.
@ -66,19 +60,10 @@ class AsyncContentProducer implements ContentProducer
// Make sure that the chunk has been fully consumed before destroying the interceptor and also make sure
// that asking this instance for chunks between recycle and reopen will only produce error'ed chunks.
if (_rawChunk == null)
_rawChunk = RECYCLED_ERROR_CHUNK;
else if (!_rawChunk.isTerminal())
throw new IllegalStateException("ContentProducer with unconsumed raw chunk cannot be recycled");
if (_transformedChunk == null)
_transformedChunk = RECYCLED_ERROR_CHUNK;
else if (!_transformedChunk.isTerminal())
throw new IllegalStateException("ContentProducer with unconsumed transformed chunk cannot be recycled");
if (_interceptor instanceof Destroyable)
((Destroyable)_interceptor).destroy();
_interceptor = null;
if (_chunk == null)
_chunk = RECYCLED_ERROR_CHUNK;
else if (!_chunk.isTerminal())
throw new IllegalStateException("ContentProducer with unconsumed chunk cannot be recycled");
}
@Override
@ -87,32 +72,16 @@ class AsyncContentProducer implements ContentProducer
assertLocked();
if (LOG.isDebugEnabled())
LOG.debug("reopening {}", this);
_rawChunk = null;
_transformedChunk = null;
_error = false;
_chunk = null;
_firstByteNanoTime = Long.MIN_VALUE;
_rawBytesArrived = 0L;
}
@Override
public HttpInput.Interceptor getInterceptor()
{
assertLocked();
return _interceptor;
}
@Override
public void setInterceptor(HttpInput.Interceptor interceptor)
{
assertLocked();
this._interceptor = interceptor;
_bytesArrived = 0L;
}
@Override
public int available()
{
assertLocked();
Content.Chunk chunk = nextTransformedChunk();
Content.Chunk chunk = produceChunk();
int available = chunk == null ? 0 : chunk.remaining();
if (LOG.isDebugEnabled())
LOG.debug("available = {} {}", available, this);
@ -123,7 +92,7 @@ class AsyncContentProducer implements ContentProducer
public boolean hasChunk()
{
assertLocked();
boolean hasChunk = _rawChunk != null;
boolean hasChunk = _chunk != null;
if (LOG.isDebugEnabled())
LOG.debug("hasChunk = {} {}", hasChunk, this);
return hasChunk;
@ -133,9 +102,10 @@ class AsyncContentProducer implements ContentProducer
public boolean isError()
{
assertLocked();
boolean error = _chunk instanceof Content.Chunk.Error;
if (LOG.isDebugEnabled())
LOG.debug("isError = {} {}", _error, this);
return _error;
LOG.debug("isError = {} {}", error, this);
return error;
}
@Override
@ -151,7 +121,7 @@ class AsyncContentProducer implements ContentProducer
if (period > 0)
{
long minimumData = minRequestDataRate * TimeUnit.NANOSECONDS.toMillis(period) / TimeUnit.SECONDS.toMillis(1);
if (getRawBytesArrived() < minimumData)
if (getBytesArrived() < minimumData)
{
if (LOG.isDebugEnabled())
LOG.debug("checkMinDataRate check failed {}", this);
@ -171,12 +141,12 @@ class AsyncContentProducer implements ContentProducer
}
@Override
public long getRawBytesArrived()
public long getBytesArrived()
{
assertLocked();
if (LOG.isDebugEnabled())
LOG.debug("getRawBytesArrived = {} {}", _rawBytesArrived, this);
return _rawBytesArrived;
LOG.debug("getBytesArrived = {} {}", _bytesArrived, this);
return _bytesArrived;
}
@Override
@ -198,28 +168,16 @@ class AsyncContentProducer implements ContentProducer
private boolean consumeCurrentChunk()
{
if (_transformedChunk != null && !_transformedChunk.isTerminal())
{
if (_transformedChunk != _rawChunk)
{
if (LOG.isDebugEnabled())
LOG.debug("releasing current transformed chunk {}", this);
_transformedChunk.skip(_transformedChunk.remaining());
_transformedChunk.release();
}
_transformedChunk = null;
}
if (_rawChunk != null && !_rawChunk.isTerminal())
if (_chunk != null && !_chunk.isTerminal())
{
if (LOG.isDebugEnabled())
LOG.debug("releasing current raw chunk {}", this);
_rawChunk.skip(_rawChunk.remaining());
_rawChunk.release();
_rawChunk = _rawChunk.isLast() ? Content.Chunk.EOF : null;
LOG.debug("releasing current chunk {}", this);
_chunk.skip(_chunk.remaining());
_chunk.release();
_chunk = _chunk.isLast() ? Content.Chunk.EOF : null;
}
return _rawChunk != null && _rawChunk.isLast();
return _chunk != null && _chunk.isLast();
}
private boolean consumeAvailableChunks()
@ -251,7 +209,7 @@ class AsyncContentProducer implements ContentProducer
public Content.Chunk nextChunk()
{
assertLocked();
Content.Chunk chunk = nextTransformedChunk();
Content.Chunk chunk = produceChunk();
if (LOG.isDebugEnabled())
LOG.debug("nextChunk = {} {}", chunk, this);
if (chunk != null)
@ -265,24 +223,20 @@ class AsyncContentProducer implements ContentProducer
assertLocked();
if (LOG.isDebugEnabled())
LOG.debug("reclaim {} {}", chunk, this);
if (_transformedChunk == chunk)
{
chunk.release();
if (_transformedChunk == _rawChunk)
_rawChunk = null;
_transformedChunk = null;
}
assert chunk == _chunk;
chunk.release();
_chunk = null;
}
@Override
public boolean isReady()
{
assertLocked();
Content.Chunk chunk = nextTransformedChunk();
Content.Chunk chunk = produceChunk();
if (chunk != null)
{
if (LOG.isDebugEnabled())
LOG.debug("isReady(), got transformed chunk {} {}", chunk, this);
LOG.debug("isReady(), got chunk {} {}", chunk, this);
return true;
}
@ -303,197 +257,71 @@ class AsyncContentProducer implements ContentProducer
return _servletChannel.getState().isInputUnready();
}
private Content.Chunk nextTransformedChunk()
private Content.Chunk produceChunk()
{
if (LOG.isDebugEnabled())
LOG.debug("nextTransformedChunk {}", this);
LOG.debug("produceChunk() {}", this);
while (true)
{
if (_transformedChunk != null)
if (_chunk != null)
{
if (_transformedChunk.isTerminal() || _transformedChunk.hasRemaining())
if (_chunk.isTerminal() || _chunk.hasRemaining())
{
if (_transformedChunk instanceof Content.Chunk.Error && !_error)
{
// In case the _rawChunk was set by consumeAvailable(), check the ServletChannel
// to see if it has a more precise error. Otherwise, the exact same
// terminal chunk will be returned by the ServletChannel; do not do that
// if the _error flag was set, meaning the current error is definitive.
Content.Chunk refreshedRawChunk = produceRawChunk();
if (refreshedRawChunk != null)
_rawChunk = _transformedChunk = refreshedRawChunk;
_error = _rawChunk instanceof Content.Chunk.Error;
if (LOG.isDebugEnabled())
LOG.debug("refreshed raw chunk: {} {}", _rawChunk, this);
}
if (LOG.isDebugEnabled())
LOG.debug("transformed chunk not yet depleted, returning it {}", this);
return _transformedChunk;
LOG.debug("chunk not yet depleted, returning it {}", this);
return _chunk;
}
else
{
if (LOG.isDebugEnabled())
LOG.debug("current transformed chunk depleted {}", this);
LOG.debug("current chunk depleted {}", this);
_transformedChunk.release();
_transformedChunk = null;
_chunk.release();
_chunk = null;
}
}
if (_rawChunk == null)
else
{
if (LOG.isDebugEnabled())
LOG.debug("producing new raw chunk {}", this);
_rawChunk = produceRawChunk();
if (_rawChunk == null)
LOG.debug("reading new chunk {}", this);
_chunk = readChunk();
if (_chunk == null)
{
if (LOG.isDebugEnabled())
LOG.debug("channel has no new raw chunk {}", this);
LOG.debug("channel has no new chunk {}", this);
return null;
}
}
if (LOG.isDebugEnabled())
LOG.debug("transforming raw chunk {}", this);
transformRawChunk();
assert _chunk != null;
// Release the chunk immediately, if it is empty.
if (!_chunk.hasRemaining() && !_chunk.isTerminal())
{
if (LOG.isDebugEnabled())
LOG.debug("releasing empty chunk {}", this);
_chunk.release();
_chunk = null;
}
}
}
private void transformRawChunk()
{
assert _rawChunk != null;
if (_interceptor != null)
{
if (LOG.isDebugEnabled())
LOG.debug("intercepting raw chunk {}", this);
_transformedChunk = intercept();
// If the interceptor generated a terminal chunk, _rawChunk must become that terminal chunk.
if (_transformedChunk != null && _transformedChunk.isTerminal() && _transformedChunk != _rawChunk)
{
if (LOG.isDebugEnabled())
LOG.debug("interceptor generated a terminal chunk, _rawChunk must become that terminal chunk {}", this);
_rawChunk.release();
_rawChunk = _transformedChunk;
return;
}
// If the interceptor generated a null chunk, release the raw chunk now if it is empty.
if (_transformedChunk == null && !_rawChunk.hasRemaining() && !_rawChunk.isTerminal())
{
if (LOG.isDebugEnabled())
LOG.debug("interceptor generated a null chunk, releasing the empty raw chunk now {}", this);
_rawChunk.release();
_rawChunk = null;
return;
}
// If the interceptor returned the raw chunk, release the raw chunk now if it is empty.
if (_transformedChunk == _rawChunk && !_rawChunk.hasRemaining() && !_rawChunk.isTerminal())
{
if (LOG.isDebugEnabled())
LOG.debug("interceptor returned the raw chunk, releasing the empty raw chunk now {}", this);
_rawChunk.release();
_rawChunk = _transformedChunk = null;
}
}
else
{
// Release the raw chunk now if it is empty.
if (!_rawChunk.hasRemaining() && !_rawChunk.isTerminal())
{
if (LOG.isDebugEnabled())
LOG.debug("releasing the empty raw chunk now {}", this);
_rawChunk.release();
_rawChunk = null;
}
if (LOG.isDebugEnabled())
LOG.debug("no interceptor, transformed chunk is raw chunk {}", this);
_transformedChunk = _rawChunk;
}
}
private Content.Chunk intercept()
{
try
{
int remainingBeforeInterception = _rawChunk.remaining();
Content.Chunk chunk = _interceptor.readFrom(_rawChunk);
if (chunk != null && chunk.isTerminal() && !_rawChunk.isTerminal())
{
if (chunk instanceof Content.Chunk.Error errorChunk)
{
// Set the _error flag to mark the chunk as definitive, i.e.:
// do not try to produce new raw chunk to get a fresher error
// when the terminal chunk was generated by the interceptor.
_error = true;
if (_servletChannel.getResponse().isCommitted())
_servletChannel.abort(errorChunk.getCause());
}
if (LOG.isDebugEnabled())
LOG.debug("interceptor generated terminal chunk {}", this);
}
else if (chunk != _rawChunk && !_rawChunk.isTerminal() && _rawChunk.hasRemaining() && _rawChunk.remaining() == remainingBeforeInterception)
{
IOException failure = new IOException("Interceptor " + _interceptor + " did not consume any of the " + _rawChunk.remaining() + " remaining byte(s) of chunk");
if (chunk != null)
chunk.release();
consumeCurrentChunk();
// Set the _error flag to mark the chunk as definitive, i.e.:
// do not try to produce new raw chunk to get a fresher error
// when the terminal chunk was caused by the interceptor not
// consuming the raw chunk.
_error = true;
Response response = _servletChannel.getResponse();
if (response.isCommitted())
_servletChannel.abort(failure);
if (LOG.isDebugEnabled())
LOG.debug("interceptor did not consume chunk {}", this);
chunk = _transformedChunk;
}
if (LOG.isDebugEnabled())
LOG.debug("intercepted raw chunk {}", this);
return chunk;
}
catch (Throwable x)
{
IOException failure = new IOException("bad chunk", x);
consumeCurrentChunk();
// Set the _error flag to mark the chunk as definitive, i.e.:
// do not try to produce new raw chunk to get a fresher error
// when the terminal chunk was caused by the interceptor throwing.
_error = true;
Response response = _servletChannel.getResponse();
if (response.isCommitted())
_servletChannel.abort(failure);
if (LOG.isDebugEnabled())
LOG.debug("interceptor threw exception {}", this, x);
return _transformedChunk;
}
}
private Content.Chunk produceRawChunk()
private Content.Chunk readChunk()
{
Content.Chunk chunk = _servletChannel.getServletContextRequest().read();
if (chunk != null)
{
_rawBytesArrived += chunk.remaining();
_bytesArrived += chunk.remaining();
if (_firstByteNanoTime == Long.MIN_VALUE)
_firstByteNanoTime = NanoTime.now();
if (LOG.isDebugEnabled())
LOG.debug("produceRawChunk updated _rawBytesArrived to {} and _firstByteTimeStamp to {} {}", _rawBytesArrived, _firstByteNanoTime, this);
LOG.debug("readChunk() updated _bytesArrived to {} and _firstByteTimeStamp to {} {}", _bytesArrived, _firstByteNanoTime, this);
// TODO: notify channel listeners (see ee9)?
if (chunk instanceof Trailers trailers)
_servletChannel.onTrailers(trailers.getTrailers());
}
if (LOG.isDebugEnabled())
LOG.debug("produceRawChunk produced {} {}", chunk, this);
LOG.debug("readChunk() produced {} {}", chunk, this);
return chunk;
}
@ -506,13 +334,10 @@ class AsyncContentProducer implements ContentProducer
@Override
public String toString()
{
return String.format("%s@%x[r=%s,t=%s,i=%s,error=%b]",
return String.format("%s@%x[c=%s]",
getClass().getSimpleName(),
hashCode(),
_rawChunk,
_transformedChunk,
_interceptor,
_error
_chunk
);
}

View File

@ -79,9 +79,9 @@ class BlockingContentProducer implements ContentProducer
}
@Override
public long getRawBytesArrived()
public long getBytesArrived()
{
return _asyncContentProducer.getRawBytesArrived();
return _asyncContentProducer.getBytesArrived();
}
@Override
@ -140,18 +140,6 @@ class BlockingContentProducer implements ContentProducer
return ready;
}
@Override
public HttpInput.Interceptor getInterceptor()
{
return _asyncContentProducer.getInterceptor();
}
@Override
public void setInterceptor(HttpInput.Interceptor interceptor)
{
_asyncContentProducer.setInterceptor(interceptor);
}
@Override
public boolean onContentProducible()
{

View File

@ -14,7 +14,6 @@
package org.eclipse.jetty.ee10.servlet;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.util.component.Destroyable;
/**
* ContentProducer is the bridge between {@link HttpInput} and {@link Content.Source}.
@ -22,7 +21,6 @@ import org.eclipse.jetty.util.component.Destroyable;
public interface ContentProducer
{
/**
* Clear the interceptor and call {@link Destroyable#destroy()} on it if it implements {@link Destroyable}.
* A recycled {@link ContentProducer} will only produce special content with a non-null error until
* {@link #reopen()} is called.
*/
@ -57,7 +55,7 @@ public interface ContentProducer
* Doesn't change state.
* @return the byte count produced by the underlying {@link Content.Source}.
*/
long getRawBytesArrived();
long getBytesArrived();
/**
* Get the byte count that can immediately be read from this
@ -93,8 +91,6 @@ public interface ContentProducer
* Get the next content chunk that can be read from or that describes the terminal condition
* that was reached (error, eof).
* This call may or may not block until some content is available, depending on the implementation.
* The returned content is decoded by the interceptor set with {@link #setInterceptor(HttpInput.Interceptor)}
* or left as-is if no intercept is set.
* After this call, state can be either of UNREADY or IDLE.
*
* @return the next content chunk that can be read from or null if the implementation does not block
@ -118,18 +114,6 @@ public interface ContentProducer
*/
boolean isReady();
/**
* Get the {@link HttpInput.Interceptor}.
* @return The {@link HttpInput.Interceptor}, or null if none set.
*/
HttpInput.Interceptor getInterceptor();
/**
* Set the interceptor.
* @param interceptor The interceptor to use.
*/
void setInterceptor(HttpInput.Interceptor interceptor);
/**
* Wake up the thread that is waiting for the next content.
* After this call, state can be READY.

View File

@ -23,8 +23,6 @@ import jakarta.servlet.ServletInputStream;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.server.Context;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.component.Destroyable;
import org.eclipse.jetty.util.thread.AutoLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -85,59 +83,6 @@ public class HttpInput extends ServletInputStream implements Runnable
}
}
/**
* @return The current Interceptor, or null if none set
*/
public Interceptor getInterceptor()
{
try (AutoLock lock = _lock.lock())
{
return _contentProducer.getInterceptor();
}
}
/**
* Set the interceptor.
*
* @param interceptor The interceptor to use.
*/
public void setInterceptor(Interceptor interceptor)
{
try (AutoLock lock = _lock.lock())
{
if (LOG.isDebugEnabled())
LOG.debug("setting interceptor to {} on {}", interceptor, this);
_contentProducer.setInterceptor(interceptor);
}
}
/**
* Set the {@link Interceptor}, chaining it to the existing one if
* an {@link Interceptor} is already set.
*
* @param interceptor the next {@link Interceptor} in a chain
*/
public void addInterceptor(Interceptor interceptor)
{
try (AutoLock lock = _lock.lock())
{
Interceptor currentInterceptor = _contentProducer.getInterceptor();
if (currentInterceptor == null)
{
if (LOG.isDebugEnabled())
LOG.debug("adding single interceptor: {} on {}", interceptor, this);
_contentProducer.setInterceptor(interceptor);
}
else
{
ChainedInterceptor chainedInterceptor = new ChainedInterceptor(currentInterceptor, interceptor);
if (LOG.isDebugEnabled())
LOG.debug("adding chained interceptor: {} on {}", chainedInterceptor, this);
_contentProducer.setInterceptor(chainedInterceptor);
}
}
}
private int get(Content.Chunk chunk, byte[] bytes, int offset, int length)
{
length = Math.min(chunk.remaining(), length);
@ -175,7 +120,7 @@ public class HttpInput extends ServletInputStream implements Runnable
{
try (AutoLock lock = _lock.lock())
{
return _contentProducer.getRawBytesArrived();
return _contentProducer.getBytesArrived();
}
}
@ -450,111 +395,4 @@ public class HttpInput extends ServletInputStream implements Runnable
" cp=" + _contentProducer +
" eof=" + _consumedEof;
}
/**
* <p>{@link Content.Chunk} interceptor that can be registered using {@link #setInterceptor(Interceptor)} or
* {@link #addInterceptor(Interceptor)}.
* When {@link Content.Chunk} instances are generated, they are passed to the registered interceptor (if any)
* that is then responsible for providing the actual content that is consumed by {@link #read(byte[], int, int)} and its
* sibling methods.</p>
* A minimal implementation could be as simple as:
* <pre>
* public HttpInput.Content readFrom(HttpInput.Content content)
* {
* LOGGER.debug("read content: {}", asString(content));
* return content;
* }
* </pre>
* which would not do anything with the content besides logging it. A more involved implementation could look like the
* following:
* <pre>
* public HttpInput.Content readFrom(HttpInput.Content content)
* {
* if (content.hasContent())
* this.processedContent = processContent(content.getByteBuffer());
* if (content.isEof())
* disposeResources();
* return content.isSpecial() ? content : this.processedContent;
* }
* </pre>
* Implementors of this interface must keep the following in mind:
* <ul>
* <li>Calling {@link Content.Chunk#getByteBuffer()} when {@link Content.Chunk#isTerminal()} returns <code>true</code> throws
* {@link IllegalStateException}.</li>
* <li>A {@link Content.Chunk} can both be non-special and have {@code content == Content.EOF} return <code>true</code>.</li>
* <li>{@link Content.Chunk} extends {@link Callback} to manage the lifecycle of the contained byte buffer. The code calling
* {@link #readFrom(Content.Chunk)} is responsible for managing the lifecycle of both the passed and the returned content
* instances, once {@link ByteBuffer#hasRemaining()} returns <code>false</code> {@code HttpInput} will make sure
* {@link Callback#succeeded()} is called, or {@link Callback#failed(Throwable)} if an error occurs.</li>
* <li>After {@link #readFrom(Content.Chunk)} is called for the first time, subsequent {@link #readFrom(Content.Chunk)} calls will
* occur only after the contained byte buffer is empty (see above) or at any time if the returned content was special.</li>
* <li>Once {@link #readFrom(Content.Chunk)} returned a special content, subsequent calls to {@link #readFrom(Content.Chunk)} must
* always return the same special content.</li>
* <li>Implementations implementing both this interface and {@link Destroyable} will have their
* {@link Destroyable#destroy()} method called when {@link #recycle()} is called.</li>
* </ul>
*/
public interface Interceptor
{
/**
* @param content The content to be intercepted.
* The content will be modified with any data the interceptor consumes. There is no requirement
* that all the data is consumed by the interceptor but at least one byte must be consumed
* unless the returned content is the passed content instance.
* @return The intercepted content or null if interception is completed for that content.
*/
Content.Chunk readFrom(Content.Chunk content);
}
/**
* An {@link Interceptor} that chains two other {@link Interceptor}s together.
* The {@link Interceptor#readFrom(Content.Chunk)} calls the previous {@link Interceptor}'s
* {@link Interceptor#readFrom(Content.Chunk)} and then passes any {@link Content.Chunk} returned
* to the next {@link Interceptor}.
*/
private static class ChainedInterceptor implements Interceptor, Destroyable
{
private final Interceptor _prev;
private final Interceptor _next;
ChainedInterceptor(Interceptor prev, Interceptor next)
{
_prev = prev;
_next = next;
}
Interceptor getPrev()
{
return _prev;
}
Interceptor getNext()
{
return _next;
}
@Override
public Content.Chunk readFrom(Content.Chunk content)
{
Content.Chunk c = getPrev().readFrom(content);
if (c == null)
return null;
return getNext().readFrom(c);
}
@Override
public void destroy()
{
if (_prev instanceof Destroyable)
((Destroyable)_prev).destroy();
if (_next instanceof Destroyable)
((Destroyable)_next).destroy();
}
@Override
public String toString()
{
return getClass().getSimpleName() + "@" + hashCode() + " [p=" + _prev + ",n=" + _next + "]";
}
}
}

View File

@ -122,75 +122,6 @@ public class HttpOutput extends ServletOutputStream implements Runnable
UNREADY, // write operating in progress, isReady has returned false
}
/**
* The HttpOutput.Interceptor is a single intercept point for all
* output written to the HttpOutput: via writer; via output stream;
* asynchronously; or blocking.
* <p>
* The Interceptor can be used to implement translations (eg Gzip) or
* additional buffering that acts on all output. Interceptors are
* created in a chain, so that multiple concerns may intercept.
* <p>
* The {@link DefaultInterceptor} is always the
* last link in any Interceptor chain.
* <p>
* Responses are committed by the first call to
* {@link #write(ByteBuffer, boolean, Callback)}
* and closed by a call to {@link #write(ByteBuffer, boolean, Callback)}
* with the last boolean set true. If no content is available to commit
* or close, then a null buffer is passed.
*/
public interface Interceptor
{
/**
* Write content.
* The response is committed by the first call to write and is closed by
* a call with last == true. Empty content buffers may be passed to
* force a commit or close.
*
* @param content The content to be written or an empty buffer.
* @param last True if this is the last call to write
* @param callback The callback to use to indicate {@link Callback#succeeded()}
* or {@link Callback#failed(Throwable)}.
*/
void write(ByteBuffer content, boolean last, Callback callback);
/**
* @return The next Interceptor in the chain or null if this is the
* last Interceptor in the chain.
*/
Interceptor getNextInterceptor();
/**
* Reset the buffers.
* <p>If the Interceptor contains buffers then reset them.
*
* @throws IllegalStateException Thrown if the response has been
* committed and buffers and/or headers cannot be reset.
*/
default void resetBuffer() throws IllegalStateException
{
Interceptor next = getNextInterceptor();
if (next != null)
next.resetBuffer();
}
}
public class DefaultInterceptor implements Interceptor
{
@Override
public void write(ByteBuffer content, boolean last, Callback callback)
{
_response.write(last, content, callback);
}
@Override
public Interceptor getNextInterceptor()
{
return null;
}
}
private static final Logger LOG = LoggerFactory.getLogger(HttpOutput.class);
private static final ThreadLocal<CharsetEncoder> _encoder = new ThreadLocal<>();
@ -198,13 +129,11 @@ public class HttpOutput extends ServletOutputStream implements Runnable
private final ServletChannel _servletChannel;
private final Response _response;
private final ByteBufferPool _byteBufferPool;
private ServletRequestState _channelState;
private SharedBlockingCallback _writeBlocker;
private final ServletRequestState _channelState;
private final SharedBlockingCallback _writeBlocker;
private ApiState _apiState = ApiState.BLOCKING;
private State _state = State.OPEN;
private boolean _softClose = false;
private Interceptor _interceptor;
private long _written;
private long _flushed;
private long _firstByteNanoTime = -1;
@ -223,7 +152,6 @@ public class HttpOutput extends ServletOutputStream implements Runnable
_byteBufferPool = _response.getRequest().getComponents().getByteBufferPool();
_channelState = _servletChannel.getState();
_interceptor = new DefaultInterceptor();
_writeBlocker = new WriteBlocker(_servletChannel);
HttpConfiguration config = _servletChannel.getHttpConfiguration();
_bufferSize = config.getOutputBufferSize();
@ -240,16 +168,6 @@ public class HttpOutput extends ServletOutputStream implements Runnable
return _response;
}
public Interceptor getInterceptor()
{
return _interceptor;
}
public void setInterceptor(Interceptor interceptor)
{
_interceptor = interceptor;
}
public boolean isWritten()
{
return _written > 0;
@ -292,8 +210,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
else
_firstByteNanoTime = Long.MAX_VALUE;
}
_interceptor.write(content, last, callback);
_response.write(last, content, callback);
}
private void onWriteComplete(boolean last, Throwable failure)
@ -1316,9 +1233,6 @@ public class HttpOutput extends ServletOutputStream implements Runnable
/**
* <p>Invoked when bytes have been flushed to the network.</p>
* <p>The number of flushed bytes may be different from the bytes written
* by the application if an {@link Interceptor} changed them, for example
* by compressing them.</p>
*
* @param bytes the number of bytes flushed
* @throws IOException if the minimum data rate, when set, is not respected
@ -1348,7 +1262,6 @@ public class HttpOutput extends ServletOutputStream implements Runnable
_state = State.OPEN;
_apiState = ApiState.BLOCKING;
_softClose = true; // Stay closed until next request
_interceptor = new DefaultInterceptor();
HttpConfiguration config = _connectionMetaData.getHttpConfiguration();
_bufferSize = config.getOutputBufferSize();
_commitSize = config.getOutputAggregationSize();
@ -1368,7 +1281,6 @@ public class HttpOutput extends ServletOutputStream implements Runnable
{
try (AutoLock l = _channelState.lock())
{
_interceptor.resetBuffer();
if (BufferUtil.hasContent(_aggregate))
BufferUtil.clear(_aggregate);
_written = 0;

View File

@ -345,6 +345,7 @@ public class ServletContextRequest extends ContextRequest
private String _method;
private ServletMultiPartFormData.Parts _parts;
private ServletPathMapping _servletPathMapping;
private boolean _asyncSupported = true;
public static Session getSession(HttpSession httpSession)
{
@ -696,7 +697,7 @@ public class ServletContextRequest extends ContextRequest
if (getRequestedSessionId() == null || _coreSession == null)
return false;
//check requestedId (which may have worker suffix) against the actual session id
return getSessionManager().getSessionIdManager().getId(getRequestedSessionId()).equals(_coreSession.getId());
return _coreSession.isValid() && getSessionManager().getSessionIdManager().getId(getRequestedSessionId()).equals(_coreSession.getId());
}
@Override
@ -1362,6 +1363,8 @@ public class ServletContextRequest extends ContextRequest
@Override
public AsyncContext startAsync() throws IllegalStateException
{
if (!isAsyncSupported())
throw new IllegalStateException("Async Not Supported");
ServletRequestState state = getState();
if (_async == null)
_async = new AsyncContextState(state);
@ -1374,6 +1377,8 @@ public class ServletContextRequest extends ContextRequest
@Override
public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException
{
if (!isAsyncSupported())
throw new IllegalStateException("Async Not Supported");
ServletRequestState state = getState();
if (_async == null)
_async = new AsyncContextState(state);
@ -1398,7 +1403,12 @@ public class ServletContextRequest extends ContextRequest
@Override
public boolean isAsyncSupported()
{
return true;
return _asyncSupported;
}
public void setAsyncSupported(boolean asyncSupported)
{
_asyncSupported = asyncSupported;
}
@Override

View File

@ -30,7 +30,6 @@ import java.util.Set;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicLong;
import jakarta.servlet.AsyncContext;
import jakarta.servlet.GenericServlet;
import jakarta.servlet.MultipartConfigElement;
import jakarta.servlet.Servlet;
@ -39,12 +38,9 @@ import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRegistration;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletRequestWrapper;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.ServletSecurityElement;
import jakarta.servlet.UnavailableException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.ee10.servlet.security.IdentityService;
import org.eclipse.jetty.ee10.servlet.security.RunAsToken;
@ -1376,49 +1372,24 @@ public class ServletHolder extends Holder<Servlet> implements Comparable<Servlet
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException
{
if (req.isAsyncSupported())
if (request.isAsyncSupported())
{
if (req instanceof HttpServletRequest httpServletRequest)
ServletContextRequest servletContextRequest = ServletContextRequest.getServletContextRequest(request);
servletContextRequest.getServletApiRequest().setAsyncSupported(false);
try
{
getWrapped().service(new HttpServletRequestWrapper(httpServletRequest)
{
@Override
public boolean isAsyncSupported()
{
return false;
}
@Override
public AsyncContext startAsync() throws IllegalStateException
{
throw new IllegalStateException("Async Not Supported");
}
}, res);
getWrapped().service(request, response);
}
else
finally
{
//TODO is this necessary to support?
getWrapped().service(new ServletRequestWrapper(req)
{
@Override
public boolean isAsyncSupported()
{
return false;
}
@Override
public AsyncContext startAsync() throws IllegalStateException
{
throw new IllegalStateException("Async Not Supported");
}
}, res);
servletContextRequest.getServletApiRequest().setAsyncSupported(true);
}
}
else
{
getWrapped().service(req, res);
getWrapped().service(request, response);
}
}
}

View File

@ -27,17 +27,10 @@ public class EncodingHttpWriter extends HttpWriter
{
final Writer _converter;
public EncodingHttpWriter(HttpOutput out, String encoding)
public EncodingHttpWriter(HttpOutput out, String encoding) throws IOException
{
super(out);
try
{
_converter = new OutputStreamWriter(_bytes, encoding);
}
catch (UnsupportedEncodingException e)
{
throw new RuntimeException(e);
}
_converter = new OutputStreamWriter(_bytes, encoding);
}
@Override

View File

@ -182,7 +182,6 @@ public class AsyncServletTest
assertThat(response, Matchers.startsWith("HTTP/1.1 200 OK"));
assertThat(_history, contains(
"REQUEST /ctx/noasync/info",
"wrapped REQ",
"initial"
));
@ -199,7 +198,6 @@ public class AsyncServletTest
assertThat(response, Matchers.startsWith("HTTP/1.1 500 "));
assertThat(_history, contains(
"REQUEST /ctx/noasync/info?start=200",
"wrapped REQ",
"initial",
"ERROR /ctx/error/custom?start=200",
"wrapped REQ",

View File

@ -0,0 +1,105 @@
//
// ========================================================================
// 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.ee10.servlet;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Server;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
public class CharacterEncodingTest
{
public static class CharsetChangeToJsonMimeTypeSetCharsetToNullServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
// set an unknown character encoding
response.setCharacterEncoding("allez-les-bleus");
// here we should have UnsupportedEncodingException
try
{
response.getWriter();
}
catch (UnsupportedEncodingException e)
{
// nothing we only test we throw this exception
}
}
}
private static Server server;
private static LocalConnector connector;
@BeforeAll
public static void startServer() throws Exception
{
server = new Server();
connector = new LocalConnector(server);
server.addConnector(connector);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
server.setHandler(context);
context.addServlet(CharsetChangeToJsonMimeTypeSetCharsetToNullServlet.class, "/character-encoding/not-exists/*");
server.start();
}
@AfterAll
public static void stopServer()
{
try
{
server.stop();
}
catch (Exception e)
{
e.printStackTrace(System.err);
}
}
@Test
public void testUnknownCharacterEncoding() throws Exception
{
HttpTester.Request request = new HttpTester.Request();
request.setMethod("GET");
request.setURI("/character-encoding/not-exists/");
request.setVersion(HttpVersion.HTTP_1_1);
request.setHeader("Host", "test");
ByteBuffer responseBuffer = connector.getResponse(request.generate());
HttpTester.Response response = HttpTester.parseResponse(responseBuffer);
// Now test for properly formatted HTTP Response Headers.
assertThat("Response Code", response.getStatus(), is(200));
}
}

View File

@ -14,14 +14,12 @@
package org.eclipse.jetty.ee10.test.client.transport;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Deque;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
@ -53,9 +51,7 @@ import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.InputStreamRequestContent;
import org.eclipse.jetty.client.util.OutputStreamRequestContent;
import org.eclipse.jetty.client.util.StringRequestContent;
import org.eclipse.jetty.ee10.servlet.HttpInput;
import org.eclipse.jetty.ee10.servlet.HttpOutput;
import org.eclipse.jetty.ee10.servlet.ServletContextRequest;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
@ -64,7 +60,6 @@ import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.client.transport.internal.HttpConnectionOverHTTP2;
import org.eclipse.jetty.http2.internal.HTTP2Session;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.logging.StacklessLogging;
import org.eclipse.jetty.server.Request;
@ -80,9 +75,7 @@ import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import static java.nio.ByteBuffer.wrap;
import static org.awaitility.Awaitility.await;
import static org.eclipse.jetty.util.BufferUtil.toArray;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
@ -1199,174 +1192,6 @@ public class AsyncIOServletTest extends AbstractTest
assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
}
@ParameterizedTest
@MethodSource("transportsNoFCGI")
public void testAsyncIntercepted(Transport transport) throws Exception
{
start(transport, new HttpServlet()
{
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
{
System.err.println("Service " + request);
HttpInput httpInput = Objects.requireNonNull(ServletContextRequest.getServletContextRequest(request)).getHttpInput();
httpInput.addInterceptor(new HttpInput.Interceptor()
{
int state = 0;
Content.Chunk saved;
@Override
public Content.Chunk readFrom(Content.Chunk chunk)
{
switch (state)
{
case 0:
// null transform
chunk.skip(chunk.remaining());
chunk.release();
state++;
return null;
case 1:
{
// copy transform
if (!chunk.hasRemaining())
{
state++;
return chunk;
}
ByteBuffer copy = wrap(toArray(chunk.getByteBuffer()));
chunk.skip(copy.remaining());
chunk.release();
return Content.Chunk.from(copy, false);
}
case 2:
// byte by byte
if (!chunk.hasRemaining())
{
state++;
return chunk;
}
byte[] b = new byte[1];
int l = chunk.get(b, 0, 1);
if (!chunk.hasRemaining())
chunk.release();
return Content.Chunk.from(wrap(b, 0, l), false);
case 3:
{
// double vision
if (!chunk.hasRemaining())
{
if (saved == null)
{
state++;
return chunk;
}
Content.Chunk ref = saved;
saved = null;
return ref;
}
byte[] data = toArray(chunk.getByteBuffer());
chunk.skip(data.length);
chunk.release();
saved = Content.Chunk.from(wrap(data), false);
return Content.Chunk.from(wrap(data), false);
}
default:
return chunk;
}
}
});
AsyncContext asyncContext = request.startAsync();
ServletInputStream input = request.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
input.setReadListener(new ReadListener()
{
@Override
public void onDataAvailable() throws IOException
{
while (input.isReady())
{
int b = input.read();
if (b > 0)
{
// System.err.printf("0x%2x %s %n", b, Character.isISOControl(b)?"?":(""+(char)b));
out.write(b);
}
else if (b < 0)
return;
}
}
@Override
public void onAllDataRead() throws IOException
{
response.getOutputStream().write(out.toByteArray());
asyncContext.complete();
}
@Override
public void onError(Throwable x)
{
}
});
}
});
AsyncRequestContent content = new AsyncRequestContent();
CountDownLatch clientLatch = new CountDownLatch(1);
String expected =
"S1" +
"S2" +
"S3S3" +
"S4" +
"S5" +
"S6";
client.newRequest(newURI(transport))
.method(HttpMethod.POST)
.body(content)
.send(new BufferingResponseListener()
{
@Override
public void onComplete(Result result)
{
if (result.isSucceeded())
{
Response response = result.getResponse();
assertThat(response.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
assertThat(getContentAsString(), Matchers.equalTo(expected));
clientLatch.countDown();
}
}
});
content.write(BufferUtil.toBuffer("S0"), Callback.NOOP);
content.flush();
content.write(BufferUtil.toBuffer("S1"), Callback.NOOP);
content.flush();
content.write(BufferUtil.toBuffer("S2"), Callback.NOOP);
content.flush();
content.write(BufferUtil.toBuffer("S3"), Callback.NOOP);
content.flush();
content.write(BufferUtil.toBuffer("S4"), Callback.NOOP);
content.flush();
content.write(BufferUtil.toBuffer("S5"), Callback.NOOP);
content.flush();
content.write(BufferUtil.toBuffer("S6"), Callback.NOOP);
content.close();
assertTrue(clientLatch.await(10, TimeUnit.SECONDS));
}
@ParameterizedTest
@MethodSource("transportsNoFCGI")
public void testAsyncEcho(Transport transport) throws Exception
@ -1443,236 +1268,6 @@ public class AsyncIOServletTest extends AbstractTest
assertThat(resultRef.get().getResponse().getStatus(), Matchers.equalTo(HttpStatus.OK_200));
}
/*
// TODO: there is no GzipHttpInputInterceptor anymore, use something else.
@ParameterizedTest
@MethodSource("transportsNoFCGI")
public void testAsyncInterceptedTwice(Transport transport) throws Exception
{
init(transport);
start(transport, new HttpServlet()
{
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
{
System.err.println("Service " + request);
HttpInput httpInput = ((Request)request).getHttpInput();
httpInput.addInterceptor(new GzipHttpInputInterceptor(new InflaterPool(-1, true), ((Request)request).getHttpChannel().getByteBufferPool(), 1024));
httpInput.addInterceptor(chunk ->
{
if (chunk.isTerminal())
return chunk;
ByteBuffer byteBuffer = chunk.getByteBuffer();
byte[] bytes = new byte[2];
bytes[1] = byteBuffer.get();
bytes[0] = byteBuffer.get();
return Content.Chunk.from(wrap(bytes), false);
});
AsyncContext asyncContext = request.startAsync();
ServletInputStream input = request.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
input.setReadListener(new ReadListener()
{
@Override
public void onDataAvailable() throws IOException
{
while (input.isReady())
{
int b = input.read();
if (b > 0)
{
// System.err.printf("0x%2x %s %n", b, Character.isISOControl(b)?"?":(""+(char)b));
out.write(b);
}
else if (b < 0)
return;
}
}
@Override
public void onAllDataRead() throws IOException
{
response.getOutputStream().write(out.toByteArray());
asyncContext.complete();
}
@Override
public void onError(Throwable x)
{
}
});
}
});
AsyncRequestContent contentProvider = new AsyncRequestContent();
CountDownLatch clientLatch = new CountDownLatch(1);
String expected =
"0S" +
"1S" +
"2S" +
"3S" +
"4S" +
"5S" +
"6S";
client.newRequest(newURI(transport))
.method(HttpMethod.POST)
.path(scenario.servletPath)
.body(contentProvider)
.send(new BufferingResponseListener()
{
@Override
public void onComplete(Result result)
{
if (result.isSucceeded())
{
Response response = result.getResponse();
assertThat(response.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
assertThat(getContentAsString(), Matchers.equalTo(expected));
clientLatch.countDown();
}
}
});
for (int i = 0; i < 7; i++)
{
contentProvider.write(gzipToBuffer("S" + i), Callback.NOOP);
contentProvider.flush();
}
contentProvider.close();
assertTrue(clientLatch.await(10, TimeUnit.SECONDS));
}
*/
@ParameterizedTest
@MethodSource("transportsNoFCGI")
public void testAsyncInterceptedTwiceWithNulls(Transport transport) throws Exception
{
start(transport, new HttpServlet()
{
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
{
System.err.println("Service " + request);
HttpInput httpInput = ((ServletContextRequest)request).getHttpInput();
httpInput.addInterceptor(chunk ->
{
if (!chunk.hasRemaining())
return chunk;
// skip contents with odd numbers
ByteBuffer duplicate = chunk.getByteBuffer().duplicate();
duplicate.get();
byte integer = duplicate.get();
int idx = Character.getNumericValue(integer);
Content.Chunk chunkCopy = Content.Chunk.from(chunk.getByteBuffer().duplicate(), false);
chunk.skip(chunk.remaining());
chunk.release();
if (idx % 2 == 0)
return chunkCopy;
return null;
});
httpInput.addInterceptor(chunk ->
{
if (!chunk.hasRemaining())
return chunk;
// reverse the bytes
ByteBuffer byteBuffer = chunk.getByteBuffer();
byte[] bytes = new byte[2];
bytes[1] = byteBuffer.get();
bytes[0] = byteBuffer.get();
return Content.Chunk.from(wrap(bytes), false);
});
AsyncContext asyncContext = request.startAsync();
ServletInputStream input = request.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
input.setReadListener(new ReadListener()
{
@Override
public void onDataAvailable() throws IOException
{
while (input.isReady())
{
int b = input.read();
if (b > 0)
{
// System.err.printf("0x%2x %s %n", b, Character.isISOControl(b)?"?":(""+(char)b));
out.write(b);
}
else if (b < 0)
return;
}
}
@Override
public void onAllDataRead() throws IOException
{
response.getOutputStream().write(out.toByteArray());
asyncContext.complete();
}
@Override
public void onError(Throwable x)
{
}
});
}
});
AsyncRequestContent contentProvider = new AsyncRequestContent();
CountDownLatch clientLatch = new CountDownLatch(1);
String expected =
"0S" +
"2S" +
"4S" +
"6S";
client.newRequest(newURI(transport))
.method(HttpMethod.POST)
.body(contentProvider)
.send(new BufferingResponseListener()
{
@Override
public void onComplete(Result result)
{
if (result.isSucceeded())
{
Response response = result.getResponse();
assertThat(response.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
assertThat(getContentAsString(), Matchers.equalTo(expected));
clientLatch.countDown();
}
}
});
contentProvider.write(BufferUtil.toBuffer("S0"), Callback.NOOP);
contentProvider.flush();
contentProvider.write(BufferUtil.toBuffer("S1"), Callback.NOOP);
contentProvider.flush();
contentProvider.write(BufferUtil.toBuffer("S2"), Callback.NOOP);
contentProvider.flush();
contentProvider.write(BufferUtil.toBuffer("S3"), Callback.NOOP);
contentProvider.flush();
contentProvider.write(BufferUtil.toBuffer("S4"), Callback.NOOP);
contentProvider.flush();
contentProvider.write(BufferUtil.toBuffer("S5"), Callback.NOOP);
contentProvider.flush();
contentProvider.write(BufferUtil.toBuffer("S6"), Callback.NOOP);
contentProvider.close();
assertTrue(clientLatch.await(10, TimeUnit.SECONDS));
}
@ParameterizedTest
@MethodSource("transportsNoFCGI")
public void testWriteListenerFromOtherThread(Transport transport) throws Exception

View File

@ -32,7 +32,7 @@
<jakarta.servlet.api.version>6.0.0</jakarta.servlet.api.version>
<jakarta.servlet.jsp.api.version>3.1.0</jakarta.servlet.jsp.api.version>
<jakarta.servlet.jsp.jstl.api.version>3.0.0</jakarta.servlet.jsp.jstl.api.version>
<jakarta.servlet.jsp.jstl.impl.version>3.0.0</jakarta.servlet.jsp.jstl.impl.version> <!-- TODO: remove? -->
<jakarta.servlet.jsp.jstl.impl.version>3.0.1</jakarta.servlet.jsp.jstl.impl.version>
<jakarta.ws.rs.api.version>3.1.0</jakarta.ws.rs.api.version>
<jakarta.xml.bind.api.version>4.0.0</jakarta.xml.bind.api.version>
<jakarta.xml.bind.impl.version>4.0.0</jakarta.xml.bind.impl.version>

View File

@ -0,0 +1 @@
invoker.goals = verify

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty.ee9.its.jetty-start-overlay-it</groupId>
<artifactId>jetty-simple-project</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>jetty-simple-base-webapp</artifactId>
<packaging>war</packaging>
<name>Jetty :: Simple :: Base Webapp</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,5 @@
<html>
<body>
<h1>Simple Base Webapp Index</h1>
</body>
</html>

View File

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty.ee9.its.jetty-start-overlay-it</groupId>
<artifactId>jetty-simple-project</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>jetty-simple-webapp</artifactId>
<packaging>war</packaging>
<name>Jetty :: Simple :: Webapp</name>
<properties>
<jetty.port.file>${project.build.directory}/jetty-start-port.txt</jetty.port.file>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.ee9.its.jetty-start-overlay-it</groupId>
<artifactId>jetty-simple-base-webapp</artifactId>
<type>war</type>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee9</groupId>
<artifactId>jetty-ee9-servlet</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee9</groupId>
<artifactId>jetty-ee9-maven-plugin</artifactId>
<classifier>tests</classifier>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-client</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<overlays>
<overlay>
<groupId>org.eclipse.jetty.ee9.its.jetty-start-overlay-it</groupId>
<artifactId>jetty-simple-base-webapp</artifactId>
<includes>
<include>index.html</include>
</includes>
</overlay>
<overlay/>
</overlays>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>run-tests</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<includes>
<include>IntegrationTest*.java</include>
</includes>
<systemPropertyVariables>
<jetty.port.file>${jetty.port.file}</jetty.port.file>
<context.path>/setbycontextxml</context.path>
<contentCheck>Simple Base Webapp Index</contentCheck>
<pathToCheck>/index.html</pathToCheck>
<maven.it.name>${project.groupId}:${project.artifactId}</maven.it.name>
</systemPropertyVariables>
<dependenciesToScan>
<dependency>org.eclipse.jetty.ee9:jetty-ee9-maven-plugin</dependency>
</dependenciesToScan>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.eclipse.jetty.ee9</groupId>
<artifactId>jetty-ee9-maven-plugin</artifactId>
<executions>
<execution>
<id>start-jetty</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
<configuration>
<contextXml>${basedir}/src/config/context.xml</contextXml>
<systemProperties>
<jetty.port.file>${jetty.port.file}</jetty.port.file>
<jetty.deployMode>EMBED</jetty.deployMode>
</systemProperties>
<jettyXmls>
<jettyXml>${basedir}/src/config/jetty.xml</jettyXml>
</jettyXmls>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure class="org.eclipse.jetty.ee9.webapp.WebAppContext">
<Set name="contextPath">/setbycontextxml</Set>
</Configure>

View File

@ -0,0 +1,40 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
<Set name="secureScheme">https</Set>
<Set name="securePort"><Property name="jetty.secure.port" default="8443" /></Set>
<Set name="outputBufferSize">32768</Set>
<Set name="requestHeaderSize">8192</Set>
<Set name="responseHeaderSize">8192</Set>
<Set name="headerCacheSize">1024</Set>
</New>
<Call name="addConnector">
<Arg>
<New class="org.eclipse.jetty.server.ServerConnector">
<Arg name="server"><Ref refid="Server" /></Arg>
<Arg name="factories">
<Array type="org.eclipse.jetty.server.ConnectionFactory">
<Item>
<New class="org.eclipse.jetty.server.HttpConnectionFactory">
<Arg name="config"><Ref refid="httpConfig" /></Arg>
</New>
</Item>
</Array>
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee9.maven.plugin.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>
</Call>
<Set name="host" property="jetty.host"/>
<Set name="port" property="jetty.port"/>
<Set name="idleTimeout">30000</Set>
</New>
</Arg>
</Call>
</Configure>

View File

@ -0,0 +1,5 @@
<html>
<body>
<h1>Simple WebApp Index</h1>
</body>
</html>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty.ee9.its</groupId>
<artifactId>it-parent-pom</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jetty.ee9.its.jetty-start-overlay-it</groupId>
<artifactId>jetty-simple-project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Jetty :: Simple Overlay</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<jetty.version>@project.version@</jetty.version>
</properties>
<modules>
<module>jetty-simple-base-webapp</module>
<module>jetty-simple-webapp</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.ee9.its.jetty-start-overlay-it</groupId>
<artifactId>jetty-simple-base-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@ -0,0 +1,21 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
File buildLog = new File( basedir, 'build.log' )
assert buildLog.text.contains( 'Started Server' )
assert buildLog.text.contains( 'Running org.eclipse.jetty.ee9.maven.plugin.it.IntegrationTestGetContent')

View File

@ -194,7 +194,7 @@ public abstract class AbstractUnassembledWebAppMojo extends AbstractWebAppMojo
}
}
}
//process any overlays and the war type artifacts, and
//sets up the base resource collection for the webapp
mavenProjectHelper.getOverlayManager().applyOverlays(webApp);

View File

@ -94,7 +94,7 @@ public class OverlayManager
//if a war matches an overlay config
Artifact a = warPlugin.getWarArtifact(config.getGroupId(), config.getArtifactId(), config.getClassifier());
if (a != null)
{
{
matchedWarArtifacts.add(a);
Resource resource = ResourceFactory.root().newJarFileResource(a.getFile().toPath().toUri()); // TODO leak
SelectiveJarResource r = new SelectiveJarResource(resource);
@ -127,7 +127,7 @@ public class OverlayManager
*/
protected Resource unpackOverlay(Overlay overlay)
throws IOException
{
{
if (overlay.getResource() == null)
return null; //nothing to unpack

View File

@ -109,6 +109,8 @@ public class MavenProjectHelper
*/
public MavenProject getMavenProjectFor(Artifact artifact)
{
if (artifact == null)
return null;
return artifactToReactorProjectMap.get(artifact.getId());
}

View File

@ -25,17 +25,10 @@ public class EncodingHttpWriter extends HttpWriter
{
final Writer _converter;
public EncodingHttpWriter(HttpOutput out, String encoding)
public EncodingHttpWriter(HttpOutput out, String encoding) throws IOException
{
super(out);
try
{
_converter = new OutputStreamWriter(_bytes, encoding);
}
catch (UnsupportedEncodingException e)
{
throw new RuntimeException(e);
}
_converter = new OutputStreamWriter(_bytes, encoding);
}
@Override

View File

@ -1511,7 +1511,7 @@ public class Request implements HttpServletRequest
if (getRequestedSessionId() == null || _coreSession == null)
return false;
return (_sessionManager.getSessionIdManager().getId(getRequestedSessionId()).equals(_coreSession.getId()));
return (_coreSession.isValid() && _sessionManager.getSessionIdManager().getId(getRequestedSessionId()).equals(_coreSession.getId()));
}
@Override

View File

@ -0,0 +1,105 @@
//
// ========================================================================
// 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.ee9.servlet;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Server;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
public class CharacterEncodingTest
{
public static class CharsetChangeToJsonMimeTypeSetCharsetToNullServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
// set an unknown character encoding
response.setCharacterEncoding("allez-les-bleus");
// here we should have UnsupportedEncodingException
try
{
response.getWriter();
}
catch (UnsupportedEncodingException e)
{
// nothing we only test we throw this exception
}
}
}
private static Server server;
private static LocalConnector connector;
@BeforeAll
public static void startServer() throws Exception
{
server = new Server();
connector = new LocalConnector(server);
server.addConnector(connector);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
server.setHandler(context);
context.addServlet(CharsetChangeToJsonMimeTypeSetCharsetToNullServlet.class, "/character-encoding/not-exists/*");
server.start();
}
@AfterAll
public static void stopServer()
{
try
{
server.stop();
}
catch (Exception e)
{
e.printStackTrace(System.err);
}
}
@Test
public void testUnknownCharacterEncoding() throws Exception
{
HttpTester.Request request = new HttpTester.Request();
request.setMethod("GET");
request.setURI("/character-encoding/not-exists/");
request.setVersion(HttpVersion.HTTP_1_1);
request.setHeader("Host", "test");
ByteBuffer responseBuffer = connector.getResponse(request.generate());
HttpTester.Response response = HttpTester.parseResponse(responseBuffer);
// Now test for properly formatted HTTP Response Headers.
assertThat("Response Code", response.getStatus(), is(200));
}
}

23
pom.xml
View File

@ -33,7 +33,7 @@
<awaitility.version>4.2.0</awaitility.version>
<bndlib.version>6.3.1</bndlib.version>
<build-support.version>1.5</build-support.version>
<checkstyle.version>10.3.3</checkstyle.version>
<checkstyle.version>10.3.4</checkstyle.version>
<commons-codec.version>1.15</commons-codec.version>
<commons.compress.version>1.22</commons.compress.version>
<commons.io.version>2.11.0</commons.io.version>
@ -43,15 +43,15 @@
<felix.version>7.0.5</felix.version>
<findbugs.jsr305.version>3.0.2</findbugs.jsr305.version>
<google.errorprone.version>2.15.0</google.errorprone.version>
<grpc.version>1.49.0</grpc.version>
<grpc.version>1.49.2</grpc.version>
<gson.version>2.9.1</gson.version>
<guava.version>31.1-jre</guava.version>
<guice.version>5.1.0</guice.version>
<hamcrest.version>2.2</hamcrest.version>
<hawtio.version>2.15.1</hawtio.version>
<hawtio.version>2.15.2</hawtio.version>
<hazelcast.version>4.2.5</hazelcast.version>
<infinispan.protostream.version>4.4.4.Final</infinispan.protostream.version>
<infinispan.version>11.0.15.Final</infinispan.version>
<infinispan.protostream.version>4.5.0.Final</infinispan.protostream.version>
<infinispan.version>11.0.16.Final</infinispan.version>
<jackson.version>2.13.4</jackson.version>
<jboss.logging.annotations.version>2.2.1.Final</jboss.logging.annotations.version>
<jboss.logging.processor.version>2.2.1.Final</jboss.logging.processor.version>
@ -68,12 +68,12 @@
<jna.version>5.12.1</jna.version>
<jnr-constants.version>0.10.4</jnr-constants.version>
<jnr-enxio.version>0.32.13</jnr-enxio.version>
<jnr-ffi.version>2.2.11</jnr-ffi.version>
<jnr-ffi.version>2.2.12</jnr-ffi.version>
<jnr-posix.version>3.1.15</jnr-posix.version>
<jnr-unixsocket.version>0.38.17</jnr-unixsocket.version>
<json-simple.version>1.1.1</json-simple.version>
<json-smart.version>2.4.8</json-smart.version>
<junit.version>5.9.0</junit.version>
<junit.version>5.9.1</junit.version>
<kerb-simplekdc.version>2.0.2</kerb-simplekdc.version>
<log4j2.version>2.19.0</log4j2.version>
<logback.version>1.4.5</logback.version>
@ -81,11 +81,11 @@
<mariadb.docker.version>10.3.6</mariadb.docker.version>
<maven.deps.version>3.8.4</maven.deps.version>
<maven-artifact-transfer.version>0.13.1</maven-artifact-transfer.version>
<maven.resolver.version>1.8.2</maven.resolver.version>
<maven.resolver.version>1.9.2</maven.resolver.version>
<maven.version>3.8.6</maven.version>
<mongodb.version>3.12.11</mongodb.version>
<openpojo.version>0.9.1</openpojo.version>
<org.osgi.annotation.version>8.0.1</org.osgi.annotation.version>
<org.osgi.annotation.version>8.1.0</org.osgi.annotation.version>
<org.osgi.core.version>6.0.0</org.osgi.core.version>
<org.osgi.util.function.version>1.2.0</org.osgi.util.function.version>
<org.osgi.util.promise.version>1.2.0</org.osgi.util.promise.version>
@ -96,9 +96,8 @@
<testcontainers.version>1.17.5</testcontainers.version>
<weld.version>4.0.3.Final</weld.version>
<wildfly.common.version>1.6.0.Final</wildfly.common.version>
<wildfly.elytron.version>1.20.1.Final</wildfly.elytron.version>
<wildfly.elytron.version>2.0.0.Final</wildfly.elytron.version>
<xmemcached.version>2.4.7</xmemcached.version>
<weld.version>4.0.2.Final</weld.version>
<!-- some maven plugins versions -->
<asciidoctor.maven.plugin.version>2.2.2</asciidoctor.maven.plugin.version>
@ -123,7 +122,7 @@
<maven.gpg.plugin.version>3.0.1</maven.gpg.plugin.version>
<maven.install.plugin.version>3.1.0</maven.install.plugin.version>
<maven.invoker.plugin.version>3.3.0</maven.invoker.plugin.version>
<maven.jar.plugin.version>3.2.2</maven.jar.plugin.version>
<maven.jar.plugin.version>3.3.0</maven.jar.plugin.version>
<maven.javadoc.plugin.version>3.4.1</maven.javadoc.plugin.version>
<maven.plugin-tools.version>3.7.0</maven.plugin-tools.version>
<maven-plugin.plugin.version>3.7.0</maven-plugin.plugin.version>