From 07a97da01069f9cc952ca47aee6570da2dc19eb0 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Wed, 7 Aug 2019 10:59:50 +0200
Subject: [PATCH 001/101] Draft 1 for week 2.
Signed-off-by: Simone Bordet
---
jetty-documentation/pom.xml | 1 +
.../main/asciidoc/embedded-guide/io-arch.adoc | 85 +++++-
.../embedded/SelectorManagerDocSnippets.java | 264 ++++++++++++++++++
.../client/SelectorManagerDocSnippets.java | 61 ----
4 files changed, 344 insertions(+), 67 deletions(-)
create mode 100644 jetty-documentation/src/main/java/embedded/SelectorManagerDocSnippets.java
delete mode 100644 jetty-documentation/src/main/java/embedded/client/SelectorManagerDocSnippets.java
diff --git a/jetty-documentation/pom.xml b/jetty-documentation/pom.xml
index 16ece04c3ea..7af6995bf49 100644
--- a/jetty-documentation/pom.xml
+++ b/jetty-documentation/pom.xml
@@ -149,6 +149,7 @@
${basedir}/src/main/asciidoc/contribution-guideindex.adoc${project.build.directory}/html/contribution-guide
+ coderay
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc
index 5bcc7c6019a..e677a5958fe 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc
@@ -145,7 +145,7 @@ The Jetty I/O library use Java NIO to handle I/O, so that I/O is non-blocking.
At the Java NIO level, in order to be notified when a `SocketChannel` has data
to be read, the `SelectionKey.OP_READ` flag must be set.
-In the Jetty I/O library, you can call `AbstractEndPoint.fillInterested(Callback)`
+In the Jetty I/O library, you can call `EndPoint.fillInterested(Callback)`
to declare interest in the "read" (or "fill") event, and the `Callback` parameter
is the object that is notified when such event occurs.
@@ -158,9 +158,82 @@ to write the ``ByteBuffer``s and the `Callback` parameter is the object that is
notified when the whole write is finished(i.e. _all_ ``ByteBuffer``s have been
fully written).
-[[io-arch-connection]]
-=== Jetty I/O: Implementing `Connection`
+The `EndPoint` APIs abstract out the Java NIO details by providing non-blocking
+APIs based on `Callback` objects for I/O operations.
+The `EndPoint` APIs are typically called by `Connection` implementations, see
+link:#io-arch-connection[this section].
-Implementing a `Connection` is how you deserialize incoming bytes into objects
-that can be used by more abstract layers, for example a HTTP request object or
-a WebSocket frame object.
+[[io-arch-connection]]
+=== Jetty I/O: `Connection`
+
+`Connection` is the abstraction that deserializes incoming bytes into objects,
+for example a HTTP request object or a WebSocket frame object, that can be used
+by more abstract layers.
+
+`Connection` instances have two lifecycle methods:
+
+* `Connection.onOpen()`, invoked when the `Connection` is associated with the
+`EndPoint`
+* `Connection.onClose(Throwable)`, invoked when the `Connection` is disassociated
+from the `EndPoint`, where the `Throwable` parameter indicates whether the
+disassociation was due to an error
+
+When a `Connection` is first created, it is not registered for any Java NIO
+event.
+It is therefore typical to implement `onOpen()` to call
+`EndPoint.fillInterested(Callback)` so that the `Connection` declares interest
+for read events and it is invoked (via the `Callback`) when the read event
+happens.
+
+Abstract class `AbstractConnection` partially implements `Connection` and
+provides simpler APIs. The example below shows a typical implementation that
+extends `AbstractConnection`:
+
+[source,java,indent=0]
+----
+include::{docbits}/embedded/SelectorManagerDocSnippets.java[tags=connection]
+----
+
+[[io-arch-echo]]
+=== Jetty I/O: Network Echo
+
+With the concepts above it is now possible to write a simple, fully non-blocking,
+`Connection` implementation that simply echoes the bytes that it reads back
+to the other peer.
+
+A naive, but wrong, implementation may be the following:
+
+[source,java,indent=0]
+----
+include::{docbits}/embedded/SelectorManagerDocSnippets.java[tags=echo-wrong]
+----
+
+NOTE: The implementation above is wrong and leads to `StackOverflowError`.
+
+The problem with this implementation is that if the writes always complete
+synchronously (i.e. without being delayed by TCP congestion), you end up with
+this sequence of calls:
+
+----
+Connection.onFillable()
+ EndPoint.write()
+ Callback.succeeded()
+ Connection.onFillable()
+ EndPoint.write()
+ Callback.succeeded()
+ ...
+----
+
+which leads to `StackOverflowException`.
+
+This is a typical side effect of asynchronous programming using non-blocking
+APIs, and happens in the Jetty I/O library as well.
+
+A correct implementation is the following:
+
+[source,java,indent=0]
+----
+include::{docbits}/embedded/SelectorManagerDocSnippets.java[tags=echo-correct]
+----
+
+The correct implementation performs the reads
diff --git a/jetty-documentation/src/main/java/embedded/SelectorManagerDocSnippets.java b/jetty-documentation/src/main/java/embedded/SelectorManagerDocSnippets.java
new file mode 100644
index 00000000000..5e4f4b36c8f
--- /dev/null
+++ b/jetty-documentation/src/main/java/embedded/SelectorManagerDocSnippets.java
@@ -0,0 +1,264 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package embedded;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.nio.channels.WritePendingException;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.jetty.io.AbstractConnection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.SelectorManager;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+
+public class SelectorManagerDocSnippets
+{
+ // tag::connect[]
+ public void connect(SelectorManager selectorManager, Map context) throws IOException
+ {
+ String host = "host";
+ int port = 8080;
+
+ // Create an unconnected SocketChannel.
+ SocketChannel socketChannel = SocketChannel.open();
+ socketChannel.configureBlocking(false);
+
+ // Connect and register to Jetty.
+ if (socketChannel.connect(new InetSocketAddress(host, port)))
+ selectorManager.accept(socketChannel, context);
+ else
+ selectorManager.connect(socketChannel, context);
+ }
+ // end::connect[]
+
+ // tag::accept[]
+ public void accept(ServerSocketChannel acceptor, SelectorManager selectorManager) throws IOException
+ {
+ // Wait until a client connects.
+ SocketChannel socketChannel = acceptor.accept();
+ socketChannel.configureBlocking(false);
+
+ // Accept and register to Jetty.
+ Object attachment = null;
+ selectorManager.accept(socketChannel, attachment);
+ }
+ // end::accept[]
+
+ public void connection()
+ {
+ // tag::connection[]
+ // Extend AbstractConnection to inherit basic implementation.
+ class MyConnection extends AbstractConnection
+ {
+ public MyConnection(EndPoint endPoint, Executor executor)
+ {
+ super(endPoint, executor);
+ }
+
+ @Override
+ public void onOpen()
+ {
+ super.onOpen();
+
+ // Declare interest for fill events.
+ fillInterested();
+ }
+
+ @Override
+ public void onFillable()
+ {
+ // Called when a fill event happens.
+ }
+ }
+ // end::connection[]
+ }
+
+ public void echoWrong()
+ {
+ // tag::echo-wrong[]
+ class EchoConnection extends AbstractConnection implements Callback
+ {
+ public EchoConnection(EndPoint endp, Executor executor)
+ {
+ super(endp, executor);
+ }
+
+ @Override
+ public void onOpen()
+ {
+ super.onOpen();
+
+ // Declare interest for fill events.
+ fillInterested();
+ }
+
+ @Override
+ public void onFillable()
+ {
+ try
+ {
+ ByteBuffer buffer = BufferUtil.allocate(1024);
+ int filled = getEndPoint().fill(buffer);
+ if (filled > 0)
+ {
+ // Filled some bytes, echo them back.
+ getEndPoint().write(this, buffer);
+ }
+ else if (filled == 0)
+ {
+ // No more bytes to fill, declare
+ // again interest for fill events.
+ fillInterested();
+ }
+ else
+ {
+ // The other peer closed the
+ // connection, close it back.
+ getEndPoint().close();
+ }
+ }
+ catch (Exception x)
+ {
+ getEndPoint().close(x);
+ }
+ }
+
+ @Override
+ public void succeeded()
+ {
+ // The write is complete, fill again.
+ onFillable();
+ }
+
+ @Override
+ public void failed(Throwable x)
+ {
+ getEndPoint().close(x);
+ }
+ }
+ // end::echo-wrong[]
+ }
+
+ public void echoCorrect()
+ {
+ // tag::echo-correct[]
+ class EchoConnection extends AbstractConnection implements Callback
+ {
+ public static final int IDLE = 0;
+ public static final int WRITING = 1;
+ public static final int PENDING = 2;
+
+ private final AtomicInteger state = new AtomicInteger();
+
+ public EchoConnection(EndPoint endp, Executor executor)
+ {
+ super(endp, executor);
+ }
+
+ @Override
+ public void onOpen()
+ {
+ super.onOpen();
+
+ // Declare interest for fill events.
+ fillInterested();
+ }
+
+ @Override
+ public void onFillable()
+ {
+ try
+ {
+ ByteBuffer buffer = BufferUtil.allocate(1024);
+ while (true)
+ {
+ int filled = getEndPoint().fill(buffer);
+ if (filled > 0)
+ {
+ // We have filled some bytes, echo them back.
+ if (write(buffer))
+ {
+ // If the write completed, continue to fill.
+ continue;
+ }
+ }
+ else if (filled == 0)
+ {
+ // No more bytes to read, declare
+ // again interest for fill events.
+ fillInterested();
+ }
+ else
+ {
+ // The other peer closed the connection.
+ close();
+ }
+ break;
+ }
+ }
+ catch (Throwable x)
+ {
+ getEndPoint().close(x);
+ }
+ }
+
+ private boolean write(ByteBuffer buffer)
+ {
+ // Check if we are writing concurrently.
+ if (!state.compareAndSet(IDLE, WRITING))
+ throw new WritePendingException();
+
+ // Write the buffer using "this" as a callback.
+ getEndPoint().write(this, buffer);
+
+ // Check if the write is already completed.
+ boolean writeIsPending = state.compareAndSet(WRITING, PENDING);
+
+ // Return true if the write was completed.
+ return !writeIsPending;
+ }
+
+ @Override
+ public void succeeded()
+ {
+ // The write is complete, reset the state.
+ int prevState = state.getAndSet(IDLE);
+
+ // If the write was pending we need
+ // to resume reading from the network.
+ if (prevState == PENDING)
+ onFillable();
+ }
+
+ @Override
+ public void failed(Throwable x)
+ {
+ getEndPoint().close(x);
+ }
+ }
+ // end::echo-correct[]
+ }
+}
diff --git a/jetty-documentation/src/main/java/embedded/client/SelectorManagerDocSnippets.java b/jetty-documentation/src/main/java/embedded/client/SelectorManagerDocSnippets.java
deleted file mode 100644
index a5b2239fb40..00000000000
--- a/jetty-documentation/src/main/java/embedded/client/SelectorManagerDocSnippets.java
+++ /dev/null
@@ -1,61 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
-// ------------------------------------------------------------------------
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.opensource.org/licenses/apache2.0.php
-//
-// You may elect to redistribute this code under either of these licenses.
-// ========================================================================
-//
-
-package embedded.client;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.nio.channels.ServerSocketChannel;
-import java.nio.channels.SocketChannel;
-import java.util.Map;
-
-import org.eclipse.jetty.io.SelectorManager;
-
-public class SelectorManagerDocSnippets
-{
- // tag::connect[]
- public void connect(SelectorManager selectorManager, Map context) throws IOException
- {
- String host = "host";
- int port = 8080;
-
- // Create an unconnected SocketChannel.
- SocketChannel socketChannel = SocketChannel.open();
- socketChannel.configureBlocking(false);
-
- // Connect and register to Jetty.
- if (socketChannel.connect(new InetSocketAddress(host, port)))
- selectorManager.accept(socketChannel, context);
- else
- selectorManager.connect(socketChannel, context);
- }
- // end::connect[]
-
- // tag::accept[]
- public void accept(ServerSocketChannel acceptor, SelectorManager selectorManager) throws IOException
- {
- // Wait until a client connects.
- SocketChannel socketChannel = acceptor.accept();
- socketChannel.configureBlocking(false);
-
- // Accept and register to Jetty.
- Object attachment = null;
- selectorManager.accept(socketChannel, attachment);
- }
- // end::accept[]
-}
From 31ba922ce05bb3d25d65dd13b40814dc92bd46c6 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Sun, 11 Aug 2019 16:03:45 +0200
Subject: [PATCH 002/101] Draft 2 for week 2.
Signed-off-by: Simone Bordet
---
.../main/asciidoc/embedded-guide/io-arch.adoc | 37 ++++++++++++++-----
.../embedded/SelectorManagerDocSnippets.java | 12 ++++--
2 files changed, 37 insertions(+), 12 deletions(-)
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc
index e677a5958fe..8fd79a91d3c 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc
@@ -101,9 +101,10 @@ The `EndPoint` and `Connection` pairs can be chained, for example in case of
encrypted communication using the TLS protocol.
There is an `EndPoint` and `Connection` TLS pair where the `EndPoint` reads the
encrypted bytes from the network and the `Connection` decrypts them; next in the
-chain there is an `EndPoint` and `Connection` pair where the `EndPoint` provides
-decrypted bytes and the `Connection` deserializes them into specific protocol
-objects (for example a HTTP/1.1 request object).
+chain there is an `EndPoint` and `Connection` pair where the `EndPoint` "reads"
+decrypted bytes (provided by the previous `Connection`) and the `Connection`
+deserializes them into specific protocol objects (for example HTTP/2 frame
+objects).
Certain protocols, such as WebSocket, start the communication with the server
using one protocol (e.g. HTTP/1.1), but then change the communication to use
@@ -155,8 +156,8 @@ is therefore writable again, the `SelectionKey.OP_WRITE` flag must be set.
In the Jetty I/O library, you can call `EndPoint.write(Callback, ByteBuffer...)`
to write the ``ByteBuffer``s and the `Callback` parameter is the object that is
-notified when the whole write is finished(i.e. _all_ ``ByteBuffer``s have been
-fully written).
+notified when the whole write is finished (i.e. _all_ ``ByteBuffer``s have been
+fully written, even if they are delayed by TCP congestion/uncongestion).
The `EndPoint` APIs abstract out the Java NIO details by providing non-blocking
APIs based on `Callback` objects for I/O operations.
@@ -176,7 +177,8 @@ by more abstract layers.
`EndPoint`
* `Connection.onClose(Throwable)`, invoked when the `Connection` is disassociated
from the `EndPoint`, where the `Throwable` parameter indicates whether the
-disassociation was due to an error
+disassociation was normal (when the parameter is `null`) or was due to an error
+(when the parameter is not `null`)
When a `Connection` is first created, it is not registered for any Java NIO
event.
@@ -208,7 +210,7 @@ A naive, but wrong, implementation may be the following:
include::{docbits}/embedded/SelectorManagerDocSnippets.java[tags=echo-wrong]
----
-NOTE: The implementation above is wrong and leads to `StackOverflowError`.
+WARNING: The implementation above is wrong and leads to `StackOverflowError`.
The problem with this implementation is that if the writes always complete
synchronously (i.e. without being delayed by TCP congestion), you end up with
@@ -224,7 +226,7 @@ Connection.onFillable()
...
----
-which leads to `StackOverflowException`.
+which leads to `StackOverflowError`.
This is a typical side effect of asynchronous programming using non-blocking
APIs, and happens in the Jetty I/O library as well.
@@ -236,4 +238,21 @@ A correct implementation is the following:
include::{docbits}/embedded/SelectorManagerDocSnippets.java[tags=echo-correct]
----
-The correct implementation performs the reads
+The correct implementation performs consecutive reads in a loop (rather than
+recursively), but _only_ if the correspondent write is completed successfully.
+
+In order to detect whether the write is completed, a concurrent state machine
+is used. This is necessary because the notification of the completion of the
+write may happen in a different thread, while the original writing thread
+may still be changing the state.
+
+The original writing thread starts moves the state from `IDLE` to `WRITING`,
+then issues the actual `write()` call.
+The original writing thread then assumes that the `write()` did not complete
+and tries to move to the `PENDING` state just after the `write()`.
+If it fails to move from the `WRITING` state to the `PENDING` state, it means
+that the write was completed.
+Otherwise, the write is now `PENDING` and waiting for the callback to be
+notified of the completion at a later time.
+When the callback is notified of the `write()` completion, it checks whether
+the `write()` was `PENDING`, and if it was it resumes reading.
diff --git a/jetty-documentation/src/main/java/embedded/SelectorManagerDocSnippets.java b/jetty-documentation/src/main/java/embedded/SelectorManagerDocSnippets.java
index 5e4f4b36c8f..3844c4f9dab 100644
--- a/jetty-documentation/src/main/java/embedded/SelectorManagerDocSnippets.java
+++ b/jetty-documentation/src/main/java/embedded/SelectorManagerDocSnippets.java
@@ -99,9 +99,9 @@ public class SelectorManagerDocSnippets
public void echoWrong()
{
// tag::echo-wrong[]
- class EchoConnection extends AbstractConnection implements Callback
+ class WrongEchoConnection extends AbstractConnection implements Callback
{
- public EchoConnection(EndPoint endp, Executor executor)
+ public WrongEchoConnection(EndPoint endp, Executor executor)
{
super(endp, executor);
}
@@ -204,19 +204,25 @@ public class SelectorManagerDocSnippets
// If the write completed, continue to fill.
continue;
}
+ else
+ {
+ // The write is pending, return to wait for completion.
+ return;
+ }
}
else if (filled == 0)
{
// No more bytes to read, declare
// again interest for fill events.
fillInterested();
+ return;
}
else
{
// The other peer closed the connection.
close();
+ return;
}
- break;
}
}
catch (Throwable x)
From 1393c0e92bc16efb852624bd3de3a575cafddfa7 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Wed, 19 Feb 2020 15:46:21 +0100
Subject: [PATCH 003/101] Issue #4400 - Review HttpClient's ContentProvider.
Introduced Request.Content with a reactive model to provide
request content.
Introduced RequestContentAdapter to wrap ContentProviders
into Request.Content.
Updated implementation to use the reactive model rather than
the old pull model.
Reimplemented all ContentProviders in terms of Request.Content.
Converted most of the tests from ContentProvider to Request.Content.
Updated proxy servlets and documentation.
Signed-off-by: Simone Bordet
---
.../jetty/embedded/ExampleServerTest.java | 4 +-
.../jetty/client/AsyncContentProvider.java | 4 +
.../client/AuthenticationProtocolHandler.java | 5 +-
.../org/eclipse/jetty/client/HttpClient.java | 6 +-
.../eclipse/jetty/client/HttpConnection.java | 21 +-
.../org/eclipse/jetty/client/HttpContent.java | 237 -------
.../eclipse/jetty/client/HttpReceiver.java | 2 +-
.../org/eclipse/jetty/client/HttpRequest.java | 27 +-
.../org/eclipse/jetty/client/HttpSender.java | 642 ++++--------------
.../eclipse/jetty/client/RequestNotifier.java | 4 +-
.../jetty/client/api/ContentProvider.java | 19 +
.../org/eclipse/jetty/client/api/Request.java | 151 ++++
.../jetty/client/http/HttpSenderOverHTTP.java | 222 +++---
.../internal/RequestContentAdapter.java | 329 +++++++++
.../client/util/AbstractRequestContent.java | 250 +++++++
.../util/AbstractTypedContentProvider.java | 4 +
.../client/util/AsyncRequestContent.java | 385 +++++++++++
.../util/ByteBufferContentProvider.java | 3 +
.../client/util/ByteBufferRequestContent.java | 94 +++
.../client/util/BytesContentProvider.java | 3 +
.../client/util/BytesRequestContent.java | 91 +++
.../client/util/DeferredContentProvider.java | 4 +
.../client/util/FormContentProvider.java | 3 +
.../jetty/client/util/FormRequestContent.java | 78 +++
.../util/InputStreamContentProvider.java | 3 +
.../util/InputStreamRequestContent.java | 146 ++++
.../util/InputStreamResponseListener.java | 23 +-
.../client/util/MultiPartContentProvider.java | 3 +
.../client/util/MultiPartRequestContent.java | 380 +++++++++++
.../util/OutputStreamContentProvider.java | 3 +
.../util/OutputStreamRequestContent.java | 125 ++++
.../client/util/PathContentProvider.java | 3 +
.../jetty/client/util/PathRequestContent.java | 170 +++++
.../client/util/StringContentProvider.java | 3 +
.../client/util/StringRequestContent.java | 52 ++
.../client/ClientConnectionCloseTest.java | 10 +-
.../jetty/client/ConnectionPoolTest.java | 4 +-
.../client/HttpClientAuthenticationTest.java | 78 +--
.../jetty/client/HttpClientFailureTest.java | 10 +-
.../jetty/client/HttpClientRedirectTest.java | 4 +-
.../client/HttpClientSynchronizationTest.java | 20 +-
.../eclipse/jetty/client/HttpClientTest.java | 79 +--
...pClientUploadDuringServerShutdownTest.java | 4 +-
.../client/HttpConnectionLifecycleTest.java | 4 +-
.../jetty/client/HttpRequestAbortTest.java | 6 +-
.../jetty/client/HttpResponseAbortTest.java | 17 +-
.../org/eclipse/jetty/client/api/Usage.java | 32 +-
.../client/http/HttpSenderOverHTTPTest.java | 8 +-
.../client/util/AsyncRequestContentTest.java | 151 ++++
.../util/DeferredContentProviderTest.java | 151 ----
.../util/InputStreamContentProviderTest.java | 161 -----
.../client/util/InputStreamContentTest.java | 266 ++++++++
...derTest.java => MultiPartContentTest.java} | 142 +---
.../util/RequestContentBehaviorTest.java | 342 ++++++++++
.../client/util/SPNEGOAuthenticationTest.java | 2 +-
.../client/util/TypedContentProviderTest.java | 4 +-
.../server/clients/http/http-client-api.adoc | 43 +-
.../fcgi/client/http/HttpSenderOverFCGI.java | 30 +-
.../jetty/fcgi/server/HttpClientTest.java | 43 +-
.../client/http/HttpSenderOverHTTP2.java | 107 +--
.../client/http/RequestTrailersTest.java | 24 +-
.../security/openid/OpenIdCredentials.java | 6 +-
.../TestJettyOSGiBootWithAnnotations.java | 10 +-
.../jetty/proxy/AsyncMiddleManServlet.java | 59 +-
.../jetty/proxy/AsyncProxyServlet.java | 34 +-
.../org/eclipse/jetty/proxy/ProxyServlet.java | 124 ++--
.../proxy/AsyncMiddleManServletTest.java | 67 +-
.../proxy/ForwardProxyTLSServerTest.java | 6 +-
.../jetty/proxy/ProxyServletFailureTest.java | 28 +-
.../jetty/proxy/ProxyServletLoadTest.java | 12 +-
.../eclipse/jetty/proxy/ProxyServletTest.java | 33 +-
.../server/MultiPartFormInputStreamTest.java | 2 +-
.../org/eclipse/jetty/servlet/FormTest.java | 10 +-
.../jetty/servlet/MultiPartServletTest.java | 24 +-
.../jetty/webapp/HugeResourceTest.java | 20 +-
.../tests/distribution/DemoBaseTests.java | 6 +-
.../jetty/http/client/AsyncIOServletTest.java | 78 +--
.../http/client/AsyncRequestContentTest.java | 94 ++-
.../http/client/ConnectionStatisticsTest.java | 4 +-
.../http/client/HttpClientContinueTest.java | 94 +--
.../jetty/http/client/HttpClientLoadTest.java | 6 +-
.../http/client/HttpClientStreamTest.java | 221 ++----
.../jetty/http/client/HttpClientTest.java | 9 +-
.../http/client/HttpClientTimeoutTest.java | 8 +-
.../jetty/http/client/HttpTrailersTest.java | 4 +-
.../jetty/http/client/ServerTimeoutsTest.java | 72 +-
.../eclipse/jetty/test/DigestPostTest.java | 52 +-
.../eclipse/jetty/JdbcLoginServiceTest.java | 9 +-
.../session/RequestDispatchedSessionTest.java | 5 +-
89 files changed, 4146 insertions(+), 2192 deletions(-)
delete mode 100644 jetty-client/src/main/java/org/eclipse/jetty/client/HttpContent.java
create mode 100644 jetty-client/src/main/java/org/eclipse/jetty/client/internal/RequestContentAdapter.java
create mode 100644 jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractRequestContent.java
create mode 100644 jetty-client/src/main/java/org/eclipse/jetty/client/util/AsyncRequestContent.java
create mode 100644 jetty-client/src/main/java/org/eclipse/jetty/client/util/ByteBufferRequestContent.java
create mode 100644 jetty-client/src/main/java/org/eclipse/jetty/client/util/BytesRequestContent.java
create mode 100644 jetty-client/src/main/java/org/eclipse/jetty/client/util/FormRequestContent.java
create mode 100644 jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamRequestContent.java
create mode 100644 jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartRequestContent.java
create mode 100644 jetty-client/src/main/java/org/eclipse/jetty/client/util/OutputStreamRequestContent.java
create mode 100644 jetty-client/src/main/java/org/eclipse/jetty/client/util/PathRequestContent.java
create mode 100644 jetty-client/src/main/java/org/eclipse/jetty/client/util/StringRequestContent.java
create mode 100644 jetty-client/src/test/java/org/eclipse/jetty/client/util/AsyncRequestContentTest.java
delete mode 100644 jetty-client/src/test/java/org/eclipse/jetty/client/util/DeferredContentProviderTest.java
delete mode 100644 jetty-client/src/test/java/org/eclipse/jetty/client/util/InputStreamContentProviderTest.java
create mode 100644 jetty-client/src/test/java/org/eclipse/jetty/client/util/InputStreamContentTest.java
rename jetty-client/src/test/java/org/eclipse/jetty/client/util/{MultiPartContentProviderTest.java => MultiPartContentTest.java} (78%)
create mode 100644 jetty-client/src/test/java/org/eclipse/jetty/client/util/RequestContentBehaviorTest.java
diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerTest.java
index 56d9e32007b..c1ef6421593 100644
--- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerTest.java
+++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerTest.java
@@ -21,7 +21,7 @@ package org.eclipse.jetty.embedded;
import java.net.URI;
import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.client.util.StringRequestContent;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.Server;
@@ -75,7 +75,7 @@ public class ExampleServerTest extends AbstractEmbeddedTest
String postBody = "Greetings from " + ExampleServerTest.class;
ContentResponse response = client.newRequest(uri)
.method(HttpMethod.POST)
- .content(new StringContentProvider(postBody))
+ .body(new StringRequestContent(postBody))
.send();
// Check the response status code
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/AsyncContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AsyncContentProvider.java
index 417d27d2e5c..ace7d76a9cc 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/AsyncContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AsyncContentProvider.java
@@ -21,10 +21,14 @@ package org.eclipse.jetty.client;
import java.util.EventListener;
import org.eclipse.jetty.client.api.ContentProvider;
+import org.eclipse.jetty.client.api.Request;
/**
* A {@link ContentProvider} that notifies listeners that content is available.
+ *
+ * @deprecated no replacement, use {@link Request.Content} instead.
*/
+@Deprecated
public interface AsyncContentProvider extends ContentProvider
{
/**
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java
index 878a639c2a1..c0cd8a0882c 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java
@@ -30,7 +30,6 @@ import java.util.regex.Pattern;
import org.eclipse.jetty.client.api.Authentication;
import org.eclipse.jetty.client.api.Authentication.HeaderInfo;
import org.eclipse.jetty.client.api.Connection;
-import org.eclipse.jetty.client.api.ContentProvider;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
@@ -187,8 +186,8 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
return;
}
- ContentProvider requestContent = request.getContent();
- if (requestContent != null && !requestContent.isReproducible())
+ Request.Content requestContent = request.getBody();
+ if (!requestContent.isReproducible())
{
if (LOG.isDebugEnabled())
LOG.debug("Request content not reproducible for {}", request);
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
index bbd56bb12b8..5a45927d063 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
@@ -53,7 +53,7 @@ import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
-import org.eclipse.jetty.client.util.FormContentProvider;
+import org.eclipse.jetty.client.util.FormRequestContent;
import org.eclipse.jetty.http.HttpCompliance;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
@@ -379,7 +379,7 @@ public class HttpClient extends ContainerLifeCycle
*/
public ContentResponse FORM(URI uri, Fields fields) throws InterruptedException, ExecutionException, TimeoutException
{
- return POST(uri).content(new FormContentProvider(fields)).send();
+ return POST(uri).body(new FormRequestContent(fields)).send();
}
/**
@@ -446,7 +446,7 @@ public class HttpClient extends ContainerLifeCycle
Request newRequest = newHttpRequest(oldRequest.getConversation(), newURI);
newRequest.method(oldRequest.getMethod())
.version(oldRequest.getVersion())
- .content(oldRequest.getContent())
+ .body(oldRequest.getBody())
.idleTimeout(oldRequest.getIdleTimeout(), TimeUnit.MILLISECONDS)
.timeout(oldRequest.getTimeout(), TimeUnit.MILLISECONDS)
.followRedirects(oldRequest.isFollowRedirects());
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
index 1cf2ef0b6a9..d719a7bf4fe 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
@@ -27,9 +27,9 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.jetty.client.api.Authentication;
-import org.eclipse.jetty.client.api.ContentProvider;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.client.util.BytesRequestContent;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpVersion;
@@ -129,11 +129,6 @@ public abstract class HttpConnection implements IConnection
if (normalized)
return;
- HttpVersion version = request.getVersion();
- HttpFields headers = request.getHeaders();
- ContentProvider content = request.getContent();
- ProxyConfiguration.Proxy proxy = destination.getProxy();
-
// Make sure the path is there
String path = request.getPath();
if (path.trim().length() == 0)
@@ -144,6 +139,7 @@ public abstract class HttpConnection implements IConnection
URI uri = request.getURI();
+ ProxyConfiguration.Proxy proxy = destination.getProxy();
if (proxy instanceof HttpProxy && !HttpClient.isSchemeSecure(request.getScheme()) && uri != null)
{
path = uri.toString();
@@ -151,6 +147,8 @@ public abstract class HttpConnection implements IConnection
}
// If we are HTTP 1.1, add the Host header
+ HttpVersion version = request.getVersion();
+ HttpFields headers = request.getHeaders();
if (version.getVersion() <= 11)
{
if (!headers.containsKey(HttpHeader.HOST.asString()))
@@ -158,13 +156,16 @@ public abstract class HttpConnection implements IConnection
}
// Add content headers
- if (content != null)
+ Request.Content content = request.getBody();
+ if (content == null)
+ {
+ request.body(new BytesRequestContent());
+ }
+ else
{
if (!headers.containsKey(HttpHeader.CONTENT_TYPE.asString()))
{
- String contentType = null;
- if (content instanceof ContentProvider.Typed)
- contentType = ((ContentProvider.Typed)content).getContentType();
+ String contentType = content.getContentType();
if (contentType != null)
{
headers.put(HttpHeader.CONTENT_TYPE, contentType);
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpContent.java
deleted file mode 100644
index 9559b43eece..00000000000
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpContent.java
+++ /dev/null
@@ -1,237 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-//
-// This program and the accompanying materials are made available under
-// the terms of the Eclipse Public License 2.0 which is available at
-// https://www.eclipse.org/legal/epl-2.0
-//
-// This Source Code may also be made available under the following
-// Secondary Licenses when the conditions for such availability set
-// forth in the Eclipse Public License, v. 2.0 are satisfied:
-// the Apache License v2.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.client;
-
-import java.io.Closeable;
-import java.nio.ByteBuffer;
-import java.util.Collections;
-import java.util.Iterator;
-
-import org.eclipse.jetty.client.api.ContentProvider;
-import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.IO;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- * {@link HttpContent} is a stateful, linear representation of the request content provided
- * by a {@link ContentProvider} that can be traversed one-way to obtain content buffers to
- * send to an HTTP server.
- *
- * {@link HttpContent} offers the notion of a one-way cursor to traverse the content.
- * The cursor starts in a virtual "before" position and can be advanced using {@link #advance()}
- * until it reaches a virtual "after" position where the content is fully consumed.
- *
- * At each valid (non-before and non-after) cursor position, {@link HttpContent} provides the following state:
- *
- *
the buffer containing the content to send, via {@link #getByteBuffer()}
- *
a copy of the content buffer that can be used for notifications, via {@link #getContent()}
- *
whether the buffer to write is the last one, via {@link #isLast()}
- *
- * {@link HttpContent} may not have content, if the related {@link ContentProvider} is {@code null}, and this
- * is reflected by {@link #hasContent()}.
- *
- * {@link HttpContent} may have {@link AsyncContentProvider deferred content}, in which case {@link #advance()}
- * moves the cursor to a position that provides {@code null} {@link #getByteBuffer() buffer} and
- * {@link #getContent() content}. When the deferred content is available, a further call to {@link #advance()}
- * will move the cursor to a position that provides non {@code null} buffer and content.
- */
-public class HttpContent implements Callback, Closeable
-{
- private static final Logger LOG = Log.getLogger(HttpContent.class);
- private static final ByteBuffer AFTER = ByteBuffer.allocate(0);
- private static final ByteBuffer CLOSE = ByteBuffer.allocate(0);
-
- private final ContentProvider provider;
- private final Iterator iterator;
- private ByteBuffer buffer;
- private ByteBuffer content;
- private boolean last;
-
- public HttpContent(ContentProvider provider)
- {
- this.provider = provider;
- this.iterator = provider == null ? Collections.emptyIterator() : provider.iterator();
- }
-
- /**
- * @return true if the buffer is the sentinel instance {@link CLOSE}
- */
- private static boolean isTheCloseBuffer(ByteBuffer buffer)
- {
- @SuppressWarnings("ReferenceEquality")
- boolean isTheCloseBuffer = (buffer == CLOSE);
- return isTheCloseBuffer;
- }
-
- /**
- * @return whether there is any content at all
- */
- public boolean hasContent()
- {
- return provider != null;
- }
-
- /**
- * @return whether the cursor points to the last content
- */
- public boolean isLast()
- {
- return last;
- }
-
- /**
- * @return the {@link ByteBuffer} containing the content at the cursor's position
- */
- public ByteBuffer getByteBuffer()
- {
- return buffer;
- }
-
- /**
- * @return a {@link ByteBuffer#slice()} of {@link #getByteBuffer()} at the cursor's position
- */
- public ByteBuffer getContent()
- {
- return content;
- }
-
- /**
- * Advances the cursor to the next block of content.
- *
- * The next block of content may be valid (which yields a non-null buffer
- * returned by {@link #getByteBuffer()}), but may also be deferred
- * (which yields a null buffer returned by {@link #getByteBuffer()}).
- *
- * If the block of content pointed by the new cursor position is valid, this method returns true.
- *
- * @return true if there is content at the new cursor's position, false otherwise.
- */
- public boolean advance()
- {
- if (iterator instanceof Synchronizable)
- {
- synchronized (((Synchronizable)iterator).getLock())
- {
- return advance(iterator);
- }
- }
- else
- {
- return advance(iterator);
- }
- }
-
- private boolean advance(Iterator iterator)
- {
- boolean hasNext = iterator.hasNext();
- ByteBuffer bytes = hasNext ? iterator.next() : null;
- boolean hasMore = hasNext && iterator.hasNext();
- boolean wasLast = last;
- last = !hasMore;
-
- if (hasNext)
- {
- buffer = bytes;
- content = bytes == null ? null : bytes.slice();
- if (LOG.isDebugEnabled())
- LOG.debug("Advanced content to {} chunk {}", hasMore ? "next" : "last", String.valueOf(bytes));
- return bytes != null;
- }
- else
- {
- // No more content, but distinguish between last and consumed.
- if (wasLast)
- {
- buffer = content = AFTER;
- if (LOG.isDebugEnabled())
- LOG.debug("Advanced content past last chunk");
- }
- else
- {
- buffer = content = CLOSE;
- if (LOG.isDebugEnabled())
- LOG.debug("Advanced content to last chunk");
- }
- return false;
- }
- }
-
- /**
- * @return whether the cursor has been advanced past the {@link #isLast() last} position.
- */
- @SuppressWarnings("ReferenceEquality")
- public boolean isConsumed()
- {
- return buffer == AFTER;
- }
-
- @Override
- public void succeeded()
- {
- if (isConsumed())
- return;
- if (isTheCloseBuffer(buffer))
- return;
- if (iterator instanceof Callback)
- ((Callback)iterator).succeeded();
- }
-
- @Override
- public void failed(Throwable x)
- {
- if (isConsumed())
- return;
- if (isTheCloseBuffer(buffer))
- return;
- if (iterator instanceof Callback)
- ((Callback)iterator).failed(x);
- }
-
- @Override
- public void close()
- {
- if (iterator instanceof Closeable)
- IO.close((Closeable)iterator);
- }
-
- @Override
- public String toString()
- {
- return String.format("%s@%x - has=%b,last=%b,consumed=%b,buffer=%s",
- getClass().getSimpleName(),
- hashCode(),
- hasContent(),
- isLast(),
- isConsumed(),
- BufferUtil.toDetailString(getContent()));
- }
-}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java
index c2fd19178f6..bb3c5d4e663 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java
@@ -528,7 +528,7 @@ public abstract class HttpReceiver
HttpResponse response = exchange.getResponse();
if (LOG.isDebugEnabled())
- LOG.debug("Response complete {}", response);
+ LOG.debug("Response complete {}, result: {}", response, result);
if (result != null)
{
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
index 0cc192f6cdd..e0919bc3f0d 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
@@ -49,8 +49,9 @@ import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.client.internal.RequestContentAdapter;
import org.eclipse.jetty.client.util.FutureResponseListener;
-import org.eclipse.jetty.client.util.PathContentProvider;
+import org.eclipse.jetty.client.util.PathRequestContent;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
@@ -81,7 +82,7 @@ public class HttpRequest implements Request
private long idleTimeout = -1;
private long timeout;
private long timeoutAt;
- private ContentProvider content;
+ private Content content;
private boolean followRedirects;
private List cookies;
private Map attributes;
@@ -647,7 +648,9 @@ public class HttpRequest implements Request
@Override
public ContentProvider getContent()
{
- return content;
+ if (content instanceof RequestContentAdapter)
+ return ((RequestContentAdapter)content).getContentProvider();
+ return null;
}
@Override
@@ -661,6 +664,18 @@ public class HttpRequest implements Request
{
if (contentType != null)
header(HttpHeader.CONTENT_TYPE, contentType);
+ return body(ContentProvider.toRequestContent(content));
+ }
+
+ @Override
+ public Content getBody()
+ {
+ return content;
+ }
+
+ @Override
+ public Request body(Content content)
+ {
this.content = content;
return this;
}
@@ -674,7 +689,7 @@ public class HttpRequest implements Request
@Override
public Request file(Path file, String contentType) throws IOException
{
- return content(new PathContentProvider(contentType, file));
+ return body(new PathRequestContent(contentType, file));
}
@Override
@@ -810,8 +825,8 @@ public class HttpRequest implements Request
{
if (aborted.compareAndSet(null, Objects.requireNonNull(cause)))
{
- if (content instanceof Callback)
- ((Callback)content).failed(cause);
+ if (content != null)
+ content.fail(cause);
return conversation.abort(cause);
}
return false;
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
index 0e2cc1280d6..d08a8321518 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
@@ -23,51 +23,37 @@ import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicReference;
-import org.eclipse.jetty.client.api.ContentProvider;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.IteratingCallback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
- * {@link HttpSender} abstracts the algorithm to send HTTP requests, so that subclasses only implement
- * the transport-specific code to send requests over the wire, implementing
- * {@link #sendHeaders(HttpExchange, HttpContent, Callback)} and
- * {@link #sendContent(HttpExchange, HttpContent, Callback)}.
- *
- * {@link HttpSender} governs two state machines.
- *
- * The request state machine is updated by {@link HttpSender} as the various steps of sending a request
- * are executed, see {@code RequestState}.
- * At any point in time, a user thread may abort the request, which may (if the request has not been
- * completely sent yet) move the request state machine to {@code RequestState#FAILURE}.
- * The request state machine guarantees that the request steps are executed (by I/O threads) only if
- * the request has not been failed already.
- *
- * The sender state machine is updated by {@link HttpSender} from three sources: deferred content notifications
- * (via {@link #onContent()}), 100-continue notifications (via {@link #proceed(HttpExchange, Throwable)})
- * and normal request send (via {@link #sendContent(HttpExchange, HttpContent, Callback)}).
- * This state machine must guarantee that the request sending is never executed concurrently: only one of
- * those sources may trigger the call to {@link #sendContent(HttpExchange, HttpContent, Callback)}.
+ *
HttpSender abstracts the algorithm to send HTTP requests, so that subclasses only
+ * implement the transport-specific code to send requests over the wire, implementing
+ * {@link #sendHeaders(HttpExchange, ByteBuffer, boolean, Callback)} and
+ * {@link #sendContent(HttpExchange, ByteBuffer, boolean, Callback)}.
+ *
HttpSender governs the request state machines, which is updated as the various
+ * steps of sending a request are executed, see {@code RequestState}.
+ * At any point in time, a user thread may abort the request, which may (if the request
+ * has not been completely sent yet) move the request state machine to {@code RequestState#FAILURE}.
+ * The request state machine guarantees that the request steps are executed (by I/O threads)
+ * only if the request has not been failed already.
*
* @see HttpReceiver
*/
-public abstract class HttpSender implements AsyncContentProvider.Listener
+public abstract class HttpSender
{
- protected static final Logger LOG = Log.getLogger(HttpSender.class);
+ private static final Logger LOG = Log.getLogger(HttpSender.class);
+ private final ContentConsumer consumer = new ContentConsumer();
private final AtomicReference requestState = new AtomicReference<>(RequestState.QUEUED);
- private final AtomicReference senderState = new AtomicReference<>(SenderState.IDLE);
- private final Callback commitCallback = new CommitCallback();
- private final IteratingCallback contentCallback = new ContentCallback();
- private final Callback lastCallback = new LastCallback();
private final HttpChannel channel;
- private HttpContent content;
+ private Request.Content.Subscription subscription;
private Throwable failure;
protected HttpSender(HttpChannel channel)
@@ -90,126 +76,22 @@ public abstract class HttpSender implements AsyncContentProvider.Listener
return requestState.get() == RequestState.FAILURE;
}
- @Override
- public void onContent()
- {
- HttpExchange exchange = getHttpExchange();
- if (exchange == null)
- return;
-
- while (true)
- {
- SenderState current = senderState.get();
- switch (current)
- {
- case IDLE:
- {
- SenderState newSenderState = SenderState.SENDING;
- if (updateSenderState(current, newSenderState))
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Deferred content available, {} -> {}", current, newSenderState);
- contentCallback.iterate();
- return;
- }
- break;
- }
- case SENDING:
- {
- SenderState newSenderState = SenderState.SENDING_WITH_CONTENT;
- if (updateSenderState(current, newSenderState))
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Deferred content available, {} -> {}", current, newSenderState);
- return;
- }
- break;
- }
- case EXPECTING:
- {
- SenderState newSenderState = SenderState.EXPECTING_WITH_CONTENT;
- if (updateSenderState(current, newSenderState))
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Deferred content available, {} -> {}", current, newSenderState);
- return;
- }
- break;
- }
- case PROCEEDING:
- {
- SenderState newSenderState = SenderState.PROCEEDING_WITH_CONTENT;
- if (updateSenderState(current, newSenderState))
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Deferred content available, {} -> {}", current, newSenderState);
- return;
- }
- break;
- }
- case SENDING_WITH_CONTENT:
- case EXPECTING_WITH_CONTENT:
- case PROCEEDING_WITH_CONTENT:
- case WAITING:
- case COMPLETED:
- case FAILED:
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Deferred content available, {}", current);
- return;
- }
- default:
- {
- illegalSenderState(current);
- return;
- }
- }
- }
- }
-
public void send(HttpExchange exchange)
{
+ Request request = exchange.getRequest();
+ Request.Content body = request.getBody();
+
+ consumer.exchange = exchange;
+ consumer.expect100 = expects100Continue(request);
+ subscription = body.subscribe(consumer, !consumer.expect100);
+
if (!queuedToBegin(exchange))
return;
- Request request = exchange.getRequest();
- ContentProvider contentProvider = request.getContent();
- HttpContent content = this.content = new HttpContent(contentProvider);
-
- SenderState newSenderState = SenderState.SENDING;
- if (expects100Continue(request))
- newSenderState = content.hasContent() ? SenderState.EXPECTING_WITH_CONTENT : SenderState.EXPECTING;
-
- out:
- while (true)
- {
- SenderState current = senderState.get();
- switch (current)
- {
- case IDLE:
- case COMPLETED:
- {
- if (updateSenderState(current, newSenderState))
- break out;
- break;
- }
- default:
- {
- illegalSenderState(current);
- return;
- }
- }
- }
-
- // Setting the listener may trigger calls to onContent() by other
- // threads so we must set it only after the sender state has been updated
- if (contentProvider instanceof AsyncContentProvider)
- ((AsyncContentProvider)contentProvider).setListener(this);
-
if (!beginToHeaders(exchange))
return;
- sendHeaders(exchange, content, commitCallback);
+ demand();
}
protected boolean expects100Continue(Request request)
@@ -353,6 +235,20 @@ public abstract class HttpSender implements AsyncContentProvider.Listener
executeAbort(exchange, failure);
}
+ private void demand()
+ {
+ try
+ {
+ subscription.demand();
+ }
+ catch (Throwable x)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug(x);
+ anyToFailure(x);
+ }
+ }
+
private void executeAbort(HttpExchange exchange, Throwable failure)
{
try
@@ -415,142 +311,62 @@ public abstract class HttpSender implements AsyncContentProvider.Listener
}
/**
- * Implementations should send the HTTP headers over the wire, possibly with some content,
- * in a single write, and notify the given {@code callback} of the result of this operation.
- *
- * If there is more content to send, then {@link #sendContent(HttpExchange, HttpContent, Callback)}
- * will be invoked.
+ *
Implementations should send the HTTP headers over the wire, possibly with some content,
+ * in a single write, and notify the given {@code callback} of the result of this operation.
+ *
If there is more content to send, then {@link #sendContent(HttpExchange, ByteBuffer, boolean, Callback)}
+ * will be invoked.
*
- * @param exchange the exchange to send
- * @param content the content to send
+ * @param exchange the exchange
+ * @param contentBuffer the content to send
+ * @param lastContent whether the content is the last content to send
* @param callback the callback to notify
*/
- protected abstract void sendHeaders(HttpExchange exchange, HttpContent content, Callback callback);
+ protected abstract void sendHeaders(HttpExchange exchange, ByteBuffer contentBuffer, boolean lastContent, Callback callback);
/**
- * Implementations should send the content at the {@link HttpContent} cursor position over the wire.
- *
- * The {@link HttpContent} cursor is advanced by HttpSender at the right time, and if more
- * content needs to be sent, this method is invoked again; subclasses need only to send the content
- * at the {@link HttpContent} cursor position.
- *
- * This method is invoked one last time when {@link HttpContent#isConsumed()} is true and therefore
- * there is no actual content to send.
- * This is done to allow subclasses to write "terminal" bytes (such as the terminal chunk when the
- * transfer encoding is chunked) if their protocol needs to.
+ *
Implementations should send the given HTTP content over the wire.
*
- * @param exchange the exchange to send
- * @param content the content to send
+ * @param exchange the exchange
+ * @param contentBuffer the content to send
+ * @param lastContent whether the content is the last content to send
* @param callback the callback to notify
*/
- protected abstract void sendContent(HttpExchange exchange, HttpContent content, Callback callback);
+ protected abstract void sendContent(HttpExchange exchange, ByteBuffer contentBuffer, boolean lastContent, Callback callback);
protected void reset()
{
- HttpContent content = this.content;
- this.content = null;
- content.close();
- senderState.set(SenderState.COMPLETED);
+ consumer.reset();
}
protected void dispose()
{
- HttpContent content = this.content;
- this.content = null;
- if (content != null)
- content.close();
- senderState.set(SenderState.FAILED);
}
public void proceed(HttpExchange exchange, Throwable failure)
{
- if (!expects100Continue(exchange.getRequest()))
- return;
-
- if (failure != null)
- {
+ consumer.expect100 = false;
+ if (failure == null)
+ demand();
+ else
anyToFailure(failure);
- return;
- }
-
- while (true)
- {
- SenderState current = senderState.get();
- switch (current)
- {
- case EXPECTING:
- {
- // We are still sending the headers, but we already got the 100 Continue.
- if (updateSenderState(current, SenderState.PROCEEDING))
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Proceeding while expecting");
- return;
- }
- break;
- }
- case EXPECTING_WITH_CONTENT:
- {
- // More deferred content was submitted to onContent(), we already
- // got the 100 Continue, but we may be still sending the headers
- // (for example, with SSL we may have sent the encrypted data,
- // received the 100 Continue but not yet updated the decrypted
- // WriteFlusher so sending more content now may result in a
- // WritePendingException).
- if (updateSenderState(current, SenderState.PROCEEDING_WITH_CONTENT))
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Proceeding while scheduled");
- return;
- }
- break;
- }
- case WAITING:
- {
- // We received the 100 Continue, now send the content if any.
- if (updateSenderState(current, SenderState.SENDING))
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Proceeding while waiting");
- contentCallback.iterate();
- return;
- }
- break;
- }
- case FAILED:
- {
- return;
- }
- default:
- {
- illegalSenderState(current);
- return;
- }
- }
- }
}
public boolean abort(HttpExchange exchange, Throwable failure)
{
// Update the state to avoid more request processing.
boolean terminate;
- out:
while (true)
{
RequestState current = requestState.get();
- switch (current)
+ if (current == RequestState.FAILURE)
{
- case FAILURE:
+ return false;
+ }
+ else
+ {
+ if (updateRequestState(current, RequestState.FAILURE))
{
- return false;
- }
- default:
- {
- if (updateRequestState(current, RequestState.FAILURE))
- {
- terminate = current != RequestState.TRANSIENT;
- break out;
- }
+ terminate = current != RequestState.TRANSIENT;
break;
}
}
@@ -590,27 +406,13 @@ public abstract class HttpSender implements AsyncContentProvider.Listener
return updated;
}
- private boolean updateSenderState(SenderState from, SenderState to)
- {
- boolean updated = senderState.compareAndSet(from, to);
- if (!updated && LOG.isDebugEnabled())
- LOG.debug("SenderState update failed: {} -> {}: {}", from, to, senderState.get());
- return updated;
- }
-
- private void illegalSenderState(SenderState current)
- {
- anyToFailure(new IllegalStateException("Expected " + current + " found " + senderState.get() + " instead"));
- }
-
@Override
public String toString()
{
- return String.format("%s@%x(req=%s,snd=%s,failure=%s)",
+ return String.format("%s@%x(req=%s,failure=%s)",
getClass().getSimpleName(),
hashCode(),
requestState,
- senderState,
failure);
}
@@ -649,286 +451,98 @@ public abstract class HttpSender implements AsyncContentProvider.Listener
FAILURE
}
- /**
- * The sender states {@link HttpSender} goes through when sending a request.
- */
- private enum SenderState
+ private class ContentConsumer implements Request.Content.Consumer, Callback
{
- /**
- * {@link HttpSender} is not sending request headers nor request content
- */
- IDLE,
- /**
- * {@link HttpSender} is sending the request header or request content
- */
- SENDING,
- /**
- * {@link HttpSender} is currently sending the request, and deferred content is available to be sent
- */
- SENDING_WITH_CONTENT,
- /**
- * {@link HttpSender} is sending the headers but will wait for 100 Continue before sending the content
- */
- EXPECTING,
- /**
- * {@link HttpSender} is currently sending the headers, will wait for 100 Continue, and deferred content is available to be sent
- */
- EXPECTING_WITH_CONTENT,
- /**
- * {@link HttpSender} has sent the headers and is waiting for 100 Continue
- */
- WAITING,
- /**
- * {@link HttpSender} is sending the headers, while 100 Continue has arrived
- */
- PROCEEDING,
- /**
- * {@link HttpSender} is sending the headers, while 100 Continue has arrived, and deferred content is available to be sent
- */
- PROCEEDING_WITH_CONTENT,
- /**
- * {@link HttpSender} has finished to send the request
- */
- COMPLETED,
- /**
- * {@link HttpSender} has failed to send the request
- */
- FAILED
- }
+ private HttpExchange exchange;
+ private boolean expect100;
+ private ByteBuffer contentBuffer;
+ private boolean lastContent;
+ private Callback callback;
+ private boolean committed;
+
+ private void reset()
+ {
+ exchange = null;
+ contentBuffer = null;
+ lastContent = false;
+ callback = null;
+ committed = false;
+ }
+
+ @Override
+ public void onContent(ByteBuffer buffer, boolean last, Callback callback)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Content {} last={} for {}", BufferUtil.toDetailString(buffer), last, exchange.getRequest());
+ this.contentBuffer = buffer.slice();
+ this.lastContent = last;
+ this.callback = callback;
+ if (committed)
+ sendContent(exchange, buffer, last, this);
+ else
+ sendHeaders(exchange, buffer, last, this);
+ }
+
+ @Override
+ public void onFailure(Throwable failure)
+ {
+ failed(failure);
+ }
- private class CommitCallback implements Callback
- {
@Override
public void succeeded()
{
- try
+ boolean proceed = false;
+ if (committed)
{
- HttpContent content = HttpSender.this.content;
- if (content == null)
- return;
- content.succeeded();
- process();
- }
- catch (Throwable x)
- {
- anyToFailure(x);
- }
- }
-
- @Override
- public void failed(Throwable failure)
- {
- HttpContent content = HttpSender.this.content;
- if (content == null)
- return;
- content.failed(failure);
- anyToFailure(failure);
- }
-
- private void process() throws Exception
- {
- HttpExchange exchange = getHttpExchange();
- if (exchange == null)
- return;
-
- if (!headersToCommit(exchange))
- return;
-
- HttpContent content = HttpSender.this.content;
- if (content == null)
- return;
-
- if (!content.hasContent())
- {
- // No content to send, we are done.
- someToSuccess(exchange);
+ proceed = someToContent(exchange, contentBuffer);
}
else
{
- // Was any content sent while committing?
- ByteBuffer contentBuffer = content.getContent();
- if (contentBuffer != null)
+ committed = true;
+ if (headersToCommit(exchange))
{
- if (!someToContent(exchange, contentBuffer))
- return;
- }
-
- while (true)
- {
- SenderState current = senderState.get();
- switch (current)
- {
- case SENDING:
- {
- contentCallback.iterate();
- return;
- }
- case SENDING_WITH_CONTENT:
- {
- // We have deferred content to send.
- updateSenderState(current, SenderState.SENDING);
- break;
- }
- case EXPECTING:
- {
- // We sent the headers, wait for the 100 Continue response.
- if (updateSenderState(current, SenderState.WAITING))
- return;
- break;
- }
- case EXPECTING_WITH_CONTENT:
- {
- // We sent the headers, we have deferred content to send,
- // wait for the 100 Continue response.
- if (updateSenderState(current, SenderState.WAITING))
- return;
- break;
- }
- case PROCEEDING:
- {
- // We sent the headers, we have the 100 Continue response,
- // we have no content to send.
- if (updateSenderState(current, SenderState.IDLE))
- return;
- break;
- }
- case PROCEEDING_WITH_CONTENT:
- {
- // We sent the headers, we have the 100 Continue response,
- // we have deferred content to send.
- updateSenderState(current, SenderState.SENDING);
- break;
- }
- case FAILED:
- {
- return;
- }
- default:
- {
- illegalSenderState(current);
- return;
- }
- }
+ proceed = true;
+ // Was any content sent while committing?
+ if (contentBuffer.hasRemaining())
+ proceed = someToContent(exchange, contentBuffer);
}
}
- }
- }
- private class ContentCallback extends IteratingCallback
- {
- @Override
- protected Action process() throws Exception
- {
- HttpExchange exchange = getHttpExchange();
- if (exchange == null)
- return Action.IDLE;
+ // Succeed the content callback only after emitting the request content event.
+ callback.succeeded();
- HttpContent content = HttpSender.this.content;
- if (content == null)
- return Action.IDLE;
+ // There was some concurrent error?
+ if (!proceed)
+ return;
- while (true)
+ if (lastContent)
+ {
+ someToSuccess(exchange);
+ }
+ else if (expect100)
{
- boolean advanced = content.advance();
- boolean lastContent = content.isLast();
if (LOG.isDebugEnabled())
- LOG.debug("Content present {}, last {}, consumed {} for {}", advanced, lastContent, content.isConsumed(), exchange.getRequest());
-
- if (advanced)
- {
- sendContent(exchange, content, this);
- return Action.SCHEDULED;
- }
-
- if (lastContent)
- {
- sendContent(exchange, content, lastCallback);
- return Action.IDLE;
- }
-
- SenderState current = senderState.get();
- switch (current)
- {
- case SENDING:
- {
- if (updateSenderState(current, SenderState.IDLE))
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Content is deferred for {}", exchange.getRequest());
- return Action.IDLE;
- }
- break;
- }
- case SENDING_WITH_CONTENT:
- {
- updateSenderState(current, SenderState.SENDING);
- break;
- }
- default:
- {
- illegalSenderState(current);
- return Action.IDLE;
- }
- }
+ LOG.debug("Expecting 100 Continue for {}", exchange.getRequest());
+ }
+ else
+ {
+ demand();
}
}
@Override
- public void succeeded()
+ public void failed(Throwable x)
{
- HttpExchange exchange = getHttpExchange();
- if (exchange == null)
- return;
- HttpContent content = HttpSender.this.content;
- if (content == null)
- return;
- content.succeeded();
- ByteBuffer buffer = content.getContent();
- someToContent(exchange, buffer);
- super.succeeded();
+ if (callback != null)
+ callback.failed(x);
+ anyToFailure(x);
}
@Override
- public void onCompleteFailure(Throwable failure)
+ public InvocationType getInvocationType()
{
- HttpContent content = HttpSender.this.content;
- if (content == null)
- return;
- content.failed(failure);
- anyToFailure(failure);
- }
-
- @Override
- protected void onCompleteSuccess()
- {
- // Nothing to do, since we always return IDLE from process().
- // Termination is obtained via LastCallback.
- }
- }
-
- private class LastCallback implements Callback
- {
- @Override
- public void succeeded()
- {
- HttpExchange exchange = getHttpExchange();
- if (exchange == null)
- return;
- HttpContent content = HttpSender.this.content;
- if (content == null)
- return;
- content.succeeded();
- someToSuccess(exchange);
- }
-
- @Override
- public void failed(Throwable failure)
- {
- HttpContent content = HttpSender.this.content;
- if (content == null)
- return;
- content.failed(failure);
- anyToFailure(failure);
+ return InvocationType.NON_BLOCKING;
}
}
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/RequestNotifier.java b/jetty-client/src/main/java/org/eclipse/jetty/client/RequestNotifier.java
index 1a2819e0ccd..c12974c1f6b 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/RequestNotifier.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/RequestNotifier.java
@@ -158,10 +158,10 @@ public class RequestNotifier
public void notifyContent(Request request, ByteBuffer content)
{
- // Slice the buffer to avoid that listeners peek into data they should not look at.
- content = content.slice();
if (!content.hasRemaining())
return;
+ // Slice the buffer to avoid that listeners peek into data they should not look at.
+ content = content.slice();
// Optimized to avoid allocations of iterator instances.
List requestListeners = request.getRequestListeners(null);
for (int i = 0; i < requestListeners.size(); ++i)
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentProvider.java
index f9a705af082..3d43110fdc3 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentProvider.java
@@ -23,6 +23,7 @@ import java.nio.ByteBuffer;
import java.util.Iterator;
import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.internal.RequestContentAdapter;
import org.eclipse.jetty.client.util.ByteBufferContentProvider;
import org.eclipse.jetty.client.util.PathContentProvider;
@@ -41,9 +42,24 @@ import org.eclipse.jetty.client.util.PathContentProvider;
* header set by applications; if the length is negative, it typically removes
* any {@code Content-Length} header set by applications, resulting in chunked
* content (i.e. {@code Transfer-Encoding: chunked}) being sent to the server.
+ *
+ * @deprecated use {@link Request.Content} instead, or {@link #toRequestContent(ContentProvider)}
+ * to convert ContentProvider to {@link Request.Content}.
*/
+@Deprecated
public interface ContentProvider extends Iterable
{
+ /**
+ *
Converts a ContentProvider to a {@link Request.Content}.
+ *
+ * @param provider the ContentProvider to convert
+ * @return a {@link Request.Content} that wraps the ContentProvider
+ */
+ public static Request.Content toRequestContent(ContentProvider provider)
+ {
+ return new RequestContentAdapter(provider);
+ }
+
/**
* @return the content length, if known, or -1 if the content length is unknown
*/
@@ -68,7 +84,10 @@ public interface ContentProvider extends Iterable
/**
* An extension of {@link ContentProvider} that provides a content type string
* to be used as a {@code Content-Type} HTTP header in requests.
+ *
+ * @deprecated use {@link Request.Content} instead
*/
+ @Deprecated
public interface Typed extends ContentProvider
{
/**
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java
index 8b7d97d1248..51c2e6ff22b 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java
@@ -19,6 +19,7 @@
package org.eclipse.jetty.client.api;
import java.io.IOException;
+import java.io.InputStream;
import java.net.HttpCookie;
import java.net.URI;
import java.net.URLEncoder;
@@ -37,6 +38,7 @@ import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Fields;
/**
@@ -216,22 +218,39 @@ public interface Request
/**
* @return the content provider of this request
+ * @deprecated use {@link #getBody()} instead
*/
+ @Deprecated
ContentProvider getContent();
/**
* @param content the content provider of this request
* @return this request object
+ * @deprecated use {@link #body(Content)} instead
*/
+ @Deprecated
Request content(ContentProvider content);
/**
* @param content the content provider of this request
* @param contentType the content type
* @return this request object
+ * @deprecated use {@link #body(Content)} instead
*/
+ @Deprecated
Request content(ContentProvider content, String contentType);
+ /**
+ * @return the request content of this request
+ */
+ Content getBody();
+
+ /**
+ * @param content the request content of this request
+ * @return this request object
+ */
+ Request body(Content content);
+
/**
* Shortcut method to specify a file as a content for this request, with the default content type of
* "application/octect-stream".
@@ -615,4 +634,136 @@ public interface Request
{
}
}
+
+ /**
+ *
A reactive model to produce request content, similar to {@link java.util.concurrent.Flow.Publisher}.
+ *
Implementations receive the content consumer via {@link #subscribe(Consumer, boolean)},
+ * and return a {@link Subscription} as the link between producer and consumer.
+ *
Content producers must notify content to the consumer only if there is demand.
+ *
Content consumers can generate demand for content by invoking {@link Subscription#demand()}.
+ */
+ public interface Content
+ {
+ /**
+ * @return the content type string such as "application/octet-stream" or
+ * "application/json;charset=UTF8", or null if no content type must be set
+ */
+ public default String getContentType()
+ {
+ return "application/octet-stream";
+ }
+
+ /**
+ * @return the content length, if known, or -1 if the content length is unknown
+ */
+ public default long getLength()
+ {
+ return -1;
+ }
+
+ /**
+ *
Whether this content producer can produce exactly the same content more
+ * than once.
+ *
Implementations should return {@code true} only if the content can be
+ * produced more than once, which means that {@link #subscribe(Consumer, boolean)}
+ * may be called again.
+ *
The {@link HttpClient} implementation may use this method in particular
+ * cases where it detects that it is safe to retry a request that failed.
+ *
+ * @return whether the content can be produced more than once
+ */
+ public default boolean isReproducible()
+ {
+ return false;
+ }
+
+ /**
+ *
Initializes this content producer with the content consumer, and with
+ * the indication of whether initial content, if present, must be emitted
+ * upon the initial demand of content (to support delaying the send of the
+ * request content in case of {@code Expect: 100-Continue} when
+ * {@code emitInitialContent} is {@code false}).
+ *
+ * @param consumer the content consumer to invoke when there is demand for content
+ * @param emitInitialContent whether to emit initial content, if present
+ * @return the Subscription that links this producer to the consumer
+ */
+ public Subscription subscribe(Consumer consumer, boolean emitInitialContent);
+
+ /**
+ *
Fails this request content, possibly failing and discarding accumulated
+ * content that was not demanded.
+ *
The failure may be notified to the consumer at a later time, when the
+ * consumer demands for content.
+ *
Typical failure: the request being aborted by user code, or idle timeouts.
+ *
+ * @param failure the reason of the failure
+ */
+ public default void fail(Throwable failure)
+ {
+ }
+
+ /**
+ *
A reactive model to consume request content, similar to {@link java.util.concurrent.Flow.Subscriber}.
+ *
Callback methods {@link #onContent(ByteBuffer, boolean, Callback)} and {@link #onFailure(Throwable)}
+ * are invoked in strict sequential order and never concurrently, although possibly by different threads.
+ */
+ public interface Consumer
+ {
+ /**
+ *
Callback method invoked by the producer when there is content available
+ * and there is demand for content.
+ *
The {@code callback} is associated with the {@code buffer} to
+ * signal when the content buffer has been consumed.
+ *
Failing the {@code callback} does not have any effect on content
+ * production. To stop the content production, the consumer must call
+ * {@link Subscription#fail(Throwable)}.
+ *
In case an exception is thrown by this method, it is equivalent to
+ * a call to {@link Subscription#fail(Throwable)}.
+ *
+ * @param buffer the content buffer to consume
+ * @param last whether it's the last content
+ * @param callback a callback to invoke when the content buffer is consumed
+ */
+ public void onContent(ByteBuffer buffer, boolean last, Callback callback);
+
+ /**
+ *
Callback method invoked by the producer when it failed to produce content.
+ *
Typical failure: a producer getting an exception while reading from an
+ * {@link InputStream} to produce content.
+ *
+ * @param failure the reason of the failure
+ */
+ public default void onFailure(Throwable failure)
+ {
+ }
+ }
+
+ /**
+ *
The link between a content producer and a content consumer.
+ *
Content consumers can demand more content via {@link #demand()},
+ * or ask the content producer to stop producing content via
+ * {@link #fail(Throwable)}.
Demands more content, which eventually results in
+ * {@link Consumer#onContent(ByteBuffer, boolean, Callback)} to be invoked.
+ */
+ public void demand();
+
+ /**
+ *
Fails the subscription, notifying the content producer to stop producing
+ * content.
+ *
Typical failure: a proxy consumer waiting for more content (or waiting
+ * to demand content) that is failed by an error response from the server.
+ *
+ * @param failure the reason of the failure
+ */
+ public default void fail(Throwable failure)
+ {
+ }
+ }
+ }
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpSenderOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpSenderOverHTTP.java
index 411cc48713b..8654f2ed87d 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpSenderOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpSenderOverHTTP.java
@@ -21,12 +21,11 @@ package org.eclipse.jetty.client.http;
import java.nio.ByteBuffer;
import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.HttpContent;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.HttpRequest;
import org.eclipse.jetty.client.HttpRequestException;
import org.eclipse.jetty.client.HttpSender;
-import org.eclipse.jetty.client.api.ContentProvider;
+import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.MetaData;
@@ -35,10 +34,21 @@ import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IteratingCallback;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
public class HttpSenderOverHTTP extends HttpSender
{
+ private static final Logger LOG = Log.getLogger(HttpSenderOverHTTP.class);
+
+ private final IteratingCallback headersCallback = new HeadersCallback();
+ private final IteratingCallback contentCallback = new ContentCallback();
private final HttpGenerator generator = new HttpGenerator();
+ private HttpExchange exchange;
+ private MetaData.Request metaData;
+ private ByteBuffer contentBuffer;
+ private boolean lastContent;
+ private Callback callback;
private boolean shutdown;
public HttpSenderOverHTTP(HttpChannelOverHTTP channel)
@@ -53,11 +63,26 @@ public class HttpSenderOverHTTP extends HttpSender
}
@Override
- protected void sendHeaders(HttpExchange exchange, HttpContent content, Callback callback)
+ protected void sendHeaders(HttpExchange exchange, ByteBuffer contentBuffer, boolean lastContent, Callback callback)
{
try
{
- new HeadersCallback(exchange, content, callback).iterate();
+ this.exchange = exchange;
+ this.contentBuffer = contentBuffer;
+ this.lastContent = lastContent;
+ this.callback = callback;
+ HttpRequest request = exchange.getRequest();
+ Request.Content requestContent = request.getBody();
+ long contentLength = requestContent == null ? -1 : requestContent.getLength();
+ String path = request.getPath();
+ String query = request.getQuery();
+ if (query != null)
+ path += "?" + query;
+ metaData = new MetaData.Request(request.getMethod(), new HttpURI(path), request.getVersion(), request.getHeaders(), contentLength);
+ metaData.setTrailerSupplier(request.getTrailers());
+ if (LOG.isDebugEnabled())
+ LOG.debug("Sending headers with content {} last={} for {}", BufferUtil.toDetailString(contentBuffer), lastContent, exchange.getRequest());
+ headersCallback.iterate();
}
catch (Throwable x)
{
@@ -68,67 +93,17 @@ public class HttpSenderOverHTTP extends HttpSender
}
@Override
- protected void sendContent(HttpExchange exchange, HttpContent content, Callback callback)
+ protected void sendContent(HttpExchange exchange, ByteBuffer contentBuffer, boolean lastContent, Callback callback)
{
try
{
- HttpClient httpClient = getHttpChannel().getHttpDestination().getHttpClient();
- ByteBufferPool bufferPool = httpClient.getByteBufferPool();
- boolean useDirectByteBuffers = httpClient.isUseOutputDirectByteBuffers();
- ByteBuffer chunk = null;
- while (true)
- {
- ByteBuffer contentBuffer = content.getByteBuffer();
- boolean lastContent = content.isLast();
- HttpGenerator.Result result = generator.generateRequest(null, null, chunk, contentBuffer, lastContent);
- if (LOG.isDebugEnabled())
- LOG.debug("Generated content ({} bytes) - {}/{}",
- contentBuffer == null ? -1 : contentBuffer.remaining(),
- result, generator);
- switch (result)
- {
- case NEED_CHUNK:
- {
- chunk = bufferPool.acquire(HttpGenerator.CHUNK_SIZE, useDirectByteBuffers);
- break;
- }
- case NEED_CHUNK_TRAILER:
- {
- chunk = bufferPool.acquire(httpClient.getRequestBufferSize(), useDirectByteBuffers);
- break;
- }
- case FLUSH:
- {
- EndPoint endPoint = getHttpChannel().getHttpConnection().getEndPoint();
- if (chunk != null)
- endPoint.write(new ByteBufferRecyclerCallback(callback, bufferPool, chunk), chunk, contentBuffer);
- else
- endPoint.write(callback, contentBuffer);
- return;
- }
- case SHUTDOWN_OUT:
- {
- shutdownOutput();
- break;
- }
- case CONTINUE:
- {
- if (lastContent)
- break;
- callback.succeeded();
- return;
- }
- case DONE:
- {
- callback.succeeded();
- return;
- }
- default:
- {
- throw new IllegalStateException(result.toString());
- }
- }
- }
+ this.exchange = exchange;
+ this.contentBuffer = contentBuffer;
+ this.lastContent = lastContent;
+ this.callback = callback;
+ if (LOG.isDebugEnabled())
+ LOG.debug("Sending content {} last={} for {}", BufferUtil.toDetailString(contentBuffer), lastContent, exchange.getRequest());
+ contentCallback.iterate();
}
catch (Throwable x)
{
@@ -141,6 +116,8 @@ public class HttpSenderOverHTTP extends HttpSender
@Override
protected void reset()
{
+ headersCallback.reset();
+ contentCallback.reset();
generator.reset();
super.reset();
}
@@ -173,54 +150,30 @@ public class HttpSenderOverHTTP extends HttpSender
private class HeadersCallback extends IteratingCallback
{
- private final HttpExchange exchange;
- private final Callback callback;
- private final MetaData.Request metaData;
private ByteBuffer headerBuffer;
private ByteBuffer chunkBuffer;
- private ByteBuffer contentBuffer;
- private boolean lastContent;
private boolean generated;
- public HeadersCallback(HttpExchange exchange, HttpContent content, Callback callback)
+ private HeadersCallback()
{
super(false);
- this.exchange = exchange;
- this.callback = callback;
-
- HttpRequest request = exchange.getRequest();
- ContentProvider requestContent = request.getContent();
- long contentLength = requestContent == null ? -1 : requestContent.getLength();
- String path = request.getPath();
- String query = request.getQuery();
- if (query != null)
- path += "?" + query;
- metaData = new MetaData.Request(request.getMethod(), new HttpURI(path), request.getVersion(), request.getHeaders(), contentLength);
- metaData.setTrailerSupplier(request.getTrailers());
-
- if (!expects100Continue(request))
- {
- content.advance();
- contentBuffer = content.getByteBuffer();
- lastContent = content.isLast();
- }
}
@Override
protected Action process() throws Exception
{
+ HttpClient httpClient = getHttpChannel().getHttpDestination().getHttpClient();
+ ByteBufferPool byteBufferPool = httpClient.getByteBufferPool();
+ boolean useDirectByteBuffers = httpClient.isUseOutputDirectByteBuffers();
while (true)
{
HttpGenerator.Result result = generator.generateRequest(metaData, headerBuffer, chunkBuffer, contentBuffer, lastContent);
if (LOG.isDebugEnabled())
- LOG.debug("Generated headers ({} bytes), chunk ({} bytes), content ({} bytes) - {}/{}",
+ LOG.debug("Generated headers ({} bytes), chunk ({} bytes), content ({} bytes) - {}/{} for {}",
headerBuffer == null ? -1 : headerBuffer.remaining(),
chunkBuffer == null ? -1 : chunkBuffer.remaining(),
contentBuffer == null ? -1 : contentBuffer.remaining(),
- result, generator);
- HttpClient httpClient = getHttpChannel().getHttpDestination().getHttpClient();
- ByteBufferPool byteBufferPool = httpClient.getByteBufferPool();
- boolean useDirectByteBuffers = httpClient.isUseOutputDirectByteBuffers();
+ result, generator, exchange.getRequest());
switch (result)
{
case NEED_HEADER:
@@ -328,37 +281,86 @@ public class HttpSenderOverHTTP extends HttpSender
}
}
- private class ByteBufferRecyclerCallback extends Callback.Nested
+ private class ContentCallback extends IteratingCallback
{
- private final ByteBufferPool pool;
- private final ByteBuffer[] buffers;
+ private ByteBuffer chunkBuffer;
- private ByteBufferRecyclerCallback(Callback callback, ByteBufferPool pool, ByteBuffer... buffers)
+ public ContentCallback()
{
- super(callback);
- this.pool = pool;
- this.buffers = buffers;
+ super(false);
}
@Override
- public void succeeded()
+ protected Action process() throws Exception
{
- for (ByteBuffer buffer : buffers)
+ HttpClient httpClient = getHttpChannel().getHttpDestination().getHttpClient();
+ ByteBufferPool bufferPool = httpClient.getByteBufferPool();
+ boolean useDirectByteBuffers = httpClient.isUseOutputDirectByteBuffers();
+ while (true)
{
- assert !buffer.hasRemaining();
- pool.release(buffer);
+ HttpGenerator.Result result = generator.generateRequest(null, null, chunkBuffer, contentBuffer, lastContent);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Generated content ({} bytes, last={}) - {}/{}",
+ contentBuffer == null ? -1 : contentBuffer.remaining(),
+ lastContent, result, generator);
+ switch (result)
+ {
+ case NEED_CHUNK:
+ {
+ chunkBuffer = bufferPool.acquire(HttpGenerator.CHUNK_SIZE, useDirectByteBuffers);
+ break;
+ }
+ case NEED_CHUNK_TRAILER:
+ {
+ chunkBuffer = bufferPool.acquire(httpClient.getRequestBufferSize(), useDirectByteBuffers);
+ break;
+ }
+ case FLUSH:
+ {
+ EndPoint endPoint = getHttpChannel().getHttpConnection().getEndPoint();
+ if (chunkBuffer != null)
+ endPoint.write(this, chunkBuffer, contentBuffer);
+ else
+ endPoint.write(this, contentBuffer);
+ return Action.SCHEDULED;
+ }
+ case SHUTDOWN_OUT:
+ {
+ shutdownOutput();
+ break;
+ }
+ case CONTINUE:
+ {
+ break;
+ }
+ case DONE:
+ {
+ release();
+ callback.succeeded();
+ return Action.IDLE;
+ }
+ default:
+ {
+ throw new IllegalStateException(result.toString());
+ }
+ }
}
- super.succeeded();
}
@Override
- public void failed(Throwable x)
+ protected void onCompleteFailure(Throwable cause)
{
- for (ByteBuffer buffer : buffers)
- {
- pool.release(buffer);
- }
- super.failed(x);
+ release();
+ callback.failed(cause);
+ }
+
+ private void release()
+ {
+ HttpClient httpClient = getHttpChannel().getHttpDestination().getHttpClient();
+ ByteBufferPool bufferPool = httpClient.getByteBufferPool();
+ bufferPool.release(chunkBuffer);
+ chunkBuffer = null;
+ contentBuffer = null;
}
}
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/internal/RequestContentAdapter.java b/jetty-client/src/main/java/org/eclipse/jetty/client/internal/RequestContentAdapter.java
new file mode 100644
index 00000000000..5d23fafa8f2
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/internal/RequestContentAdapter.java
@@ -0,0 +1,329 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.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.client.internal;
+
+import java.io.Closeable;
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+
+import org.eclipse.jetty.client.AsyncContentProvider;
+import org.eclipse.jetty.client.Synchronizable;
+import org.eclipse.jetty.client.api.ContentProvider;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.AutoLock;
+
+/**
+ *
Implements the conversion from {@link ContentProvider} to {@link Request.Content}.
+ */
+public class RequestContentAdapter implements Request.Content, Request.Content.Subscription, AsyncContentProvider.Listener, Callback
+{
+ private static final Logger LOG = Log.getLogger(RequestContentAdapter.class);
+
+ private final AutoLock lock = new AutoLock();
+ private final ContentProvider provider;
+ private Iterator iterator;
+ private Consumer consumer;
+ private boolean emitInitialContent;
+ private boolean lastContent;
+ private boolean committed;
+ private int demand;
+ private boolean stalled;
+ private boolean hasContent;
+ private Throwable failure;
+
+ public RequestContentAdapter(ContentProvider provider)
+ {
+ this.provider = provider;
+ if (provider instanceof AsyncContentProvider)
+ ((AsyncContentProvider)provider).setListener(this);
+ }
+
+ public ContentProvider getContentProvider()
+ {
+ return provider;
+ }
+
+ @Override
+ public String getContentType()
+ {
+ return provider instanceof ContentProvider.Typed ? ((ContentProvider.Typed)provider).getContentType() : null;
+ }
+
+ @Override
+ public long getLength()
+ {
+ return provider.getLength();
+ }
+
+ @Override
+ public boolean isReproducible()
+ {
+ return provider.isReproducible();
+ }
+
+ @Override
+ public Subscription subscribe(Consumer consumer, boolean emitInitialContent)
+ {
+ try (AutoLock ignored = lock.lock())
+ {
+ if (this.consumer != null && !isReproducible())
+ throw new IllegalStateException("Multiple subscriptions not supported on " + this);
+ this.iterator = provider.iterator();
+ this.consumer = consumer;
+ this.emitInitialContent = emitInitialContent;
+ this.lastContent = false;
+ this.committed = false;
+ this.demand = 0;
+ this.stalled = true;
+ this.hasContent = false;
+ }
+ return this;
+ }
+
+ @Override
+ public void demand()
+ {
+ boolean produce;
+ try (AutoLock ignored = lock.lock())
+ {
+ ++demand;
+ produce = stalled;
+ if (stalled)
+ stalled = false;
+ }
+ if (LOG.isDebugEnabled())
+ LOG.debug("Content demand, producing {} for {}", produce, this);
+ if (produce)
+ produce();
+ }
+
+ @Override
+ public void fail(Throwable failure)
+ {
+ try (AutoLock ignored = lock.lock())
+ {
+ if (this.failure == null)
+ this.failure = failure;
+ }
+ failed(failure);
+ }
+
+ @Override
+ public void onContent()
+ {
+ boolean produce = false;
+ try (AutoLock ignored = lock.lock())
+ {
+ hasContent = true;
+ if (demand > 0)
+ {
+ produce = stalled;
+ if (stalled)
+ stalled = false;
+ }
+ }
+ if (LOG.isDebugEnabled())
+ LOG.debug("Content event, processing {} for {}", produce, this);
+ if (produce)
+ produce();
+ }
+
+ @Override
+ public void succeeded()
+ {
+ if (iterator instanceof Callback)
+ ((Callback)iterator).succeeded();
+ if (lastContent && iterator instanceof Closeable)
+ IO.close((Closeable)iterator);
+ }
+
+ @Override
+ public void failed(Throwable x)
+ {
+ if (iterator == null)
+ failed(provider, x);
+ else
+ failed(iterator, x);
+ }
+
+ private void failed(Object object, Throwable failure)
+ {
+ if (object instanceof Callback)
+ ((Callback)object).failed(failure);
+ if (object instanceof Closeable)
+ IO.close((Closeable)object);
+ }
+
+ @Override
+ public InvocationType getInvocationType()
+ {
+ return InvocationType.NON_BLOCKING;
+ }
+
+ private void produce()
+ {
+ while (true)
+ {
+ Throwable failure;
+ try (AutoLock ignored = lock.lock())
+ {
+ failure = this.failure;
+ }
+ if (failure != null)
+ {
+ notifyFailure(failure);
+ return;
+ }
+
+ if (committed)
+ {
+ ByteBuffer content = advance();
+ if (content != null)
+ {
+ notifyContent(content, lastContent);
+ }
+ else
+ {
+ try (AutoLock ignored = lock.lock())
+ {
+ // Call to advance() said there was no content,
+ // but some content may have arrived meanwhile.
+ if (hasContent)
+ {
+ hasContent = false;
+ continue;
+ }
+ else
+ {
+ stalled = true;
+ }
+ }
+ if (LOG.isDebugEnabled())
+ LOG.debug("No content, processing stalled for {}", this);
+ return;
+ }
+ }
+ else
+ {
+ committed = true;
+ if (emitInitialContent)
+ {
+ ByteBuffer content = advance();
+ if (content != null)
+ notifyContent(content, lastContent);
+ else
+ notifyContent(BufferUtil.EMPTY_BUFFER, false);
+ }
+ else
+ {
+ notifyContent(BufferUtil.EMPTY_BUFFER, false);
+ }
+ }
+ boolean noDemand;
+ try (AutoLock ignored = lock.lock())
+ {
+ noDemand = demand == 0;
+ if (noDemand)
+ stalled = true;
+ }
+ if (noDemand)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("No demand, processing stalled for {}", this);
+ return;
+ }
+ }
+ }
+
+ private ByteBuffer advance()
+ {
+ if (iterator instanceof Synchronizable)
+ {
+ synchronized (((Synchronizable)iterator).getLock())
+ {
+ return next();
+ }
+ }
+ else
+ {
+ return next();
+ }
+ }
+
+ private ByteBuffer next()
+ {
+ boolean hasNext = iterator.hasNext();
+ ByteBuffer bytes = hasNext ? iterator.next() : null;
+ boolean hasMore = hasNext && iterator.hasNext();
+ lastContent = !hasMore;
+ return hasNext ? bytes : BufferUtil.EMPTY_BUFFER;
+ }
+
+ private void notifyContent(ByteBuffer buffer, boolean last)
+ {
+ try (AutoLock ignored = lock.lock())
+ {
+ --demand;
+ hasContent = false;
+ }
+
+ try
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Notifying content last={} {} for {}", last, BufferUtil.toDetailString(buffer), this);
+ consumer.onContent(buffer, last, this);
+ }
+ catch (Throwable x)
+ {
+ fail(x);
+ }
+ }
+
+ private void notifyFailure(Throwable failure)
+ {
+ try
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Notifying failure for {}", this, failure);
+ consumer.onFailure(failure);
+ }
+ catch (Exception x)
+ {
+ LOG.ignore(x);
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ int demand;
+ boolean stalled;
+ try (AutoLock ignored = lock.lock())
+ {
+ demand = this.demand;
+ stalled = this.stalled;
+ }
+ return String.format("%s@%x[demand=%d,stalled=%b]", getClass().getSimpleName(), hashCode(), demand, stalled);
+ }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractRequestContent.java
new file mode 100644
index 00000000000..9f18c301801
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractRequestContent.java
@@ -0,0 +1,250 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.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.client.util;
+
+import java.io.EOFException;
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.AutoLock;
+
+public abstract class AbstractRequestContent implements Request.Content
+{
+ private static final Logger LOG = Log.getLogger(AbstractRequestContent.class);
+
+ private final AutoLock lock = new AutoLock();
+ private final String contentType;
+ private Subscription subscription;
+ private Throwable failure;
+
+ protected AbstractRequestContent(String contentType)
+ {
+ this.contentType = contentType;
+ }
+
+ @Override
+ public String getContentType()
+ {
+ return contentType;
+ }
+
+ @Override
+ public Subscription subscribe(Consumer consumer, boolean emitInitialContent)
+ {
+ Subscription oldSubscription;
+ Subscription newSubscription;
+ try (AutoLock ignored = lock.lock())
+ {
+ if (subscription != null && !isReproducible())
+ throw new IllegalStateException("Multiple subscriptions not supported on " + this);
+ oldSubscription = subscription;
+ newSubscription = subscription = newSubscription(consumer, emitInitialContent, failure);
+ }
+ if (oldSubscription != null)
+ oldSubscription.fail(new EOFException("Content replay"));
+ if (LOG.isDebugEnabled())
+ LOG.debug("Content subscription for {}: {}", this, consumer);
+ return newSubscription;
+ }
+
+ protected abstract Subscription newSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure);
+
+ @Override
+ public void fail(Throwable failure)
+ {
+ Subscription subscription = null;
+ try (AutoLock ignored = lock.lock())
+ {
+ if (this.failure == null)
+ {
+ this.failure = failure;
+ subscription = this.subscription;
+ }
+ }
+ if (subscription != null)
+ subscription.fail(failure);
+ }
+
+ public abstract class AbstractSubscription implements Subscription
+ {
+ private final Consumer consumer;
+ private final boolean emitInitialContent;
+ private Throwable failure;
+ private int demand;
+ private boolean stalled;
+ private boolean committed;
+
+ public AbstractSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ {
+ this.consumer = consumer;
+ this.emitInitialContent = emitInitialContent;
+ this.failure = failure;
+ this.stalled = true;
+ }
+
+ @Override
+ public void demand()
+ {
+ boolean produce;
+ try (AutoLock ignored = lock.lock())
+ {
+ ++demand;
+ produce = stalled;
+ if (stalled)
+ stalled = false;
+ }
+ if (LOG.isDebugEnabled())
+ LOG.debug("Content demand, producing {} for {}", produce, this);
+ if (produce)
+ produce();
+ }
+
+ private void produce()
+ {
+ while (true)
+ {
+ Throwable failure;
+ boolean committed;
+ try (AutoLock ignored = lock.lock())
+ {
+ failure = this.failure;
+ committed = this.committed;
+ }
+ if (failure != null)
+ {
+ notifyFailure(failure);
+ return;
+ }
+
+ if (committed || emitInitialContent)
+ {
+ try
+ {
+ if (!produceContent(this::processContent))
+ return;
+ }
+ catch (Throwable x)
+ {
+ // Fail and loop around to notify the failure.
+ fail(x);
+ }
+ }
+ else
+ {
+ if (!processContent(BufferUtil.EMPTY_BUFFER, false, Callback.NOOP))
+ return;
+ }
+ }
+ }
+
+ protected abstract boolean produceContent(Producer producer) throws Exception;
+
+ @Override
+ public void fail(Throwable failure)
+ {
+ try (AutoLock ignored = lock.lock())
+ {
+ if (this.failure == null)
+ this.failure = failure;
+ }
+ }
+
+ private boolean processContent(ByteBuffer content, boolean last, Callback callback)
+ {
+ try (AutoLock ignored = lock.lock())
+ {
+ committed = true;
+ --demand;
+ }
+
+ if (content != null)
+ notifyContent(content, last, callback);
+ else
+ callback.succeeded();
+
+ boolean noDemand;
+ try (AutoLock ignored = lock.lock())
+ {
+ noDemand = demand == 0;
+ if (noDemand)
+ stalled = true;
+ }
+ if (noDemand)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("No demand, processing stalled for {}", this);
+ return false;
+ }
+ return true;
+ }
+
+ protected void notifyContent(ByteBuffer buffer, boolean last, Callback callback)
+ {
+ try
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Notifying content last={} {} for {}", last, BufferUtil.toDetailString(buffer), this);
+ consumer.onContent(buffer, last, callback);
+ }
+ catch (Throwable x)
+ {
+ callback.failed(x);
+ fail(x);
+ }
+ }
+
+ private void notifyFailure(Throwable failure)
+ {
+ try
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Notifying failure for {}", this, failure);
+ consumer.onFailure(failure);
+ }
+ catch (Exception x)
+ {
+ LOG.ignore(x);
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ int demand;
+ boolean stalled;
+ try (AutoLock ignored = lock.lock())
+ {
+ demand = this.demand;
+ stalled = this.stalled;
+ }
+ return String.format("%s.%s@%x[demand=%d,stalled=%b]",
+ getClass().getEnclosingClass().getSimpleName(),
+ getClass().getSimpleName(), hashCode(), demand, stalled);
+ }
+ }
+
+ public interface Producer
+ {
+ boolean produce(ByteBuffer content, boolean lastContent, Callback callback);
+ }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractTypedContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractTypedContentProvider.java
index 679de25be1b..6779bd5028a 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractTypedContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractTypedContentProvider.java
@@ -20,6 +20,10 @@ package org.eclipse.jetty.client.util;
import org.eclipse.jetty.client.api.ContentProvider;
+/**
+ * @deprecated use {@link AbstractRequestContent} instead.
+ */
+@Deprecated
public abstract class AbstractTypedContentProvider implements ContentProvider.Typed
{
private final String contentType;
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/AsyncRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/AsyncRequestContent.java
new file mode 100644
index 00000000000..d60a309a2e9
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/AsyncRequestContent.java
@@ -0,0 +1,385 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.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.client.util;
+
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.locks.Condition;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.AutoLock;
+
+public class AsyncRequestContent implements Request.Content, Request.Content.Subscription, Closeable
+{
+ private static final Logger LOG = Log.getLogger(AsyncRequestContent.class);
+
+ private final AutoLock lock = new AutoLock();
+ private final Condition flush = lock.newCondition();
+ private final Deque chunks = new ArrayDeque<>();
+ private final String contentType;
+ private long length = -1;
+ private Consumer consumer;
+ private boolean emitInitialContent;
+ private int demand;
+ private boolean stalled;
+ private boolean committed;
+ private boolean closed;
+ private boolean terminated;
+ private Throwable failure;
+
+ public AsyncRequestContent(ByteBuffer... buffers)
+ {
+ this("application/octet-stream", buffers);
+ }
+
+ public AsyncRequestContent(String contentType, ByteBuffer... buffers)
+ {
+ this.contentType = contentType;
+ Stream.of(buffers).forEach(this::offer);
+ }
+
+ @Override
+ public String getContentType()
+ {
+ return contentType;
+ }
+
+ @Override
+ public long getLength()
+ {
+ return length;
+ }
+
+ @Override
+ public Subscription subscribe(Consumer consumer, boolean emitInitialContent)
+ {
+ try (AutoLock ignored = lock.lock())
+ {
+ if (this.consumer != null)
+ throw new IllegalStateException("Multiple subscriptions not supported on " + this);
+ this.consumer = consumer;
+ this.emitInitialContent = emitInitialContent;
+ this.stalled = true;
+ if (closed)
+ length = chunks.stream().mapToLong(chunk -> chunk.buffer.remaining()).sum();
+ }
+ if (LOG.isDebugEnabled())
+ LOG.debug("Content subscription for {}: {}", this, consumer);
+ return this;
+ }
+
+ @Override
+ public void demand()
+ {
+ boolean produce;
+ try (AutoLock ignored = lock.lock())
+ {
+ ++demand;
+ produce = stalled;
+ if (stalled)
+ stalled = false;
+ }
+ if (LOG.isDebugEnabled())
+ LOG.debug("Content demand, producing {} for {}", produce, this);
+ if (produce)
+ produce();
+ }
+
+ @Override
+ public void fail(Throwable failure)
+ {
+ List toFail = List.of();
+ try (AutoLock ignored = lock.lock())
+ {
+ if (this.failure == null)
+ {
+ this.failure = failure;
+ // Transfer all chunks to fail them all.
+ toFail = chunks.stream()
+ .map(chunk -> chunk.callback)
+ .collect(Collectors.toList());
+ chunks.clear();
+ flush.signal();
+ }
+ }
+ toFail.forEach(c -> c.failed(failure));
+ }
+
+ public boolean offer(ByteBuffer buffer)
+ {
+ return offer(buffer, Callback.NOOP);
+ }
+
+ public boolean offer(ByteBuffer buffer, Callback callback)
+ {
+ return offer(new Chunk(buffer, callback));
+ }
+
+ private boolean offer(Chunk chunk)
+ {
+ boolean produce = false;
+ Throwable failure;
+ try (AutoLock ignored = lock.lock())
+ {
+ failure = this.failure;
+ if (failure == null)
+ {
+ if (closed)
+ {
+ failure = new IOException("closed");
+ }
+ else
+ {
+ chunks.offer(chunk);
+ if (demand > 0)
+ {
+ if (stalled)
+ {
+ stalled = false;
+ produce = true;
+ }
+ }
+ }
+ }
+ }
+ if (LOG.isDebugEnabled())
+ LOG.debug("Content offer {}, producing {} for {}", failure == null ? "succeeded" : "failed", produce, this, failure);
+ if (failure != null)
+ {
+ chunk.callback.failed(failure);
+ return false;
+ }
+ else if (produce)
+ {
+ produce();
+ }
+ return true;
+ }
+
+ private void produce()
+ {
+ while (true)
+ {
+ Throwable failure;
+ try (AutoLock ignored = lock.lock())
+ {
+ failure = this.failure;
+ }
+ if (failure != null)
+ {
+ notifyFailure(consumer, failure);
+ return;
+ }
+
+ try
+ {
+ Consumer consumer;
+ Chunk chunk = Chunk.EMPTY;
+ boolean lastContent = false;
+ try (AutoLock ignored = lock.lock())
+ {
+ if (terminated)
+ throw new EOFException("Demand after last content");
+ consumer = this.consumer;
+ if (committed || emitInitialContent)
+ {
+ chunk = chunks.poll();
+ lastContent = closed && chunks.isEmpty();
+ if (lastContent)
+ terminated = true;
+ }
+ if (chunk == null && (lastContent || !committed))
+ chunk = Chunk.EMPTY;
+ if (chunk == null)
+ {
+ stalled = true;
+ }
+ else
+ {
+ --demand;
+ committed = true;
+ }
+ }
+ if (chunk == null)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("No content, processing stalled for {}", this);
+ return;
+ }
+
+ notifyContent(consumer, chunk.buffer, lastContent, Callback.from(this::notifyFlush, chunk.callback));
+
+ boolean noDemand;
+ try (AutoLock ignored = lock.lock())
+ {
+ noDemand = demand == 0;
+ if (noDemand)
+ stalled = true;
+ }
+ if (noDemand)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("No demand, processing stalled for {}", this);
+ return;
+ }
+ }
+ catch (Throwable x)
+ {
+ // Fail and loop around to notify the failure.
+ fail(x);
+ }
+ }
+ }
+
+ private void notifyContent(Consumer consumer, ByteBuffer buffer, boolean last, Callback callback)
+ {
+ try
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Notifying content last={} {} for {}", last, BufferUtil.toDetailString(buffer), this);
+ consumer.onContent(buffer, last, callback);
+ }
+ catch (Throwable x)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Failure while notifying content", x);
+ callback.failed(x);
+ fail(x);
+ }
+ }
+
+ private void notifyFailure(Consumer consumer, Throwable failure)
+ {
+ try
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Notifying failure for {}", this, failure);
+ consumer.onFailure(failure);
+ }
+ catch (Throwable x)
+ {
+ LOG.ignore(x);
+ }
+ }
+
+ private void notifyFlush()
+ {
+ try (AutoLock ignored = lock.lock())
+ {
+ flush.signal();
+ }
+ }
+
+ public void flush() throws IOException
+ {
+ try (AutoLock ignored = lock.lock())
+ {
+ try
+ {
+ while (true)
+ {
+ // Always wrap the exception to make sure
+ // the stack trace comes from flush().
+ if (failure != null)
+ throw new IOException(failure);
+ if (chunks.isEmpty())
+ return;
+ flush.await();
+ }
+ }
+ catch (InterruptedException x)
+ {
+ throw new InterruptedIOException();
+ }
+ }
+ }
+
+ @Override
+ public void close()
+ {
+ boolean produce = false;
+ try (AutoLock ignored = lock.lock())
+ {
+ if (closed)
+ return;
+ closed = true;
+ if (demand > 0)
+ {
+ if (stalled)
+ {
+ stalled = false;
+ produce = true;
+ }
+ }
+ flush.signal();
+ }
+ if (produce)
+ produce();
+ }
+
+ public boolean isClosed()
+ {
+ try (AutoLock ignored = lock.lock())
+ {
+ return closed;
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ int demand;
+ boolean stalled;
+ int chunks;
+ try (AutoLock ignored = lock.lock())
+ {
+ demand = this.demand;
+ stalled = this.stalled;
+ chunks = this.chunks.size();
+ }
+ return String.format("%s@%x[demand=%d,stalled=%b,chunks=%d]", getClass().getSimpleName(), hashCode(), demand, stalled, chunks);
+ }
+
+ private static class Chunk
+ {
+ private static final Chunk EMPTY = new Chunk(BufferUtil.EMPTY_BUFFER, Callback.NOOP);
+
+ private final ByteBuffer buffer;
+ private final Callback callback;
+
+ private Chunk(ByteBuffer buffer, Callback callback)
+ {
+ this.buffer = Objects.requireNonNull(buffer);
+ this.callback = Objects.requireNonNull(callback);
+ }
+ }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/ByteBufferContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/ByteBufferContentProvider.java
index 1342dc6cc0c..e1c94d3595c 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/ByteBufferContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/ByteBufferContentProvider.java
@@ -30,7 +30,10 @@ import org.eclipse.jetty.client.api.ContentProvider;
* The position and limit of the {@link ByteBuffer}s passed to the constructor are not modified,
* and each invocation of the {@link #iterator()} method returns a {@link ByteBuffer#slice() slice}
* of the original {@link ByteBuffer}.
+ *
+ * @deprecated use {@link ByteBufferRequestContent} instead.
*/
+@Deprecated
public class ByteBufferContentProvider extends AbstractTypedContentProvider
{
private final ByteBuffer[] buffers;
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/ByteBufferRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/ByteBufferRequestContent.java
new file mode 100644
index 00000000000..c5c9f35f36b
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/ByteBufferRequestContent.java
@@ -0,0 +1,94 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.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.client.util;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+
+/**
+ *
A {@link Request.Content} for {@link ByteBuffer}s.
+ *
The position and limit of the {@link ByteBuffer}s passed to the constructor are not modified;
+ * content production returns a {@link ByteBuffer#slice() slice} of the original {@link ByteBuffer}.
+ */
+public class ByteBufferRequestContent extends AbstractRequestContent
+{
+ private final ByteBuffer[] buffers;
+ private final long length;
+
+ public ByteBufferRequestContent(ByteBuffer... buffers)
+ {
+ this("application/octet-stream", buffers);
+ }
+
+ public ByteBufferRequestContent(String contentType, ByteBuffer... buffers)
+ {
+ super(contentType);
+ this.buffers = buffers;
+ this.length = Arrays.stream(buffers).mapToLong(Buffer::remaining).sum();
+ }
+
+ @Override
+ public long getLength()
+ {
+ return length;
+ }
+
+ @Override
+ public boolean isReproducible()
+ {
+ return true;
+ }
+
+ @Override
+ protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ {
+ return new SubscriptionImpl(consumer, emitInitialContent, failure);
+ }
+
+ private class SubscriptionImpl extends AbstractSubscription
+ {
+ private int index;
+
+ private SubscriptionImpl(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ {
+ super(consumer, emitInitialContent, failure);
+ }
+
+ @Override
+ protected boolean produceContent(Producer producer) throws IOException
+ {
+ if (index < 0)
+ throw new EOFException("Demand after last content");
+ ByteBuffer buffer = BufferUtil.EMPTY_BUFFER;
+ if (index < buffers.length)
+ buffer = buffers[index++];
+ boolean lastContent = index == buffers.length;
+ if (lastContent)
+ index = -1;
+ return producer.produce(buffer.slice(), lastContent, Callback.NOOP);
+ }
+ }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BytesContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BytesContentProvider.java
index 631311fdb37..3f4d3f1f2f8 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BytesContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BytesContentProvider.java
@@ -26,7 +26,10 @@ import org.eclipse.jetty.client.api.ContentProvider;
/**
* A {@link ContentProvider} for byte arrays.
+ *
+ * @deprecated use {@link BytesRequestContent} instead.
*/
+@Deprecated
public class BytesContentProvider extends AbstractTypedContentProvider
{
private final byte[][] bytes;
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BytesRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BytesRequestContent.java
new file mode 100644
index 00000000000..c1e2e09ba42
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BytesRequestContent.java
@@ -0,0 +1,91 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.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.client.util;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+
+/**
+ * A {@link Request.Content} for byte arrays.
+ */
+public class BytesRequestContent extends AbstractRequestContent
+{
+ private final byte[][] bytes;
+ private final long length;
+
+ public BytesRequestContent(byte[]... bytes)
+ {
+ this("application/octet-stream", bytes);
+ }
+
+ public BytesRequestContent(String contentType, byte[]... bytes)
+ {
+ super(contentType);
+ this.bytes = bytes;
+ this.length = Arrays.stream(bytes).mapToLong(a -> a.length).sum();
+ }
+
+ @Override
+ public long getLength()
+ {
+ return length;
+ }
+
+ @Override
+ public boolean isReproducible()
+ {
+ return true;
+ }
+
+ @Override
+ protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ {
+ return new SubscriptionImpl(consumer, emitInitialContent, failure);
+ }
+
+ private class SubscriptionImpl extends AbstractSubscription
+ {
+ private int index;
+
+ private SubscriptionImpl(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ {
+ super(consumer, emitInitialContent, failure);
+ }
+
+ @Override
+ protected boolean produceContent(Producer producer) throws IOException
+ {
+ if (index < 0)
+ throw new EOFException("Demand after last content");
+ ByteBuffer buffer = BufferUtil.EMPTY_BUFFER;
+ if (index < bytes.length)
+ buffer = ByteBuffer.wrap(bytes[index++]);
+ boolean lastContent = index == bytes.length;
+ if (lastContent)
+ index = -1;
+ return producer.produce(buffer, lastContent, Callback.NOOP);
+ }
+ }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/DeferredContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/DeferredContentProvider.java
index 174b2e25356..2d369b08fa4 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/DeferredContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/DeferredContentProvider.java
@@ -85,7 +85,10 @@ import org.eclipse.jetty.util.Callback;
* content.offer(ByteBuffer.wrap("some content".getBytes()));
* }
*
+ *
+ * @deprecated use {@link AsyncRequestContent} instead.
*/
+@Deprecated
public class DeferredContentProvider implements AsyncContentProvider, Callback, Closeable
{
private static final Chunk CLOSE = new Chunk(BufferUtil.EMPTY_BUFFER, Callback.NOOP);
@@ -285,6 +288,7 @@ public class DeferredContentProvider implements AsyncContentProvider, Callback,
synchronized (lock)
{
chunk = current;
+ current = null;
if (chunk != null)
{
--size;
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/FormContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/FormContentProvider.java
index 280494282d8..2bff3857e3a 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/FormContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/FormContentProvider.java
@@ -30,7 +30,10 @@ import org.eclipse.jetty.util.Fields;
/**
* A {@link ContentProvider} for form uploads with the
* "application/x-www-form-urlencoded" content type.
+ *
+ * @deprecated use {@link FormRequestContent} instead.
*/
+@Deprecated
public class FormContentProvider extends StringContentProvider
{
public FormContentProvider(Fields fields)
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/FormRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/FormRequestContent.java
new file mode 100644
index 00000000000..03f29139c16
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/FormRequestContent.java
@@ -0,0 +1,78 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.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.client.util;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.charset.UnsupportedCharsetException;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.util.Fields;
+
+/**
+ *
A {@link Request.Content} for form uploads with the
+ * "application/x-www-form-urlencoded" content type.
+ */
+public class FormRequestContent extends StringRequestContent
+{
+ public FormRequestContent(Fields fields)
+ {
+ this(fields, StandardCharsets.UTF_8);
+ }
+
+ public FormRequestContent(Fields fields, Charset charset)
+ {
+ super("application/x-www-form-urlencoded", convert(fields, charset), charset);
+ }
+
+ public static String convert(Fields fields)
+ {
+ return convert(fields, StandardCharsets.UTF_8);
+ }
+
+ public static String convert(Fields fields, Charset charset)
+ {
+ // Assume 32 chars between name and value.
+ StringBuilder builder = new StringBuilder(fields.getSize() * 32);
+ for (Fields.Field field : fields)
+ {
+ for (String value : field.getValues())
+ {
+ if (builder.length() > 0)
+ builder.append("&");
+ builder.append(encode(field.getName(), charset)).append("=").append(encode(value, charset));
+ }
+ }
+ return builder.toString();
+ }
+
+ private static String encode(String value, Charset charset)
+ {
+ try
+ {
+ return URLEncoder.encode(value, charset.name());
+ }
+ catch (UnsupportedEncodingException x)
+ {
+ throw new UnsupportedCharsetException(charset.name());
+ }
+ }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamContentProvider.java
index 4fd7c1a9fc9..e04a6962835 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamContentProvider.java
@@ -50,7 +50,10 @@ import org.eclipse.jetty.util.log.Logger;
* The {@link InputStream} passed to the constructor is by default closed when is it fully
* consumed (or when an exception is thrown while reading it), unless otherwise specified
* to the {@link #InputStreamContentProvider(java.io.InputStream, int, boolean) constructor}.
+ *
+ * @deprecated use {@link InputStreamRequestContent} instead
*/
+@Deprecated
public class InputStreamContentProvider implements ContentProvider, Callback, Closeable
{
private static final Logger LOG = Log.getLogger(InputStreamContentProvider.class);
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamRequestContent.java
new file mode 100644
index 00000000000..8a5e72994ce
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamRequestContent.java
@@ -0,0 +1,146 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.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.client.util;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.IO;
+
+/**
+ *
A {@link Request.Content} that produces content from an {@link InputStream}.
+ *
The input stream is read once and therefore fully consumed.
+ *
It is possible to specify, at the constructor, a buffer size used to read
+ * content from the stream, by default 1024 bytes.
+ *
The {@link InputStream} passed to the constructor is by default closed
+ * when is it fully consumed.
+ */
+public class InputStreamRequestContent extends AbstractRequestContent
+{
+ private static final int DEFAULT_BUFFER_SIZE = 4096;
+
+ private final InputStream stream;
+ private final int bufferSize;
+
+ public InputStreamRequestContent(InputStream stream)
+ {
+ this(stream, DEFAULT_BUFFER_SIZE);
+ }
+
+ public InputStreamRequestContent(String contentType, InputStream stream)
+ {
+ this(contentType, stream, DEFAULT_BUFFER_SIZE);
+ }
+
+ public InputStreamRequestContent(InputStream stream, int bufferSize)
+ {
+ this("application/octet-stream", stream, bufferSize);
+ }
+
+ public InputStreamRequestContent(String contentType, InputStream stream, int bufferSize)
+ {
+ super(contentType);
+ this.stream = stream;
+ this.bufferSize = bufferSize;
+ }
+
+ @Override
+ protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ {
+ return new SubscriptionImpl(consumer, emitInitialContent, failure);
+ }
+
+ @Override
+ public void fail(Throwable failure)
+ {
+ super.fail(failure);
+ close();
+ }
+
+ protected ByteBuffer onRead(byte[] buffer, int offset, int length)
+ {
+ return ByteBuffer.wrap(buffer, offset, length);
+ }
+
+ protected void onReadFailure(Throwable failure)
+ {
+ }
+
+ private void close()
+ {
+ IO.close(stream);
+ }
+
+ private class SubscriptionImpl extends AbstractSubscription
+ {
+ private boolean terminated;
+
+ private SubscriptionImpl(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ {
+ super(consumer, emitInitialContent, failure);
+ }
+
+ @Override
+ protected boolean produceContent(Producer producer) throws IOException
+ {
+ if (terminated)
+ throw new EOFException("Demand after last content");
+ byte[] bytes = new byte[bufferSize];
+ int read = read(bytes);
+ ByteBuffer buffer = BufferUtil.EMPTY_BUFFER;
+ boolean last = true;
+ if (read < 0)
+ {
+ close();
+ terminated = true;
+ }
+ else
+ {
+ buffer = onRead(bytes, 0, read);
+ last = false;
+ }
+ return producer.produce(buffer, last, Callback.NOOP);
+ }
+
+ private int read(byte[] bytes) throws IOException
+ {
+ try
+ {
+ return stream.read(bytes);
+ }
+ catch (Throwable x)
+ {
+ onReadFailure(x);
+ throw x;
+ }
+ }
+
+ @Override
+ public void fail(Throwable failure)
+ {
+ super.fail(failure);
+ close();
+ }
+ }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamResponseListener.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamResponseListener.java
index a48115460e5..f03b511c569 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamResponseListener.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamResponseListener.java
@@ -27,6 +27,7 @@ import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
@@ -76,12 +77,12 @@ import org.eclipse.jetty.util.log.Logger;
public class InputStreamResponseListener extends Listener.Adapter
{
private static final Logger LOG = Log.getLogger(InputStreamResponseListener.class);
- private static final DeferredContentProvider.Chunk EOF = new DeferredContentProvider.Chunk(BufferUtil.EMPTY_BUFFER, Callback.NOOP);
+ private static final Chunk EOF = new Chunk(BufferUtil.EMPTY_BUFFER, Callback.NOOP);
private final Object lock = this;
private final CountDownLatch responseLatch = new CountDownLatch(1);
private final CountDownLatch resultLatch = new CountDownLatch(1);
private final AtomicReference stream = new AtomicReference<>();
- private final Queue chunks = new ArrayDeque<>();
+ private final Queue chunks = new ArrayDeque<>();
private Response response;
private Result result;
private Throwable failure;
@@ -120,7 +121,7 @@ public class InputStreamResponseListener extends Listener.Adapter
{
if (LOG.isDebugEnabled())
LOG.debug("Queueing content {}", content);
- chunks.add(new DeferredContentProvider.Chunk(content, callback));
+ chunks.add(new Chunk(content, callback));
lock.notifyAll();
}
}
@@ -268,7 +269,7 @@ public class InputStreamResponseListener extends Listener.Adapter
{
while (true)
{
- DeferredContentProvider.Chunk chunk = chunks.peek();
+ Chunk chunk = chunks.peek();
if (chunk == null || chunk == EOF)
break;
callbacks.add(chunk.callback);
@@ -299,7 +300,7 @@ public class InputStreamResponseListener extends Listener.Adapter
Callback callback = null;
synchronized (lock)
{
- DeferredContentProvider.Chunk chunk;
+ Chunk chunk;
while (true)
{
chunk = chunks.peek();
@@ -367,4 +368,16 @@ public class InputStreamResponseListener extends Listener.Adapter
super.close();
}
}
+
+ private static class Chunk
+ {
+ private final ByteBuffer buffer;
+ private final Callback callback;
+
+ private Chunk(ByteBuffer buffer, Callback callback)
+ {
+ this.buffer = Objects.requireNonNull(buffer);
+ this.callback = Objects.requireNonNull(callback);
+ }
+ }
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartContentProvider.java
index 27ce2ca5136..901a1a6255c 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartContentProvider.java
@@ -63,7 +63,10 @@ import org.eclipse.jetty.util.log.Logger;
* <input type="file" name="icon" />
* </form>
*
+ *
+ * @deprecated use {@link MultiPartRequestContent} instead.
*/
+@Deprecated
public class MultiPartContentProvider extends AbstractTypedContentProvider implements AsyncContentProvider, Closeable
{
private static final Logger LOG = Log.getLogger(MultiPartContentProvider.class);
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartRequestContent.java
new file mode 100644
index 00000000000..ddca420a34d
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartRequestContent.java
@@ -0,0 +1,380 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.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.client.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.io.RuntimeIOException;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ *
A {@link Request.Content} for form uploads with the {@code "multipart/form-data"}
+ * content type.
+ */
+public class MultiPartRequestContent extends AbstractRequestContent implements Closeable
+{
+ private static final Logger LOG = Log.getLogger(MultiPartRequestContent.class);
+ private static final byte[] COLON_SPACE_BYTES = new byte[]{':', ' '};
+ private static final byte[] CR_LF_BYTES = new byte[]{'\r', '\n'};
+
+ private static String makeBoundary()
+ {
+ Random random = new Random();
+ StringBuilder builder = new StringBuilder("JettyHttpClientBoundary");
+ int length = builder.length();
+ while (builder.length() < length + 16)
+ {
+ long rnd = random.nextLong();
+ builder.append(Long.toString(rnd < 0 ? -rnd : rnd, 36));
+ }
+ builder.setLength(length + 16);
+ return builder.toString();
+ }
+
+ private final List parts = new ArrayList<>();
+ private final ByteBuffer firstBoundary;
+ private final ByteBuffer middleBoundary;
+ private final ByteBuffer onlyBoundary;
+ private final ByteBuffer lastBoundary;
+ private long length;
+ private volatile boolean closed;
+
+ public MultiPartRequestContent()
+ {
+ this(makeBoundary());
+ }
+
+ public MultiPartRequestContent(String boundary)
+ {
+ super("multipart/form-data; boundary=" + boundary);
+ String firstBoundaryLine = "--" + boundary + "\r\n";
+ this.firstBoundary = ByteBuffer.wrap(firstBoundaryLine.getBytes(StandardCharsets.US_ASCII));
+ String middleBoundaryLine = "\r\n" + firstBoundaryLine;
+ this.middleBoundary = ByteBuffer.wrap(middleBoundaryLine.getBytes(StandardCharsets.US_ASCII));
+ String onlyBoundaryLine = "--" + boundary + "--\r\n";
+ this.onlyBoundary = ByteBuffer.wrap(onlyBoundaryLine.getBytes(StandardCharsets.US_ASCII));
+ String lastBoundaryLine = "\r\n" + onlyBoundaryLine;
+ this.lastBoundary = ByteBuffer.wrap(lastBoundaryLine.getBytes(StandardCharsets.US_ASCII));
+ this.length = -1;
+ }
+
+ @Override
+ public long getLength()
+ {
+ return length;
+ }
+
+ @Override
+ protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ {
+ length = calculateLength();
+ return new SubscriptionImpl(consumer, emitInitialContent, failure);
+ }
+
+ /**
+ *
Adds a field part with the given {@code name} as field name, and the given
+ * {@code content} as part content.
+ *
The {@code Content-Type} of this part will be obtained from:
+ *
+ *
the {@code Content-Type} header in the {@code fields} parameter; otherwise
+ *
the {@link Request.Content#getContentType()}
+ *
+ *
+ * @param name the part name
+ * @param content the part content
+ * @param fields the headers associated with this part
+ */
+ public void addFieldPart(String name, Request.Content content, HttpFields fields)
+ {
+ addPart(new Part(name, null, content, fields));
+ }
+
+ /**
+ *
Adds a file part with the given {@code name} as field name, the given
+ * {@code fileName} as file name, and the given {@code content} as part content.
+ *
The {@code Content-Type} of this part will be obtained from:
+ *
+ *
the {@code Content-Type} header in the {@code fields} parameter; otherwise
+ *
the {@link Request.Content#getContentType()}
+ *
+ *
+ * @param name the part name
+ * @param fileName the file name associated to this part
+ * @param content the part content
+ * @param fields the headers associated with this part
+ */
+ public void addFilePart(String name, String fileName, Request.Content content, HttpFields fields)
+ {
+ addPart(new Part(name, fileName, content, fields));
+ }
+
+ private void addPart(Part part)
+ {
+ parts.add(part);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Added {}", part);
+ }
+
+ @Override
+ public void close()
+ {
+ closed = true;
+ }
+
+ private long calculateLength()
+ {
+ // Compute the length, if possible.
+ if (parts.isEmpty())
+ {
+ return onlyBoundary.remaining();
+ }
+ else
+ {
+ long result = 0;
+ for (int i = 0; i < parts.size(); ++i)
+ {
+ result += (i == 0) ? firstBoundary.remaining() : middleBoundary.remaining();
+ Part part = parts.get(i);
+ long partLength = part.length;
+ result += partLength;
+ if (partLength < 0)
+ {
+ result = -1;
+ break;
+ }
+ }
+ if (result > 0)
+ result += lastBoundary.remaining();
+ return result;
+ }
+ }
+
+ private static class Part
+ {
+ private final String name;
+ private final String fileName;
+ private final Request.Content content;
+ private final HttpFields fields;
+ private final ByteBuffer headers;
+ private final long length;
+
+ private Part(String name, String fileName, Request.Content content, HttpFields fields)
+ {
+ this.name = name;
+ this.fileName = fileName;
+ this.content = content;
+ this.fields = fields;
+ this.headers = headers();
+ this.length = content.getLength() < 0 ? -1 : headers.remaining() + content.getLength();
+ }
+
+ private ByteBuffer headers()
+ {
+ try
+ {
+ // Compute the Content-Disposition.
+ String contentDisposition = "Content-Disposition: form-data; name=\"" + name + "\"";
+ if (fileName != null)
+ contentDisposition += "; filename=\"" + fileName + "\"";
+ contentDisposition += "\r\n";
+
+ // Compute the Content-Type.
+ String contentType = fields == null ? null : fields.get(HttpHeader.CONTENT_TYPE);
+ if (contentType == null)
+ contentType = content.getContentType();
+ contentType = "Content-Type: " + contentType + "\r\n";
+
+ if (fields == null || fields.size() == 0)
+ {
+ String headers = contentDisposition;
+ headers += contentType;
+ headers += "\r\n";
+ return ByteBuffer.wrap(headers.getBytes(StandardCharsets.UTF_8));
+ }
+
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream((fields.size() + 1) * contentDisposition.length());
+ buffer.write(contentDisposition.getBytes(StandardCharsets.UTF_8));
+ buffer.write(contentType.getBytes(StandardCharsets.UTF_8));
+ for (HttpField field : fields)
+ {
+ if (HttpHeader.CONTENT_TYPE.equals(field.getHeader()))
+ continue;
+ buffer.write(field.getName().getBytes(StandardCharsets.US_ASCII));
+ buffer.write(COLON_SPACE_BYTES);
+ String value = field.getValue();
+ if (value != null)
+ buffer.write(value.getBytes(StandardCharsets.UTF_8));
+ buffer.write(CR_LF_BYTES);
+ }
+ buffer.write(CR_LF_BYTES);
+ return ByteBuffer.wrap(buffer.toByteArray());
+ }
+ catch (IOException x)
+ {
+ throw new RuntimeIOException(x);
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s@%x[name=%s,fileName=%s,length=%d,headers=%s]",
+ getClass().getSimpleName(),
+ hashCode(),
+ name,
+ fileName,
+ content.getLength(),
+ fields);
+ }
+ }
+
+ private class SubscriptionImpl extends AbstractSubscription implements Consumer
+ {
+ private State state = State.FIRST_BOUNDARY;
+ private int index;
+ private Subscription subscription;
+
+ private SubscriptionImpl(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ {
+ super(consumer, emitInitialContent, failure);
+ }
+
+ @Override
+ protected boolean produceContent(Producer producer) throws IOException
+ {
+ ByteBuffer buffer;
+ boolean last = false;
+ switch (state)
+ {
+ case FIRST_BOUNDARY:
+ {
+ if (parts.isEmpty())
+ {
+ state = State.COMPLETE;
+ buffer = onlyBoundary.slice();
+ last = true;
+ break;
+ }
+ else
+ {
+ state = State.HEADERS;
+ buffer = firstBoundary.slice();
+ break;
+ }
+ }
+ case HEADERS:
+ {
+ Part part = parts.get(index);
+ Request.Content content = part.content;
+ subscription = content.subscribe(this, true);
+ state = State.CONTENT;
+ buffer = part.headers.slice();
+ break;
+ }
+ case CONTENT:
+ {
+ buffer = null;
+ subscription.demand();
+ break;
+ }
+ case MIDDLE_BOUNDARY:
+ {
+ state = State.HEADERS;
+ buffer = middleBoundary.slice();
+ break;
+ }
+ case LAST_BOUNDARY:
+ {
+ state = State.COMPLETE;
+ buffer = lastBoundary.slice();
+ last = true;
+ break;
+ }
+ case COMPLETE:
+ {
+ throw new EOFException("Demand after last content");
+ }
+ default:
+ {
+ throw new IllegalStateException("Invalid state " + state);
+ }
+ }
+ return producer.produce(buffer, last, Callback.NOOP);
+ }
+
+ @Override
+ public void onContent(ByteBuffer buffer, boolean last, Callback callback)
+ {
+ if (last)
+ {
+ ++index;
+ if (index < parts.size())
+ state = State.MIDDLE_BOUNDARY;
+ else
+ state = State.LAST_BOUNDARY;
+ }
+ notifyContent(buffer, false, callback);
+ }
+
+ @Override
+ public void onFailure(Throwable failure)
+ {
+ parts.stream()
+ .map(part -> part.content)
+ .forEach(content -> content.fail(failure));
+ }
+ }
+
+ private enum State
+ {
+ FIRST_BOUNDARY, HEADERS, CONTENT, MIDDLE_BOUNDARY, LAST_BOUNDARY, COMPLETE
+ }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/OutputStreamContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/OutputStreamContentProvider.java
index edffa00bd68..fe015c6f314 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/OutputStreamContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/OutputStreamContentProvider.java
@@ -72,7 +72,10 @@ import org.eclipse.jetty.util.Callback;
* output.write("some content".getBytes());
* }
*
+ *
+ * @deprecated use {@link OutputStreamRequestContent} instead
*/
+@Deprecated
public class OutputStreamContentProvider implements AsyncContentProvider, Callback, Closeable
{
private final DeferredContentProvider deferred = new DeferredContentProvider();
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/OutputStreamRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/OutputStreamRequestContent.java
new file mode 100644
index 00000000000..f093456c575
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/OutputStreamRequestContent.java
@@ -0,0 +1,125 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.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.client.util;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.concurrent.ExecutionException;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.util.FutureCallback;
+
+/**
+ *
A {@link Request.Content} that provides content asynchronously through an {@link OutputStream}
+ * similar to {@link AsyncRequestContent}.
+ *
{@link OutputStreamRequestContent} can only be used in conjunction with
+ * {@link Request#send(Response.CompleteListener)} (and not with its blocking counterpart
+ * {@link Request#send()}) because it provides content asynchronously.
+ *
Content must be provided by writing to the {@link #getOutputStream() output stream}
+ * that must be {@link OutputStream#close() closed} when all content has been provided.
+ *
Example usage:
+ *
+ * HttpClient httpClient = ...;
+ *
+ * // Use try-with-resources to autoclose the output stream.
+ * OutputStreamRequestContent content = new OutputStreamRequestContent();
+ * try (OutputStream output = content.getOutputStream())
+ * {
+ * httpClient.newRequest("localhost", 8080)
+ * .content(content)
+ * .send(new Response.CompleteListener()
+ * {
+ * @Override
+ * public void onComplete(Result result)
+ * {
+ * // Your logic here
+ * }
+ * });
+ *
+ * // At a later time...
+ * output.write("some content".getBytes());
+ *
+ * // Even later...
+ * output.write("more content".getBytes());
+ * } // Implicit call to output.close().
+ *
+ */
+public class OutputStreamRequestContent extends AsyncRequestContent
+{
+ private final AsyncOutputStream output;
+
+ public OutputStreamRequestContent()
+ {
+ this("application/octet-stream");
+ }
+
+ public OutputStreamRequestContent(String contentType)
+ {
+ super(contentType);
+ this.output = new AsyncOutputStream();
+ }
+
+ public OutputStream getOutputStream()
+ {
+ return output;
+ }
+
+ private class AsyncOutputStream extends OutputStream
+ {
+ @Override
+ public void write(int b) throws IOException
+ {
+ write(new byte[]{(byte)b}, 0, 1);
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException
+ {
+ try
+ {
+ FutureCallback callback = new FutureCallback();
+ offer(ByteBuffer.wrap(b, off, len), callback);
+ callback.get();
+ }
+ catch (InterruptedException x)
+ {
+ throw new InterruptedIOException();
+ }
+ catch (ExecutionException x)
+ {
+ throw new IOException(x.getCause());
+ }
+ }
+
+ @Override
+ public void flush() throws IOException
+ {
+ OutputStreamRequestContent.this.flush();
+ }
+
+ @Override
+ public void close()
+ {
+ OutputStreamRequestContent.this.close();
+ }
+ }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathContentProvider.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathContentProvider.java
index 67509c9b8c5..90f6fd2b973 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathContentProvider.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathContentProvider.java
@@ -43,7 +43,10 @@ import org.eclipse.jetty.util.log.Logger;
* If a {@link ByteBufferPool} is provided via {@link #setByteBufferPool(ByteBufferPool)},
* the buffer will be allocated from that pool, otherwise one buffer will be
* allocated and used to read the file.
+ *
+ * @deprecated use {@link PathRequestContent} instead.
*/
+@Deprecated
public class PathContentProvider extends AbstractTypedContentProvider
{
private static final Logger LOG = Log.getLogger(PathContentProvider.class);
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathRequestContent.java
new file mode 100644
index 00000000000..9f0165a26a9
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathRequestContent.java
@@ -0,0 +1,170 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.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.client.util;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.file.AccessDeniedException;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ *
A {@link Request.Content} for files using JDK 7's {@code java.nio.file} APIs.
+ *
It is possible to specify, at the constructor, a buffer size used to read
+ * content from the stream, by default 4096 bytes.
+ * If a {@link ByteBufferPool} is provided via {@link #setByteBufferPool(ByteBufferPool)},
+ * the buffer will be allocated from that pool, otherwise one buffer will be
+ * allocated and used to read the file.
* It is possible to specify, at the constructor, an encoding used to convert
* the string into bytes, by default UTF-8.
+ *
+ * @deprecated use {@link StringRequestContent} instead.
*/
+@Deprecated
public class StringContentProvider extends BytesContentProvider
{
public StringContentProvider(String content)
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/StringRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/StringRequestContent.java
new file mode 100644
index 00000000000..41054c62857
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/StringRequestContent.java
@@ -0,0 +1,52 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.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.client.util;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+import org.eclipse.jetty.client.api.Request;
+
+/**
+ *
A {@link Request.Content} for strings.
+ *
It is possible to specify, at the constructor, an encoding used to convert
+ * the string into bytes, by default UTF-8.
+ */
+public class StringRequestContent extends BytesRequestContent
+{
+ public StringRequestContent(String content)
+ {
+ this("text/plain;charset=UTF-8", content);
+ }
+
+ public StringRequestContent(String content, Charset encoding)
+ {
+ this("text/plain;charset=" + encoding.name(), content, encoding);
+ }
+
+ public StringRequestContent(String contentType, String content)
+ {
+ this(contentType, content, StandardCharsets.UTF_8);
+ }
+
+ public StringRequestContent(String contentType, String content, Charset encoding)
+ {
+ super(contentType, content.getBytes(encoding));
+ }
+}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ClientConnectionCloseTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ClientConnectionCloseTest.java
index 8f3d2144e92..fab10cb6b6c 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/ClientConnectionCloseTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ClientConnectionCloseTest.java
@@ -29,8 +29,8 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
-import org.eclipse.jetty.client.util.DeferredContentProvider;
-import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.client.util.AsyncRequestContent;
+import org.eclipse.jetty.client.util.StringRequestContent;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpStatus;
@@ -87,7 +87,7 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
var request = client.newRequest(host, port)
.scheme(scenario.getScheme())
.header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
- .content(new StringContentProvider("0"))
+ .body(new StringRequestContent("0"))
.onRequestSuccess(r ->
{
HttpDestination destination = (HttpDestination)client.resolveDestination(r);
@@ -184,12 +184,12 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
- DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.allocate(8));
+ AsyncRequestContent content = new AsyncRequestContent(ByteBuffer.allocate(8));
CountDownLatch resultLatch = new CountDownLatch(1);
var request = client.newRequest(host, port)
.scheme(scenario.getScheme())
.header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
- .content(content)
+ .body(content)
.idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
.onRequestSuccess(r ->
{
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ConnectionPoolTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ConnectionPoolTest.java
index c90329b07a2..08139dbf1d6 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/ConnectionPoolTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ConnectionPoolTest.java
@@ -33,7 +33,7 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
-import org.eclipse.jetty.client.util.BytesContentProvider;
+import org.eclipse.jetty.client.util.BytesRequestContent;
import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
@@ -213,7 +213,7 @@ public class ConnectionPoolTest
break;
case POST:
request.header(HttpHeader.CONTENT_LENGTH, String.valueOf(contentLength));
- request.content(new BytesContentProvider(new byte[contentLength]));
+ request.body(new BytesRequestContent(new byte[contentLength]));
break;
default:
throw new IllegalStateException();
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java
index b10039ea904..7545b50e6cd 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java
@@ -22,9 +22,7 @@ import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
-import java.util.Iterator;
import java.util.List;
-import java.util.NoSuchElementException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -37,15 +35,15 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.Authentication;
import org.eclipse.jetty.client.api.Authentication.HeaderInfo;
import org.eclipse.jetty.client.api.AuthenticationStore;
-import org.eclipse.jetty.client.api.ContentProvider;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Response.Listener;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.AbstractAuthentication;
+import org.eclipse.jetty.client.util.AbstractRequestContent;
+import org.eclipse.jetty.client.util.AsyncRequestContent;
import org.eclipse.jetty.client.util.BasicAuthentication;
-import org.eclipse.jetty.client.util.DeferredContentProvider;
import org.eclipse.jetty.client.util.DigestAuthentication;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
@@ -60,6 +58,8 @@ import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.Attributes;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.security.Constraint;
@@ -460,7 +460,7 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
CountDownLatch resultLatch = new CountDownLatch(1);
byte[] data = new byte[]{'h', 'e', 'l', 'l', 'o'};
- DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.wrap(data))
+ AsyncRequestContent content = new AsyncRequestContent(ByteBuffer.wrap(data))
{
@Override
public boolean isReproducible()
@@ -470,7 +470,7 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
};
Request request = client.newRequest(uri)
.path("/secure")
- .content(content);
+ .body(content);
request.send(result ->
{
if (result.isSucceeded() && result.getResponse().getStatus() == HttpStatus.UNAUTHORIZED_401)
@@ -527,7 +527,7 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
authenticationStore.addAuthentication(authentication);
AtomicBoolean fail = new AtomicBoolean(true);
- GeneratingContentProvider content = new GeneratingContentProvider(index ->
+ GeneratingRequestContent content = new GeneratingRequestContent(index ->
{
switch (index)
{
@@ -546,9 +546,8 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
catch (InterruptedException ignored)
{
}
-
// Trigger request failure.
- throw new RuntimeException();
+ throw new RuntimeException("explicitly_thrown_by_test");
}
else
{
@@ -563,7 +562,7 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.path("/secure")
- .content(content)
+ .body(content)
.onResponseSuccess(r -> authLatch.countDown())
.send(result ->
{
@@ -803,23 +802,16 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
assertEquals(headerInfo.getParameter("nonce"), "1523430383=");
}
- private static class GeneratingContentProvider implements ContentProvider
+ private static class GeneratingRequestContent extends AbstractRequestContent
{
- private static final ByteBuffer DONE = ByteBuffer.allocate(0);
-
private final IntFunction generator;
- private GeneratingContentProvider(IntFunction generator)
+ private GeneratingRequestContent(IntFunction generator)
{
+ super("application/octet-stream");
this.generator = generator;
}
- @Override
- public long getLength()
- {
- return -1;
- }
-
@Override
public boolean isReproducible()
{
@@ -827,36 +819,32 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
}
@Override
- public Iterator iterator()
+ protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure)
{
- return new Iterator()
+ return new SubscriptionImpl(consumer, emitInitialContent, failure);
+ }
+
+ private class SubscriptionImpl extends AbstractSubscription
+ {
+ private int index;
+
+ public SubscriptionImpl(Consumer consumer, boolean emitInitialContent, Throwable failure)
{
- private int index;
- public ByteBuffer current;
+ super(consumer, emitInitialContent, failure);
+ }
- @Override
- @SuppressWarnings("ReferenceEquality")
- public boolean hasNext()
+ @Override
+ protected boolean produceContent(Producer producer)
+ {
+ ByteBuffer buffer = generator.apply(index++);
+ boolean last = false;
+ if (buffer == null)
{
- if (current == null)
- {
- current = generator.apply(index++);
- if (current == null)
- current = DONE;
- }
- return current != DONE;
+ buffer = BufferUtil.EMPTY_BUFFER;
+ last = true;
}
-
- @Override
- public ByteBuffer next()
- {
- ByteBuffer result = current;
- current = null;
- if (result == null)
- throw new NoSuchElementException();
- return result;
- }
- };
+ return producer.produce(buffer, last, Callback.NOOP);
+ }
}
}
}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientFailureTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientFailureTest.java
index a7c32775530..a9a4f2d42ac 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientFailureTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientFailureTest.java
@@ -28,7 +28,7 @@ import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
-import org.eclipse.jetty.client.util.DeferredContentProvider;
+import org.eclipse.jetty.client.util.AsyncRequestContent;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
@@ -116,16 +116,16 @@ public class HttpClientFailureTest
});
client.start();
- final CountDownLatch commitLatch = new CountDownLatch(1);
- final CountDownLatch completeLatch = new CountDownLatch(1);
- DeferredContentProvider content = new DeferredContentProvider();
+ CountDownLatch commitLatch = new CountDownLatch(1);
+ CountDownLatch completeLatch = new CountDownLatch(1);
+ AsyncRequestContent content = new AsyncRequestContent();
client.newRequest("localhost", connector.getLocalPort())
.onRequestCommit(request ->
{
connectionRef.get().getEndPoint().close();
commitLatch.countDown();
})
- .content(content)
+ .body(content)
.idleTimeout(2, TimeUnit.SECONDS)
.send(result ->
{
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientRedirectTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientRedirectTest.java
index a6ef79dcaa7..442c317d4a5 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientRedirectTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientRedirectTest.java
@@ -36,7 +36,7 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
-import org.eclipse.jetty.client.util.ByteBufferContentProvider;
+import org.eclipse.jetty.client.util.ByteBufferRequestContent;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
@@ -153,7 +153,7 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
.scheme(scenario.getScheme())
.method(HttpMethod.POST)
.path("/307/localhost/done")
- .content(new ByteBufferContentProvider(ByteBuffer.wrap(data)))
+ .body(new ByteBufferRequestContent(ByteBuffer.wrap(data)))
.timeout(5, TimeUnit.SECONDS)
.send();
assertNotNull(response);
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientSynchronizationTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientSynchronizationTest.java
index eab4e28f00d..411d2d3a3d5 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientSynchronizationTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientSynchronizationTest.java
@@ -47,20 +47,22 @@ public class HttpClientSynchronizationTest extends AbstractHttpClientServerTest
server.stop();
int count = 10;
- final CountDownLatch latch = new CountDownLatch(count);
+ CountDownLatch latch = new CountDownLatch(count);
for (int i = 0; i < count; ++i)
{
Request request = client.newRequest("localhost", port)
- .scheme(scenario.getScheme());
+ .scheme(scenario.getScheme())
+ .path("/" + i);
- synchronized (this)
+ Object lock = this;
+ synchronized (lock)
{
request.send(new Response.Listener.Adapter()
{
@Override
public void onFailure(Response response, Throwable failure)
{
- synchronized (HttpClientSynchronizationTest.this)
+ synchronized (lock)
{
assertThat(failure, Matchers.instanceOf(ConnectException.class));
latch.countDown();
@@ -80,20 +82,22 @@ public class HttpClientSynchronizationTest extends AbstractHttpClientServerTest
start(scenario, new EmptyServerHandler());
int count = 10;
- final CountDownLatch latch = new CountDownLatch(count);
+ CountDownLatch latch = new CountDownLatch(count);
for (int i = 0; i < count; ++i)
{
Request request = client.newRequest("localhost", connector.getLocalPort())
- .scheme(scenario.getScheme());
+ .scheme(scenario.getScheme())
+ .path("/" + i);
- synchronized (this)
+ Object lock = this;
+ synchronized (lock)
{
request.send(new Response.Listener.Adapter()
{
@Override
public void onComplete(Result result)
{
- synchronized (HttpClientSynchronizationTest.this)
+ synchronized (lock)
{
assertFalse(result.isFailed());
latch.countDown();
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
index aff6cd24616..481c1fc0db0 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
@@ -38,10 +38,8 @@ import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.NoSuchElementException;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Exchanger;
@@ -59,7 +57,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.Connection;
-import org.eclipse.jetty.client.api.ContentProvider;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.client.api.Request;
@@ -67,11 +64,12 @@ import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
+import org.eclipse.jetty.client.util.AbstractRequestContent;
+import org.eclipse.jetty.client.util.AsyncRequestContent;
import org.eclipse.jetty.client.util.BufferingResponseListener;
-import org.eclipse.jetty.client.util.BytesContentProvider;
-import org.eclipse.jetty.client.util.DeferredContentProvider;
+import org.eclipse.jetty.client.util.BytesRequestContent;
import org.eclipse.jetty.client.util.FutureResponseListener;
-import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.client.util.StringRequestContent;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
@@ -231,7 +229,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
});
String value1 = "\u20AC";
- String paramValue1 = URLEncoder.encode(value1, "UTF-8");
+ String paramValue1 = URLEncoder.encode(value1, StandardCharsets.UTF_8);
String query = paramName1 + "=" + paramValue1 + "&" + paramName2;
ContentResponse response = client.GET(scenario.getScheme() + "://localhost:" + connector.getLocalPort() + "/?" + query);
@@ -268,9 +266,9 @@ public class HttpClientTest extends AbstractHttpClientServerTest
String value11 = "\u20AC";
String value12 = "\u20AA";
String value2 = "&";
- String paramValue11 = URLEncoder.encode(value11, "UTF-8");
- String paramValue12 = URLEncoder.encode(value12, "UTF-8");
- String paramValue2 = URLEncoder.encode(value2, "UTF-8");
+ String paramValue11 = URLEncoder.encode(value11, StandardCharsets.UTF_8);
+ String paramValue12 = URLEncoder.encode(value12, StandardCharsets.UTF_8);
+ String paramValue2 = URLEncoder.encode(value2, StandardCharsets.UTF_8);
String query = paramName1 + "=" + paramValue11 + "&" + paramName1 + "=" + paramValue12 + "&" + paramName2 + "=" + paramValue2;
ContentResponse response = client.GET(scenario.getScheme() + "://localhost:" + connector.getLocalPort() + "/?" + query);
@@ -318,7 +316,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
{
String paramName = "a";
String paramValue = "\u20AC";
- String encodedParamValue = URLEncoder.encode(paramValue, "UTF-8");
+ String encodedParamValue = URLEncoder.encode(paramValue, StandardCharsets.UTF_8);
start(scenario, new AbstractHandler()
{
@Override
@@ -372,7 +370,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
ContentResponse response = client.POST(scenario.getScheme() + "://localhost:" + connector.getLocalPort() + "/?b=1")
.param(paramName, paramValue)
- .content(new BytesContentProvider(content))
+ .body(new BytesRequestContent(content))
.timeout(5, TimeUnit.SECONDS)
.send();
@@ -404,7 +402,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
if (!Arrays.equals(content, bytes))
request.abort(new Exception());
})
- .content(new BytesContentProvider(content))
+ .body(new BytesRequestContent(content))
.timeout(5, TimeUnit.SECONDS)
.send();
@@ -435,7 +433,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
buffer.get(bytes);
assertEquals(bytes[0], progress.getAndIncrement());
})
- .content(new BytesContentProvider(new byte[]{0}, new byte[]{1}, new byte[]{2}, new byte[]{3}, new byte[]{4}))
+ .body(new BytesRequestContent(new byte[]{0}, new byte[]{1}, new byte[]{2}, new byte[]{3}, new byte[]{4}))
.timeout(5, TimeUnit.SECONDS)
.send();
@@ -511,7 +509,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
client.setMaxConnectionsPerDestination(1);
- try (StacklessLogging stackless = new StacklessLogging(org.eclipse.jetty.server.HttpChannel.class))
+ try (StacklessLogging ignored = new StacklessLogging(org.eclipse.jetty.server.HttpChannel.class))
{
CountDownLatch latch = new CountDownLatch(2);
client.newRequest("localhost", connector.getLocalPort())
@@ -630,36 +628,23 @@ public class HttpClientTest extends AbstractHttpClientServerTest
CountDownLatch latch = new CountDownLatch(1);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
- // The second ByteBuffer set to null will throw an exception
- .content(new ContentProvider()
+ .body(new AbstractRequestContent("application/octet-stream")
{
@Override
- public long getLength()
+ protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure)
{
- return -1;
- }
-
- @Override
- public Iterator iterator()
- {
- return new Iterator<>()
+ return new AbstractSubscription(consumer, emitInitialContent, failure)
{
- @Override
- public boolean hasNext()
- {
- return true;
- }
+ private int count;
@Override
- public ByteBuffer next()
+ protected boolean produceContent(Producer producer) throws Exception
{
- throw new NoSuchElementException("explicitly_thrown_by_test");
- }
-
- @Override
- public void remove()
- {
- throw new UnsupportedOperationException();
+ if (count == 2)
+ throw new IOException("explicitly_thrown_by_test");
+ ByteBuffer buffer = BufferUtil.allocate(512);
+ ++count;
+ return producer.produce(buffer, false, Callback.NOOP);
}
};
}
@@ -1244,7 +1229,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{
// Send the headers at this point, then write the content
- byte[] content = "TEST".getBytes("UTF-8");
+ byte[] content = "TEST".getBytes(StandardCharsets.UTF_8);
response.setContentLength(content.length);
response.flushBuffer();
response.getOutputStream().write(content);
@@ -1413,11 +1398,11 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
});
- DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.wrap(new byte[]{0}));
+ AsyncRequestContent content = new AsyncRequestContent(ByteBuffer.wrap(new byte[]{0}));
Request request = client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.version(version)
- .content(content);
+ .body(content);
FutureResponseListener listener = new FutureResponseListener(request);
request.send(listener);
// Wait some time to simulate a slow request.
@@ -1530,7 +1515,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector)
{
@Override
- public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map context) throws IOException
+ public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map context)
{
return new HttpConnectionOverHTTP(endPoint, context)
{
@@ -1658,7 +1643,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
assertCopyRequest(client.newRequest("http://example.com/some/url")
.method(HttpMethod.HEAD)
.version(HttpVersion.HTTP_2)
- .content(new StringContentProvider("some string"))
+ .body(new StringRequestContent("some string"))
.timeout(321, TimeUnit.SECONDS)
.idleTimeout(2221, TimeUnit.SECONDS)
.followRedirects(true)
@@ -1668,7 +1653,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
assertCopyRequest(client.newRequest("https://example.com")
.method(HttpMethod.POST)
.version(HttpVersion.HTTP_1_0)
- .content(new StringContentProvider("some other string"))
+ .body(new StringRequestContent("some other string"))
.timeout(123231, TimeUnit.SECONDS)
.idleTimeout(232342, TimeUnit.SECONDS)
.followRedirects(false)
@@ -1797,7 +1782,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@Override
- public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{
baseRequest.setHandled(true);
ServletOutputStream output = response.getOutputStream();
@@ -1845,7 +1830,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
assertEquals(original.getURI(), copy.getURI());
assertEquals(original.getMethod(), copy.getMethod());
assertEquals(original.getVersion(), copy.getVersion());
- assertEquals(original.getContent(), copy.getContent());
+ assertEquals(original.getBody(), copy.getBody());
assertEquals(original.getIdleTimeout(), copy.getIdleTimeout());
assertEquals(original.getTimeout(), copy.getTimeout());
assertEquals(original.isFollowRedirects(), copy.isFollowRedirects());
@@ -1910,7 +1895,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
.scheme(scheme)
.method("POST")
.param("attempt", String.valueOf(retries))
- .content(new StringContentProvider("0123456789ABCDEF"))
+ .body(new StringRequestContent("0123456789ABCDEF"))
.send(this);
}
}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientUploadDuringServerShutdownTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientUploadDuringServerShutdownTest.java
index 7572259e681..a3573378a5a 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientUploadDuringServerShutdownTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientUploadDuringServerShutdownTest.java
@@ -32,7 +32,7 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.http.HttpChannelOverHTTP;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
-import org.eclipse.jetty.client.util.BytesContentProvider;
+import org.eclipse.jetty.client.util.BytesRequestContent;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
@@ -116,7 +116,7 @@ public class HttpClientUploadDuringServerShutdownTest
{
int length = 16 * 1024 * 1024 + random.nextInt(16 * 1024 * 1024);
client.newRequest("localhost", 8888)
- .content(new BytesContentProvider(new byte[length]))
+ .body(new BytesRequestContent(new byte[length]))
.send(result -> latch.countDown());
long sleep = 1 + random.nextInt(10);
TimeUnit.MILLISECONDS.sleep(sleep);
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java
index c0d4aba223c..406a602f136 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java
@@ -32,7 +32,7 @@ import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
-import org.eclipse.jetty.client.util.ByteBufferContentProvider;
+import org.eclipse.jetty.client.util.ByteBufferRequestContent;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.handler.AbstractHandler;
@@ -408,7 +408,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
CountDownLatch latch = new CountDownLatch(1);
ByteBuffer buffer = ByteBuffer.allocate(16 * 1024 * 1024);
Arrays.fill(buffer.array(), (byte)'x');
- request.content(new ByteBufferContentProvider(buffer))
+ request.body(new ByteBufferRequestContent(buffer))
.send(new Response.Listener.Adapter()
{
@Override
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpRequestAbortTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpRequestAbortTest.java
index d16996c8e26..fc94ff3bfef 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpRequestAbortTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpRequestAbortTest.java
@@ -32,7 +32,7 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Result;
-import org.eclipse.jetty.client.util.ByteBufferContentProvider;
+import org.eclipse.jetty.client.util.ByteBufferRequestContent;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.StacklessLogging;
@@ -268,7 +268,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
{
aborted.set(r.abort(cause));
latch.countDown();
- }).content(new ByteBufferContentProvider(ByteBuffer.wrap(new byte[]{0}), ByteBuffer.wrap(new byte[]{1}))
+ }).body(new ByteBufferRequestContent(ByteBuffer.wrap(new byte[]{0}), ByteBuffer.wrap(new byte[]{1}))
{
@Override
public long getLength()
@@ -323,7 +323,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
{
aborted.set(r.abort(cause));
latch.countDown();
- }).content(new ByteBufferContentProvider(ByteBuffer.wrap(new byte[]{0}), ByteBuffer.wrap(new byte[]{1}))
+ }).body(new ByteBufferRequestContent(ByteBuffer.wrap(new byte[]{0}), ByteBuffer.wrap(new byte[]{1}))
{
@Override
public long getLength()
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseAbortTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseAbortTest.java
index 191a60dafd1..c85c98c253a 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseAbortTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseAbortTest.java
@@ -24,11 +24,10 @@ import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
-import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jetty.client.util.DeferredContentProvider;
+import org.eclipse.jetty.client.util.AsyncRequestContent;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.junit.jupiter.params.ParameterizedTest;
@@ -104,7 +103,7 @@ public class HttpResponseAbortTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@Override
- public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
{
try
{
@@ -141,7 +140,7 @@ public class HttpResponseAbortTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@Override
- public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
{
try
{
@@ -159,18 +158,18 @@ public class HttpResponseAbortTest extends AbstractHttpClientServerTest
}
});
- final DeferredContentProvider contentProvider = new DeferredContentProvider(ByteBuffer.allocate(1));
- final AtomicInteger completes = new AtomicInteger();
- final CountDownLatch completeLatch = new CountDownLatch(1);
+ AsyncRequestContent requestContent = new AsyncRequestContent(ByteBuffer.allocate(1));
+ AtomicInteger completes = new AtomicInteger();
+ CountDownLatch completeLatch = new CountDownLatch(1);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
- .content(contentProvider)
+ .body(requestContent)
.onResponseContent((response, content) ->
{
try
{
response.abort(new Exception());
- contentProvider.close();
+ requestContent.close();
// Delay to let the request side to finish its processing.
Thread.sleep(1000);
}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/api/Usage.java b/jetty-client/src/test/java/org/eclipse/jetty/client/api/Usage.java
index 1d6d86a792d..81a06f8ec8f 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/api/Usage.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/api/Usage.java
@@ -32,12 +32,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.util.AsyncRequestContent;
import org.eclipse.jetty.client.util.BasicAuthentication;
-import org.eclipse.jetty.client.util.DeferredContentProvider;
import org.eclipse.jetty.client.util.FutureResponseListener;
-import org.eclipse.jetty.client.util.InputStreamContentProvider;
+import org.eclipse.jetty.client.util.InputStreamRequestContent;
import org.eclipse.jetty.client.util.InputStreamResponseListener;
-import org.eclipse.jetty.client.util.OutputStreamContentProvider;
+import org.eclipse.jetty.client.util.OutputStreamRequestContent;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.util.FuturePromise;
@@ -101,16 +101,12 @@ public class Usage
client.newRequest("localhost", 8080)
// Send asynchronously
- .send(new Response.CompleteListener()
+ .send(result ->
{
- @Override
- public void onComplete(Result result)
+ if (result.isSucceeded())
{
- if (result.isSucceeded())
- {
- responseRef.set(result.getResponse());
- latch.countDown();
- }
+ responseRef.set(result.getResponse());
+ latch.countDown();
}
});
@@ -278,7 +274,7 @@ public class Usage
ContentResponse response = client.newRequest("localhost", 8080)
// Provide the content as InputStream
- .content(new InputStreamContentProvider(input))
+ .body(new InputStreamRequestContent(input))
.send();
assertEquals(200, response.getStatus());
@@ -290,11 +286,11 @@ public class Usage
HttpClient client = new HttpClient();
client.start();
- OutputStreamContentProvider content = new OutputStreamContentProvider();
+ OutputStreamRequestContent content = new OutputStreamRequestContent();
try (OutputStream output = content.getOutputStream())
{
client.newRequest("localhost", 8080)
- .content(content)
+ .body(content)
.send(result -> assertEquals(200, result.getResponse().getStatus()));
output.write(new byte[1024]);
@@ -308,15 +304,15 @@ public class Usage
public void testProxyUsage() throws Exception
{
// In proxies, we receive the headers but not the content, so we must be able to send the request with
- // a lazy content provider that does not block request.send(...)
+ // a lazy request content that does not block request.send(...)
HttpClient client = new HttpClient();
client.start();
- final AtomicBoolean sendContent = new AtomicBoolean(true);
- DeferredContentProvider async = new DeferredContentProvider(ByteBuffer.wrap(new byte[]{0, 1, 2}));
+ AtomicBoolean sendContent = new AtomicBoolean(true);
+ AsyncRequestContent async = new AsyncRequestContent(ByteBuffer.wrap(new byte[]{0, 1, 2}));
client.newRequest("localhost", 8080)
- .content(async)
+ .body(async)
.send(new Response.Listener.Adapter()
{
@Override
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpSenderOverHTTPTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpSenderOverHTTPTest.java
index 374fa8eda0b..e7c1df5d2aa 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpSenderOverHTTPTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpSenderOverHTTPTest.java
@@ -33,7 +33,7 @@ import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
-import org.eclipse.jetty.client.util.ByteBufferContentProvider;
+import org.eclipse.jetty.client.util.ByteBufferRequestContent;
import org.eclipse.jetty.io.ByteArrayEndPoint;
import org.eclipse.jetty.util.Promise;
import org.hamcrest.Matchers;
@@ -201,7 +201,7 @@ public class HttpSenderOverHTTPTest
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter());
Request request = client.newRequest(URI.create("http://localhost/"));
String content = "abcdef";
- request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content.getBytes(StandardCharsets.UTF_8))));
+ request.body(new ByteBufferRequestContent(ByteBuffer.wrap(content.getBytes(StandardCharsets.UTF_8))));
final CountDownLatch headersLatch = new CountDownLatch(1);
final CountDownLatch successLatch = new CountDownLatch(1);
request.listener(new Request.Listener.Adapter()
@@ -237,7 +237,7 @@ public class HttpSenderOverHTTPTest
Request request = client.newRequest(URI.create("http://localhost/"));
String content1 = "0123456789";
String content2 = "abcdef";
- request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content1.getBytes(StandardCharsets.UTF_8)), ByteBuffer.wrap(content2.getBytes(StandardCharsets.UTF_8))));
+ request.body(new ByteBufferRequestContent(ByteBuffer.wrap(content1.getBytes(StandardCharsets.UTF_8)), ByteBuffer.wrap(content2.getBytes(StandardCharsets.UTF_8))));
final CountDownLatch headersLatch = new CountDownLatch(1);
final CountDownLatch successLatch = new CountDownLatch(1);
request.listener(new Request.Listener.Adapter()
@@ -273,7 +273,7 @@ public class HttpSenderOverHTTPTest
Request request = client.newRequest(URI.create("http://localhost/"));
String content1 = "0123456789";
String content2 = "ABCDEF";
- request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content1.getBytes(StandardCharsets.UTF_8)), ByteBuffer.wrap(content2.getBytes(StandardCharsets.UTF_8)))
+ request.body(new ByteBufferRequestContent(ByteBuffer.wrap(content1.getBytes(StandardCharsets.UTF_8)), ByteBuffer.wrap(content2.getBytes(StandardCharsets.UTF_8)))
{
@Override
public long getLength()
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/util/AsyncRequestContentTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/util/AsyncRequestContentTest.java
new file mode 100644
index 00000000000..03dee4af220
--- /dev/null
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/util/AsyncRequestContentTest.java
@@ -0,0 +1,151 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.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.client.util;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.util.Callback;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class AsyncRequestContentTest
+{
+ private ExecutorService executor;
+
+ @BeforeEach
+ public void prepare()
+ {
+ executor = Executors.newCachedThreadPool();
+ }
+
+ @AfterEach
+ public void dispose()
+ {
+ executor.shutdownNow();
+ }
+
+ @Test
+ public void testWhenEmptyFlushDoesNotBlock() throws Exception
+ {
+ AsyncRequestContent content = new AsyncRequestContent();
+
+ Future> task = executor.submit(() ->
+ {
+ content.flush();
+ return null;
+ });
+
+ assertTrue(await(task, 5000));
+ }
+
+ @Test
+ public void testOfferFlushDemandBlocksUntilSucceeded() throws Exception
+ {
+ AsyncRequestContent content = new AsyncRequestContent();
+ content.offer(ByteBuffer.allocate(1));
+
+ Future> task = executor.submit(() ->
+ {
+ content.flush();
+ return null;
+ });
+
+ // Wait until flush() blocks.
+ assertFalse(await(task, 500));
+
+ AtomicReference callbackRef = new AtomicReference<>();
+ content.subscribe((buffer, last, callback) -> callbackRef.set(callback), true).demand();
+
+ // Flush should block until the callback is succeeded.
+ assertFalse(await(task, 500));
+
+ callbackRef.get().succeeded();
+
+ // Flush should return.
+ assertTrue(await(task, 5000));
+ }
+
+ @Test
+ public void testCloseFlushDoesNotBlock() throws Exception
+ {
+ AsyncRequestContent content = new AsyncRequestContent();
+ content.close();
+
+ Future> task = executor.submit(() ->
+ {
+ content.flush();
+ return null;
+ });
+
+ assertTrue(await(task, 5000));
+ }
+
+ @Test
+ public void testStallThenCloseProduces() throws Exception
+ {
+ AsyncRequestContent content = new AsyncRequestContent();
+
+ CountDownLatch latch = new CountDownLatch(1);
+ Request.Content.Subscription subscription = content.subscribe((buffer, last, callback) ->
+ {
+ callback.succeeded();
+ if (last)
+ latch.countDown();
+ }, true);
+
+ // Demand the initial content.
+ subscription.demand();
+
+ // Content must not be the last one.
+ assertFalse(latch.await(1, TimeUnit.SECONDS));
+
+ // Demand more content, now we are stalled.
+ subscription.demand();
+
+ // Close, we must be notified.
+ content.close();
+
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ }
+
+ private boolean await(Future> task, long time) throws Exception
+ {
+ try
+ {
+ task.get(time, TimeUnit.MILLISECONDS);
+ return true;
+ }
+ catch (TimeoutException x)
+ {
+ return false;
+ }
+ }
+}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/util/DeferredContentProviderTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/util/DeferredContentProviderTest.java
deleted file mode 100644
index 88304c9f249..00000000000
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/util/DeferredContentProviderTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-//
-// This program and the accompanying materials are made available under
-// the terms of the Eclipse Public License 2.0 which is available at
-// https://www.eclipse.org/legal/epl-2.0
-//
-// This Source Code may also be made available under the following
-// Secondary Licenses when the conditions for such availability set
-// forth in the Eclipse Public License, v. 2.0 are satisfied:
-// the Apache License v2.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.client.util;
-
-import java.nio.ByteBuffer;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.eclipse.jetty.util.Callback;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-public class DeferredContentProviderTest
-{
- private ExecutorService executor;
-
- @BeforeEach
- public void prepare() throws Exception
- {
- executor = Executors.newCachedThreadPool();
- }
-
- @AfterEach
- public void dispose() throws Exception
- {
- executor.shutdownNow();
- }
-
- @Test
- public void testWhenEmptyFlushDoesNotBlock() throws Exception
- {
- final DeferredContentProvider provider = new DeferredContentProvider();
-
- Future> task = executor.submit(new Callable
*
Content producers must notify content to the consumer only if there is demand.
*
Content consumers can generate demand for content by invoking {@link Subscription#demand()}.
+ *
Content production must follow this algorithm:
+ *
+ *
the first time content is demanded
+ *
+ *
when the content is not available => produce an empty content
+ *
when the content is available:
+ *
+ *
when {@code emitInitialContent == false} => produce an empty content
+ *
when {@code emitInitialContent == true} => produce the content
+ *
+ *
+ *
+ *
+ *
the second and subsequent times content is demanded
+ *
+ *
when the content is not available => do not produce content
+ *
when the content is available => produce the content
Partial implementation of {@link Request.Content}.
+ *
Manages a single subscription at a time (multiple simultaneous subscriptions are not allowed).
+ */
public abstract class AbstractRequestContent implements Request.Content
{
private static final Logger LOG = LoggerFactory.getLogger(AbstractRequestContent.class);
@@ -85,13 +89,19 @@ public abstract class AbstractRequestContent implements Request.Content
subscription.fail(failure);
}
+ /**
+ *
Partial implementation of {@code Subscription}.
+ *
Implements the algorithm described in {@link Request.Content}.
+ */
public abstract class AbstractSubscription implements Subscription
{
private final Consumer consumer;
private final boolean emitInitialContent;
private Throwable failure;
private int demand;
+ // Whether content production was stalled because there was no demand.
private boolean stalled;
+ // Whether the first content has been produced.
private boolean committed;
public AbstractSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure)
@@ -157,6 +167,32 @@ public abstract class AbstractRequestContent implements Request.Content
}
}
+ /**
+ *
Subclasses implement this method to produce content,
+ * without worrying about demand or exception handling.
+ *
Typical implementation (pseudo code):
+ *
+ * protected boolean produceContent(Producer producer) throws Exception
+ * {
+ * // Step 1: try to produce content, exceptions may be thrown during production
+ * // (for example, producing content reading from an InputStream may throw).
+ *
+ * // Step 2A: content could be produced.
+ * ByteBuffer buffer = ...;
+ * boolean last = ...;
+ * Callback callback = ...;
+ * return producer.produce(buffer, last, callback);
+ *
+ * // Step 2B: content could not be produced.
+ * // (for example it is not available yet)
+ * return false;
+ * }
+ *
+ *
+ * @param producer the producer to notify when content can be produced
+ * @return whether content production should continue
+ * @throws Exception when content production fails
+ */
protected abstract boolean produceContent(Producer producer) throws Exception;
@Override
@@ -232,14 +268,16 @@ public abstract class AbstractRequestContent implements Request.Content
{
int demand;
boolean stalled;
+ boolean committed;
try (AutoLock ignored = lock.lock())
{
demand = this.demand;
stalled = this.stalled;
+ committed = this.committed;
}
- return String.format("%s.%s@%x[demand=%d,stalled=%b]",
+ return String.format("%s.%s@%x[demand=%d,stalled=%b,committed=%b,emitInitial=%b]",
getClass().getEnclosingClass().getSimpleName(),
- getClass().getSimpleName(), hashCode(), demand, stalled);
+ getClass().getSimpleName(), hashCode(), demand, stalled, committed, emitInitialContent);
}
}
From f3bd6611f468b28ee655bea2aec92122348ee40f Mon Sep 17 00:00:00 2001
From: Joakim Erdfelt
Date: Tue, 24 Mar 2020 08:41:32 -0500
Subject: [PATCH 008/101] Fix OWB (Open WebBeans) tests.
+ OWB needs init-parameter to allow it's own
ServletContainerInitializer found at
org.apache.webbeans.servlet.WebBeansConfigurationListener$Auto
to execute and properly add the required Listener for
OWB to function.
+ See OWB-1296
Signed-off-by: Joakim Erdfelt
---
.../src/test/resources/jetty-logging.properties | 1 +
tests/test-webapps/test-owb-cdi-webapp/pom.xml | 2 +-
.../services/javax.servlet.ServletContainerInitializer | 1 -
.../src/main/webapp/WEB-INF/jetty-env.xml | 9 ++++++++-
4 files changed, 10 insertions(+), 3 deletions(-)
delete mode 100644 tests/test-webapps/test-owb-cdi-webapp/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer
diff --git a/tests/test-distribution/src/test/resources/jetty-logging.properties b/tests/test-distribution/src/test/resources/jetty-logging.properties
index 56cc73e5d68..6c7059f135c 100644
--- a/tests/test-distribution/src/test/resources/jetty-logging.properties
+++ b/tests/test-distribution/src/test/resources/jetty-logging.properties
@@ -1,2 +1,3 @@
# Jetty Logging using jetty-slf4j-impl
+org.eclipse.jetty.logging.appender.MESSAGE_ESCAPE=false
#org.eclipse.jetty.LEVEL=DEBUG
diff --git a/tests/test-webapps/test-owb-cdi-webapp/pom.xml b/tests/test-webapps/test-owb-cdi-webapp/pom.xml
index 7540b59c8dd..42b5630fbd5 100644
--- a/tests/test-webapps/test-owb-cdi-webapp/pom.xml
+++ b/tests/test-webapps/test-owb-cdi-webapp/pom.xml
@@ -64,7 +64,7 @@
org.apache.openwebbeansopenwebbeans-jetty9
- 2.0.11
+ 2.0.15
diff --git a/tests/test-webapps/test-owb-cdi-webapp/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer b/tests/test-webapps/test-owb-cdi-webapp/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer
deleted file mode 100644
index f164a18c5a8..00000000000
--- a/tests/test-webapps/test-owb-cdi-webapp/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer
+++ /dev/null
@@ -1 +0,0 @@
-org.eclipse.jetty.cdi.owb.OwbServletContainerInitializer
\ No newline at end of file
diff --git a/tests/test-webapps/test-owb-cdi-webapp/src/main/webapp/WEB-INF/jetty-env.xml b/tests/test-webapps/test-owb-cdi-webapp/src/main/webapp/WEB-INF/jetty-env.xml
index 99f1d2befa7..44a3d0cb030 100644
--- a/tests/test-webapps/test-owb-cdi-webapp/src/main/webapp/WEB-INF/jetty-env.xml
+++ b/tests/test-webapps/test-owb-cdi-webapp/src/main/webapp/WEB-INF/jetty-env.xml
@@ -1,9 +1,16 @@
+
+
+ openwebbeans.web.sci.active
+ true
+
+
-
+ BeanManager
From 0f2ddc8c9f1ab0e677e3b6999992b2d88a0bc838 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Thu, 26 Mar 2020 17:46:59 +0100
Subject: [PATCH 009/101] Issue #4400 - Review HttpClient's ContentProvider.
Review updates.
* Now AbstractRequestContent supports multiple subscriptions.
* Reviewed abort() path to fail the content and the subscription
and notify FailureListener sequentially with other listeners.
Signed-off-by: Simone Bordet
---
.../eclipse/jetty/client/HttpExchange.java | 7 ++
.../org/eclipse/jetty/client/HttpRequest.java | 4 --
.../org/eclipse/jetty/client/HttpSender.java | 69 +++++++++----------
.../client/util/AbstractRequestContent.java | 23 ++-----
.../client/util/MultiPartRequestContent.java | 9 ++-
.../util/RequestContentBehaviorTest.java | 57 +++++++++++----
6 files changed, 98 insertions(+), 71 deletions(-)
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java
index 1a743f496f9..75a89dcefe4 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java
@@ -20,6 +20,7 @@ package org.eclipse.jetty.client;
import java.util.List;
+import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.slf4j.Logger;
@@ -237,6 +238,12 @@ public class HttpExchange
// We failed this exchange, deal with it.
+ // Applications could be blocked providing
+ // request content, notify them of the failure.
+ Request.Content body = request.getBody();
+ if (abortRequest && body != null)
+ body.fail(failure);
+
// Case #1: exchange was in the destination queue.
if (destination.remove(this))
{
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
index e0919bc3f0d..92ec7e7ae8b 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
@@ -824,11 +824,7 @@ public class HttpRequest implements Request
public boolean abort(Throwable cause)
{
if (aborted.compareAndSet(null, Objects.requireNonNull(cause)))
- {
- if (content != null)
- content.fail(cause);
return conversation.abort(cause);
- }
return false;
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
index de4c67c85a5..0fe281890cd 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java
@@ -52,9 +52,9 @@ public abstract class HttpSender
private final ContentConsumer consumer = new ContentConsumer();
private final AtomicReference requestState = new AtomicReference<>(RequestState.QUEUED);
+ private final AtomicReference failure = new AtomicReference<>();
private final HttpChannel channel;
private Request.Content.Subscription subscription;
- private Throwable failure;
protected HttpSender(HttpChannel channel)
{
@@ -78,13 +78,6 @@ public abstract class HttpSender
public void send(HttpExchange exchange)
{
- Request request = exchange.getRequest();
- Request.Content body = request.getBody();
-
- consumer.exchange = exchange;
- consumer.expect100 = expects100Continue(request);
- subscription = body.subscribe(consumer, !consumer.expect100);
-
if (!queuedToBegin(exchange))
return;
@@ -110,10 +103,16 @@ public abstract class HttpSender
RequestNotifier notifier = getHttpChannel().getHttpDestination().getRequestNotifier();
notifier.notifyBegin(request);
+ Request.Content body = request.getBody();
+
+ consumer.exchange = exchange;
+ consumer.expect100 = expects100Continue(request);
+ subscription = body.subscribe(consumer, !consumer.expect100);
+
if (updateRequestState(RequestState.TRANSIENT, RequestState.BEGIN))
return true;
- terminateRequest(exchange);
+ abortRequest(exchange);
return false;
}
@@ -131,7 +130,7 @@ public abstract class HttpSender
if (updateRequestState(RequestState.TRANSIENT, RequestState.HEADERS))
return true;
- terminateRequest(exchange);
+ abortRequest(exchange);
return false;
}
@@ -149,7 +148,7 @@ public abstract class HttpSender
if (updateRequestState(RequestState.TRANSIENT, RequestState.COMMIT))
return true;
- terminateRequest(exchange);
+ abortRequest(exchange);
return false;
}
@@ -173,7 +172,7 @@ public abstract class HttpSender
if (updateRequestState(RequestState.TRANSIENT, RequestState.CONTENT))
return true;
- terminateRequest(exchange);
+ abortRequest(exchange);
return false;
}
default:
@@ -264,13 +263,23 @@ public abstract class HttpSender
}
}
- private void terminateRequest(HttpExchange exchange)
+ private void abortRequest(HttpExchange exchange)
{
- // In abort(), the state is updated before the failure is recorded
- // to avoid to overwrite it, so here we may read a null failure.
- Throwable failure = this.failure;
- if (failure == null)
- failure = new HttpRequestException("Concurrent failure", exchange.getRequest());
+ Throwable failure = this.failure.get();
+
+ if (subscription != null)
+ subscription.fail(failure);
+
+ dispose();
+
+ Request request = exchange.getRequest();
+ if (LOG.isDebugEnabled())
+ LOG.debug("Request abort {} {} on {}: {}", request, exchange, getHttpChannel(), failure);
+ HttpDestination destination = getHttpChannel().getHttpDestination();
+ destination.getRequestNotifier().notifyFailure(request, failure);
+
+ // Mark atomically the request as terminated, with
+ // respect to concurrency between request and response.
Result result = exchange.terminateRequest();
terminateRequest(exchange, failure, result);
}
@@ -353,8 +362,11 @@ public abstract class HttpSender
public boolean abort(HttpExchange exchange, Throwable failure)
{
+ // Store only the first failure.
+ this.failure.compareAndSet(null, failure);
+
// Update the state to avoid more request processing.
- boolean terminate;
+ boolean abort;
while (true)
{
RequestState current = requestState.get();
@@ -366,28 +378,15 @@ public abstract class HttpSender
{
if (updateRequestState(current, RequestState.FAILURE))
{
- terminate = current != RequestState.TRANSIENT;
+ abort = current != RequestState.TRANSIENT;
break;
}
}
}
- this.failure = failure;
-
- dispose();
-
- Request request = exchange.getRequest();
- if (LOG.isDebugEnabled())
- LOG.debug("Request abort {} {} on {}: {}", request, exchange, getHttpChannel(), failure);
- HttpDestination destination = getHttpChannel().getHttpDestination();
- destination.getRequestNotifier().notifyFailure(request, failure);
-
- if (terminate)
+ if (abort)
{
- // Mark atomically the request as terminated, with
- // respect to concurrency between request and response.
- Result result = exchange.terminateRequest();
- terminateRequest(exchange, failure, result);
+ abortRequest(exchange);
return true;
}
else
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractRequestContent.java
index 2b6019cf873..a4cd2469f9e 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractRequestContent.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractRequestContent.java
@@ -18,7 +18,6 @@
package org.eclipse.jetty.client.util;
-import java.io.EOFException;
import java.nio.ByteBuffer;
import org.eclipse.jetty.client.api.Request;
@@ -38,7 +37,6 @@ public abstract class AbstractRequestContent implements Request.Content
private final AutoLock lock = new AutoLock();
private final String contentType;
- private Subscription subscription;
private Throwable failure;
protected AbstractRequestContent(String contentType)
@@ -55,20 +53,15 @@ public abstract class AbstractRequestContent implements Request.Content
@Override
public Subscription subscribe(Consumer consumer, boolean emitInitialContent)
{
- Subscription oldSubscription;
- Subscription newSubscription;
+ Throwable failure;
try (AutoLock ignored = lock.lock())
{
- if (subscription != null && !isReproducible())
- throw new IllegalStateException("Multiple subscriptions not supported on " + this);
- oldSubscription = subscription;
- newSubscription = subscription = newSubscription(consumer, emitInitialContent, failure);
+ failure = this.failure;
}
- if (oldSubscription != null)
- oldSubscription.fail(new EOFException("Content replay"));
+ Subscription subscription = newSubscription(consumer, emitInitialContent, failure);
if (LOG.isDebugEnabled())
- LOG.debug("Content subscription for {}: {}", this, consumer);
- return newSubscription;
+ LOG.debug("Content subscription for {}: {}", subscription, consumer);
+ return subscription;
}
protected abstract Subscription newSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure);
@@ -76,17 +69,11 @@ public abstract class AbstractRequestContent implements Request.Content
@Override
public void fail(Throwable failure)
{
- Subscription subscription = null;
try (AutoLock ignored = lock.lock())
{
if (this.failure == null)
- {
this.failure = failure;
- subscription = this.subscription;
- }
}
- if (subscription != null)
- subscription.fail(failure);
}
/**
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartRequestContent.java
index 9c020c780db..866e3f5c754 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartRequestContent.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartRequestContent.java
@@ -27,6 +27,7 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
+import java.util.stream.IntStream;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpField;
@@ -115,7 +116,8 @@ public class MultiPartRequestContent extends AbstractRequestContent implements C
@Override
protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure)
{
- length = calculateLength();
+ if (closed)
+ length = calculateLength();
return new SubscriptionImpl(consumer, emitInitialContent, failure);
}
@@ -367,7 +369,10 @@ public class MultiPartRequestContent extends AbstractRequestContent implements C
@Override
public void onFailure(Throwable failure)
{
- parts.stream()
+ if (subscription != null)
+ subscription.fail(failure);
+ IntStream.range(index, parts.size())
+ .mapToObj(parts::get)
.map(part -> part.content)
.forEach(content -> content.fail(failure));
}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/util/RequestContentBehaviorTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/util/RequestContentBehaviorTest.java
index 45c3ff305af..c4427e889d0 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/util/RequestContentBehaviorTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/util/RequestContentBehaviorTest.java
@@ -23,9 +23,9 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.List;
+import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -39,6 +39,7 @@ import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.IO;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
@@ -56,21 +57,23 @@ public class RequestContentBehaviorTest
@BeforeAll
public static void prepare() throws IOException
{
- emptyFile = MavenTestingUtils.getTargetTestingPath().resolve("empty.txt");
- Files.newOutputStream(emptyFile, StandardOpenOption.CREATE).close();
- smallFile = MavenTestingUtils.getTargetTestingPath().resolve("small.txt");
- try (var s = Files.newOutputStream(smallFile, StandardOpenOption.CREATE))
- {
- byte[] bytes = new byte[64];
- Arrays.fill(bytes, (byte)'#');
- s.write(bytes);
- }
+ Path testPath = MavenTestingUtils.getTargetTestingPath();
+ Files.createDirectories(testPath);
+ emptyFile = testPath.resolve("empty.txt");
+ Files.write(emptyFile, new byte[0]);
+ smallFile = testPath.resolve("small.txt");
+ byte[] bytes = new byte[64];
+ Arrays.fill(bytes, (byte)'#');
+ Files.write(smallFile, bytes);
}
@AfterAll
public static void dispose() throws IOException
{
- Files.delete(emptyFile);
+ if (smallFile != null)
+ Files.delete(smallFile);
+ if (emptyFile != null)
+ Files.delete(emptyFile);
}
public static List emptyContents() throws IOException
@@ -290,7 +293,7 @@ public class RequestContentBehaviorTest
assertEquals(1, notified.get());
- content.fail(testFailure);
+ subscription.fail(testFailure);
subscription.demand();
assertEquals(1, notified.get());
@@ -339,4 +342,34 @@ public class RequestContentBehaviorTest
assertNotNull(failure);
assertEquals(1, failure.getSuppressed().length);
}
+
+ @Test
+ public void testSameContentMultipleSubscriptions() throws Exception
+ {
+ byte[] bytes = new byte[64];
+ new Random().nextBytes(bytes);
+ Request.Content content = new BytesRequestContent(bytes);
+
+ CountDownLatch latch1 = new CountDownLatch(1);
+ Request.Content.Subscription subscription1 = content.subscribe((buffer, last, callback) ->
+ {
+ if (last)
+ latch1.countDown();
+ }, true);
+
+ CountDownLatch latch2 = new CountDownLatch(1);
+ Request.Content.Subscription subscription2 = content.subscribe((buffer, last, callback) ->
+ {
+ if (last)
+ latch2.countDown();
+ }, true);
+
+ // Initial demand.
+ subscription1.demand();
+ assertTrue(latch1.await(5, TimeUnit.SECONDS));
+
+ // Initial demand.
+ subscription2.demand();
+ assertTrue(latch2.await(5, TimeUnit.SECONDS));
+ }
}
From 708115f6095bd65052dbf5ff93fddcf9b1a16053 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Fri, 27 Mar 2020 15:33:21 +0100
Subject: [PATCH 010/101] Issue #4400 - Review HttpClient's ContentProvider.
Review updates.
* Updated AbstractRequestContent (and subclasses) failure handling.
* Updated MultiPartRequestContent failure handling.
Signed-off-by: Simone Bordet
---
.../client/util/AbstractRequestContent.java | 24 ++--------
.../client/util/ByteBufferRequestContent.java | 8 ++--
.../client/util/BytesRequestContent.java | 8 ++--
.../util/InputStreamRequestContent.java | 11 +++--
.../client/util/MultiPartRequestContent.java | 31 +++++++-----
.../jetty/client/util/PathRequestContent.java | 15 ++++--
.../client/HttpClientAuthenticationTest.java | 8 ++--
.../eclipse/jetty/client/HttpClientTest.java | 4 +-
.../client/util/MultiPartContentTest.java | 4 +-
.../util/RequestContentBehaviorTest.java | 48 +++----------------
10 files changed, 62 insertions(+), 99 deletions(-)
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractRequestContent.java
index a4cd2469f9e..7b90acb084d 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractRequestContent.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/AbstractRequestContent.java
@@ -29,7 +29,6 @@ import org.slf4j.LoggerFactory;
/**
*
Partial implementation of {@link Request.Content}.
- *
Manages a single subscription at a time (multiple simultaneous subscriptions are not allowed).
@@ -91,11 +74,10 @@ public abstract class AbstractRequestContent implements Request.Content
// Whether the first content has been produced.
private boolean committed;
- public AbstractSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ public AbstractSubscription(Consumer consumer, boolean emitInitialContent)
{
this.consumer = consumer;
this.emitInitialContent = emitInitialContent;
- this.failure = failure;
this.stalled = true;
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/ByteBufferRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/ByteBufferRequestContent.java
index c5c9f35f36b..36362acfc3f 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/ByteBufferRequestContent.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/ByteBufferRequestContent.java
@@ -63,18 +63,18 @@ public class ByteBufferRequestContent extends AbstractRequestContent
}
@Override
- protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent)
{
- return new SubscriptionImpl(consumer, emitInitialContent, failure);
+ return new SubscriptionImpl(consumer, emitInitialContent);
}
private class SubscriptionImpl extends AbstractSubscription
{
private int index;
- private SubscriptionImpl(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ private SubscriptionImpl(Consumer consumer, boolean emitInitialContent)
{
- super(consumer, emitInitialContent, failure);
+ super(consumer, emitInitialContent);
}
@Override
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BytesRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BytesRequestContent.java
index c1e2e09ba42..43001d607ce 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BytesRequestContent.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BytesRequestContent.java
@@ -60,18 +60,18 @@ public class BytesRequestContent extends AbstractRequestContent
}
@Override
- protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent)
{
- return new SubscriptionImpl(consumer, emitInitialContent, failure);
+ return new SubscriptionImpl(consumer, emitInitialContent);
}
private class SubscriptionImpl extends AbstractSubscription
{
private int index;
- private SubscriptionImpl(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ private SubscriptionImpl(Consumer consumer, boolean emitInitialContent)
{
- super(consumer, emitInitialContent, failure);
+ super(consumer, emitInitialContent);
}
@Override
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamRequestContent.java
index 8a5e72994ce..586b3f07e4d 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamRequestContent.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamRequestContent.java
@@ -42,6 +42,7 @@ public class InputStreamRequestContent extends AbstractRequestContent
private final InputStream stream;
private final int bufferSize;
+ private Subscription subscription;
public InputStreamRequestContent(InputStream stream)
{
@@ -66,9 +67,11 @@ public class InputStreamRequestContent extends AbstractRequestContent
}
@Override
- protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent)
{
- return new SubscriptionImpl(consumer, emitInitialContent, failure);
+ if (subscription != null)
+ throw new IllegalStateException("Multiple subscriptions not supported on " + this);
+ return subscription = new SubscriptionImpl(consumer, emitInitialContent);
}
@Override
@@ -96,9 +99,9 @@ public class InputStreamRequestContent extends AbstractRequestContent
{
private boolean terminated;
- private SubscriptionImpl(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ private SubscriptionImpl(Consumer consumer, boolean emitInitialContent)
{
- super(consumer, emitInitialContent, failure);
+ super(consumer, emitInitialContent);
}
@Override
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartRequestContent.java
index 866e3f5c754..7acfdb20c89 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartRequestContent.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartRequestContent.java
@@ -27,7 +27,6 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
-import java.util.stream.IntStream;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpField;
@@ -86,7 +85,8 @@ public class MultiPartRequestContent extends AbstractRequestContent implements C
private final ByteBuffer onlyBoundary;
private final ByteBuffer lastBoundary;
private long length;
- private volatile boolean closed;
+ private boolean closed;
+ private Subscription subscription;
public MultiPartRequestContent()
{
@@ -114,11 +114,22 @@ public class MultiPartRequestContent extends AbstractRequestContent implements C
}
@Override
- protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent)
{
- if (closed)
- length = calculateLength();
- return new SubscriptionImpl(consumer, emitInitialContent, failure);
+ if (!closed)
+ throw new IllegalStateException("MultiPartRequestContent must be closed before sending the request");
+ if (subscription != null)
+ throw new IllegalStateException("Multiple subscriptions not supported on " + this);
+ length = calculateLength();
+ return subscription = new SubscriptionImpl(consumer, emitInitialContent);
+ }
+
+ @Override
+ public void fail(Throwable failure)
+ {
+ parts.stream()
+ .map(part -> part.content)
+ .forEach(content -> content.fail(failure));
}
/**
@@ -284,9 +295,9 @@ public class MultiPartRequestContent extends AbstractRequestContent implements C
private int index;
private Subscription subscription;
- private SubscriptionImpl(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ private SubscriptionImpl(Consumer consumer, boolean emitInitialContent)
{
- super(consumer, emitInitialContent, failure);
+ super(consumer, emitInitialContent);
}
@Override
@@ -371,10 +382,6 @@ public class MultiPartRequestContent extends AbstractRequestContent implements C
{
if (subscription != null)
subscription.fail(failure);
- IntStream.range(index, parts.size())
- .mapToObj(parts::get)
- .map(part -> part.content)
- .forEach(content -> content.fail(failure));
}
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathRequestContent.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathRequestContent.java
index 916ad2a2954..20737ab2dec 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathRequestContent.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/PathRequestContent.java
@@ -114,9 +114,9 @@ public class PathRequestContent extends AbstractRequestContent
}
@Override
- protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent)
{
- return new SubscriptionImpl(consumer, emitInitialContent, failure);
+ return new SubscriptionImpl(consumer, emitInitialContent);
}
private class SubscriptionImpl extends AbstractSubscription
@@ -124,9 +124,9 @@ public class PathRequestContent extends AbstractRequestContent
private ReadableByteChannel channel;
private long readTotal;
- private SubscriptionImpl(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ private SubscriptionImpl(Consumer consumer, boolean emitInitialContent)
{
- super(consumer, emitInitialContent, failure);
+ super(consumer, emitInitialContent);
}
@Override
@@ -166,5 +166,12 @@ public class PathRequestContent extends AbstractRequestContent
if (bufferPool != null)
bufferPool.release(buffer);
}
+
+ @Override
+ public void fail(Throwable failure)
+ {
+ super.fail(failure);
+ IO.close(channel);
+ }
}
}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java
index 7545b50e6cd..d054243ebbb 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientAuthenticationTest.java
@@ -819,18 +819,18 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
}
@Override
- protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent)
{
- return new SubscriptionImpl(consumer, emitInitialContent, failure);
+ return new SubscriptionImpl(consumer, emitInitialContent);
}
private class SubscriptionImpl extends AbstractSubscription
{
private int index;
- public SubscriptionImpl(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ public SubscriptionImpl(Consumer consumer, boolean emitInitialContent)
{
- super(consumer, emitInitialContent, failure);
+ super(consumer, emitInitialContent);
}
@Override
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
index b7409702436..fe2efa598db 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
@@ -631,9 +631,9 @@ public class HttpClientTest extends AbstractHttpClientServerTest
.body(new AbstractRequestContent("application/octet-stream")
{
@Override
- protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent, Throwable failure)
+ protected Subscription newSubscription(Consumer consumer, boolean emitInitialContent)
{
- return new AbstractSubscription(consumer, emitInitialContent, failure)
+ return new AbstractSubscription(consumer, emitInitialContent)
{
private int count;
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartContentTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartContentTest.java
index af3fd7611bb..947fce50d17 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartContentTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartContentTest.java
@@ -408,6 +408,8 @@ public class MultiPartContentTest extends AbstractHttpClientServerTest
multiPart.addFieldPart("field", fieldContent, null);
AsyncRequestContent fileContent = new AsyncRequestContent();
multiPart.addFilePart("file", "fileName", fileContent, null);
+ multiPart.close();
+
CountDownLatch responseLatch = new CountDownLatch(1);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
@@ -432,8 +434,6 @@ public class MultiPartContentTest extends AbstractHttpClientServerTest
fieldContent.offer(encoding.encode(value));
fieldContent.close();
- multiPart.close();
-
assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/util/RequestContentBehaviorTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/util/RequestContentBehaviorTest.java
index c4427e889d0..b7ee58d3df3 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/util/RequestContentBehaviorTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/util/RequestContentBehaviorTest.java
@@ -25,7 +25,6 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
-import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -39,7 +38,6 @@ import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.IO;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
@@ -48,6 +46,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
public class RequestContentBehaviorTest
{
@@ -171,6 +170,7 @@ public class RequestContentBehaviorTest
{
{
addFieldPart("field", new StringRequestContent("*".repeat(64)), null);
+ close();
}
},
new PathRequestContent(smallFile),
@@ -229,41 +229,6 @@ public class RequestContentBehaviorTest
assertTrue(latch.await(5, TimeUnit.SECONDS));
}
- @ParameterizedTest
- @MethodSource("smallContents")
- public void testSmallContentFailedBeforeSubscription(Request.Content content)
- {
- Throwable testFailure = new Throwable("test_failure");
- content.fail(testFailure);
-
- AtomicInteger notified = new AtomicInteger();
- AtomicReference failureRef = new AtomicReference<>();
- Request.Content.Subscription subscription = content.subscribe(new Request.Content.Consumer()
- {
- @Override
- public void onContent(ByteBuffer buffer, boolean last, Callback callback)
- {
- notified.getAndIncrement();
- }
-
- @Override
- public void onFailure(Throwable error)
- {
- testFailure.addSuppressed(new Throwable("suppressed"));
- failureRef.compareAndSet(null, error);
- }
- }, true);
-
- // Initial demand.
- subscription.demand();
-
- assertEquals(0, notified.get());
- Throwable failure = failureRef.get();
- assertNotNull(failure);
- assertSame(testFailure, failure);
- assertEquals(1, failure.getSuppressed().length);
- }
-
@ParameterizedTest
@MethodSource("smallContents")
public void testSmallContentFailedAfterFirstDemand(Request.Content content)
@@ -343,12 +308,11 @@ public class RequestContentBehaviorTest
assertEquals(1, failure.getSuppressed().length);
}
- @Test
- public void testSameContentMultipleSubscriptions() throws Exception
+ @ParameterizedTest
+ @MethodSource("smallContents")
+ public void testReproducibleContentCanHaveMultipleSubscriptions(Request.Content content) throws Exception
{
- byte[] bytes = new byte[64];
- new Random().nextBytes(bytes);
- Request.Content content = new BytesRequestContent(bytes);
+ assumeTrue(content.isReproducible());
CountDownLatch latch1 = new CountDownLatch(1);
Request.Content.Subscription subscription1 = content.subscribe((buffer, last, callback) ->
From 34b0726eb1993aaa1712d2b44be0f8d56940bf76 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Mon, 30 Mar 2020 12:20:23 +0200
Subject: [PATCH 011/101] A little bit of work on the new Jetty 10
documentation.
Signed-off-by: Simone Bordet
---
jetty-documentation/pom.xml | 1 -
.../embedded-guide/.asciidoctorconfig | 3 ++
.../client/client-concepts.adoc | 30 +++++++++----------
.../main/asciidoc/embedded-guide/io-arch.adoc | 25 +++++++++-------
...Snippets.java => SelectorManagerDocs.java} | 6 ++--
...Snippets.java => ClientConnectorDocs.java} | 6 ++--
6 files changed, 40 insertions(+), 31 deletions(-)
create mode 100644 jetty-documentation/src/main/asciidoc/embedded-guide/.asciidoctorconfig
rename jetty-documentation/src/main/java/embedded/{SelectorManagerDocSnippets.java => SelectorManagerDocs.java} (98%)
rename jetty-documentation/src/main/java/embedded/client/{ClientConnectorDocSnippets.java => ClientConnectorDocs.java} (97%)
diff --git a/jetty-documentation/pom.xml b/jetty-documentation/pom.xml
index 460b4b34f93..4661ac8c479 100644
--- a/jetty-documentation/pom.xml
+++ b/jetty-documentation/pom.xml
@@ -125,7 +125,6 @@
http://central.maven.org/maven2${project.version}${maven.build.timestamp}
- ${basedir}/src/main/java
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/.asciidoctorconfig b/jetty-documentation/src/main/asciidoc/embedded-guide/.asciidoctorconfig
new file mode 100644
index 00000000000..b1785c722ed
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/.asciidoctorconfig
@@ -0,0 +1,3 @@
+// Asciidoctor IDE configuration file.
+// See https://github.com/asciidoctor/asciidoctor-intellij-plugin/wiki/Support-project-specific-configurations
+:doc_code: ../../java
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/client-concepts.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/client-concepts.adoc
index bf7dae893b1..bf63bce5013 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/client-concepts.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/client-concepts.adoc
@@ -31,9 +31,9 @@ There are conceptually three layers that compose the Jetty client libraries, fro
more abstract to more concrete:
. The API layer, that exposes semantic APIs to applications so that they can write
-code such as "GET me the resource at this URI"
+code such as "GET me the resource at this URI".
. The protocol layer, where the API request is converted into the appropriate
-protocol bytes, for example encrypted HTTP/2
+protocol bytes, for example encrypted HTTP/2.
. The infrastructure layer, that handles the low level I/O and deals with network,
buffer, threads, etc.
@@ -50,26 +50,31 @@ link:{JDURL}/org/eclipse/jetty/io/ClientConnector.html[`ClientConnector`].
The `ClientConnector` primarily wraps the
link:{JDURL}/org/eclipse/jetty/io/SelectorManager.html[`SelectorManager`]
-and aggregates other four components: the thread pool (in form of an `Executor`),
-the `Scheduler`, the `ByteBufferPool` and the `SslContextFactory.Client`.
+and aggregates other four components:
+
+* a thread pool (in form of an `java.util.concurrent.Executor`)
+* a scheduler (in form of `org.eclipse.jetty.util.thread.Scheduler`)
+* a byte buffer pool (in form of `org.eclipse.jetty.io.ByteBufferPool`)
+* a TLS factory (in form of `org.eclipse.jetty.util.ssl.SslContextFactory.Client`)
The `ClientConnector` is where you want to set those components after you
have configured them.
If you don't explicitly set those components on the `ClientConnector`, then
appropriate defaults will be chosen when the `ClientConnector` starts.
-The simplest example that creates and starts a `ClientConnector`:
+The simplest example that creates and starts a `ClientConnector` is the
+following:
[source,java,indent=0]
----
-include::{docbits}/embedded/client/ClientConnectorDocSnippets.java[tags=simplest]
+include::../{doc_code}/embedded/client/ClientConnectorDocs.java[tags=simplest]
----
A more typical example:
[source,java,indent=0]
----
-include::{docbits}/embedded/client/ClientConnectorDocSnippets.java[tags=typical]
+include::../{doc_code}/embedded/client/ClientConnectorDocs.java[tags=typical]
----
A more advanced example that customizes the `ClientConnector` by overriding
@@ -77,12 +82,11 @@ factory methods:
[source,java,indent=0]
----
-include::{docbits}/embedded/client/ClientConnectorDocSnippets.java[tags=advanced]
+include::../{doc_code}/embedded/client/ClientConnectorDocs.java[tags=advanced]
----
Since `ClientConnector` is the component that handles the low-level network, it
-is also the component where you want to configure the parameters that control
-how it should handle the low-level network.
+is also the component where you want to configure the low-leven network configuration.
The most common parameters are:
@@ -122,9 +126,6 @@ Once the `ClientConnector` is configured and started, it can be used to connect
to the server via `ClientConnector.connect(SocketAddress, Map)`
which in turn will call `SocketChannel.connect(SocketAddress)`.
-
-// TODO: from down here, moved to io-arch.adoc
-
When establishing a TCP connection to a server, applications need to tell
`ClientConnector` how to create the `Connection` for that particular
TCP connection.
@@ -134,10 +135,9 @@ that must be passed in the context `Map` as follows:
[source,java,indent=0]
----
-include::{docbits}/embedded/client/ClientConnectorDocSnippets.java[tags=connect]
+include::../{doc_code}/embedded/client/ClientConnectorDocs.java[tags=connect]
----
-
TODO: expand on what is the API to use, what parameters the context Map must
have, and basically how we can write a generic network client with it.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc
index e0106e8059b..e083aa1d6ee 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc
@@ -40,26 +40,26 @@ NOTE: TODO: add image
to a server and by a network server when accepting connections from network
clients.
In both cases the `SocketChannel` instance is passed to `SelectorManager`
-(and to `ManagedSelector` and eventually to `java.nio.channels.Selector`)
-to be registered for use within Jetty.
+(which passes it to `ManagedSelector` and eventually to
+`java.nio.channels.Selector`) to be registered for use within Jetty.
-It is therefore possible for an application to create the `SocketChannel`
+It is possible for an application to create the `SocketChannel`
instances outside Jetty, even perform some initial network traffic also
outside Jetty (for example for authentication purposes), and then pass the
`SocketChannel` instance to `SelectorManager` for use within Jetty.
-This example shows how to connect to a server:
+This example shows how a client can connect to a server:
[source,java,indent=0]
----
-include::{docbits}/embedded/SelectorManagerDocSnippets.java[tags=connect]
+include::{doc_code}/embedded/SelectorManagerDocs.java[tags=connect]
----
-This example shows how to accept a client connection:
+This example shows how a server accepts a client connection:
[source,java,indent=0]
----
-include::{docbits}/embedded/SelectorManagerDocSnippets.java[tags=accept]
+include::{doc_code}/embedded/SelectorManagerDocs.java[tags=accept]
----
[[io-arch-endpoint-connection]]
@@ -193,7 +193,7 @@ extends `AbstractConnection`:
[source,java,indent=0]
----
-include::{docbits}/embedded/SelectorManagerDocSnippets.java[tags=connection]
+include::{doc_code}/embedded/SelectorManagerDocs.java[tags=connection]
----
[[io-arch-echo]]
@@ -207,7 +207,7 @@ A naive, but wrong, implementation may be the following:
[source,java,indent=0]
----
-include::{docbits}/embedded/SelectorManagerDocSnippets.java[tags=echo-wrong]
+include::{doc_code}/embedded/SelectorManagerDocs.java[tags=echo-wrong]
----
WARNING: The implementation above is wrong and leads to `StackOverflowError`.
@@ -231,11 +231,16 @@ which leads to `StackOverflowError`.
This is a typical side effect of asynchronous programming using non-blocking
APIs, and happens in the Jetty I/O library as well.
+NOTE: The callback is invoked synchronously for efficiency reasons.
+Submitting the invocation of the callback to an `Executor` to be invoked in
+a different thread would cause a context switch and make simple writes
+extremely inefficient.
+
A correct implementation is the following:
[source,java,indent=0]
----
-include::{docbits}/embedded/SelectorManagerDocSnippets.java[tags=echo-correct]
+include::{doc_code}/embedded/SelectorManagerDocs.java[tags=echo-correct]
----
The correct implementation performs consecutive reads in a loop (rather than
diff --git a/jetty-documentation/src/main/java/embedded/SelectorManagerDocSnippets.java b/jetty-documentation/src/main/java/embedded/SelectorManagerDocs.java
similarity index 98%
rename from jetty-documentation/src/main/java/embedded/SelectorManagerDocSnippets.java
rename to jetty-documentation/src/main/java/embedded/SelectorManagerDocs.java
index 3844c4f9dab..8350b6c120b 100644
--- a/jetty-documentation/src/main/java/embedded/SelectorManagerDocSnippets.java
+++ b/jetty-documentation/src/main/java/embedded/SelectorManagerDocs.java
@@ -34,7 +34,7 @@ import org.eclipse.jetty.io.SelectorManager;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
-public class SelectorManagerDocSnippets
+public class SelectorManagerDocs
{
// tag::connect[]
public void connect(SelectorManager selectorManager, Map context) throws IOException
@@ -101,9 +101,9 @@ public class SelectorManagerDocSnippets
// tag::echo-wrong[]
class WrongEchoConnection extends AbstractConnection implements Callback
{
- public WrongEchoConnection(EndPoint endp, Executor executor)
+ public WrongEchoConnection(EndPoint endPoint, Executor executor)
{
- super(endp, executor);
+ super(endPoint, executor);
}
@Override
diff --git a/jetty-documentation/src/main/java/embedded/client/ClientConnectorDocSnippets.java b/jetty-documentation/src/main/java/embedded/client/ClientConnectorDocs.java
similarity index 97%
rename from jetty-documentation/src/main/java/embedded/client/ClientConnectorDocSnippets.java
rename to jetty-documentation/src/main/java/embedded/client/ClientConnectorDocs.java
index 41f92da336f..840bb63a0b2 100644
--- a/jetty-documentation/src/main/java/embedded/client/ClientConnectorDocSnippets.java
+++ b/jetty-documentation/src/main/java/embedded/client/ClientConnectorDocs.java
@@ -36,7 +36,7 @@ import org.eclipse.jetty.util.thread.Scheduler;
import static java.lang.System.Logger.Level.INFO;
-public class ClientConnectorDocSnippets
+public class ClientConnectorDocs
{
public void simplest() throws Exception
{
@@ -107,6 +107,7 @@ public class ClientConnectorDocSnippets
public void connect() throws Exception
{
+ // tag::connect[]
class CustomHTTPConnection extends AbstractConnection
{
public CustomHTTPConnection(EndPoint endPoint, Executor executor)
@@ -141,10 +142,11 @@ public class ClientConnectorDocSnippets
Map context = new HashMap<>();
context.put(ClientConnector.CLIENT_CONNECTION_FACTORY_CONTEXT_KEY, connectionFactory);
clientConnector.connect(address, context);
+ // end::connect[]
}
public static void main(String[] args) throws Exception
{
- new ClientConnectorDocSnippets().connect();
+ new ClientConnectorDocs().connect();
}
}
From 97eb8ec8a4cfeb36b8b3a7465604d993e214ae89 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Mon, 30 Mar 2020 12:26:57 +0200
Subject: [PATCH 012/101] Fixed generation of documentation via Maven Plugin.
Now including .asciidoctorconfig so that generation and IDEs render the
documentation correctly.
Signed-off-by: Simone Bordet
---
jetty-documentation/src/main/asciidoc/embedded-guide/index.adoc | 1 +
1 file changed, 1 insertion(+)
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/index.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/index.adoc
index 1dfc4bdaa93..5988c442888 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/index.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/index.adoc
@@ -58,6 +58,7 @@ endif::[]
// suppress automatic id generation
:sectids!:
+include::.asciidoctorconfig[]
include::client/client.adoc[]
include::server/server.adoc[]
include::io-arch.adoc[]
From e224be650b0a31a248dac3902ec31bce9e1862fa Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Mon, 30 Mar 2020 12:34:21 +0200
Subject: [PATCH 013/101] Issue #4400 - Review HttpClient's ContentProvider.
Review updates.
Closing MultiPartRequestContent before sending it.
Signed-off-by: Simone Bordet
---
.../jetty/osgi/test/TestJettyOSGiBootWithAnnotations.java | 2 +-
.../test/java/org/eclipse/jetty/webapp/HugeResourceTest.java | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithAnnotations.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithAnnotations.java
index dbd722b628c..00b4d6d016a 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithAnnotations.java
+++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithAnnotations.java
@@ -47,7 +47,6 @@ import static org.ops4j.pax.exam.CoreOptions.systemProperty;
* top of this.
*/
@RunWith(PaxExam.class)
-
public class TestJettyOSGiBootWithAnnotations
{
private static final String LOG_LEVEL = "WARN";
@@ -135,6 +134,7 @@ public class TestJettyOSGiBootWithAnnotations
TestOSGiUtil.assertContains("Response contents", content, "
FRAGMENT
");
MultiPartRequestContent multiPart = new MultiPartRequestContent();
multiPart.addFieldPart("field", new StringRequestContent("foo"), null);
+ multiPart.close();
response = client.newRequest("http://127.0.0.1:" + port + "/multi").method("POST")
.body(multiPart).send();
assertEquals(HttpStatus.OK_200, response.getStatus());
diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/HugeResourceTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/HugeResourceTest.java
index 8d4f46a57ea..8bd2d5c1916 100644
--- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/HugeResourceTest.java
+++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/HugeResourceTest.java
@@ -267,6 +267,7 @@ public class HugeResourceTest
Path inputFile = staticBase.resolve(filename);
String name = String.format("file-%d", expectedSize);
multipart.addFilePart(name, filename, new PathRequestContent(inputFile), null);
+ multipart.close();
URI destUri = server.getURI().resolve("/multipart");
client.setIdleTimeout(90_000);
From cb47b06f1449bb3101d6c2b7616a0ad51045d6e1 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Mon, 30 Mar 2020 12:49:13 +0200
Subject: [PATCH 014/101] Fixed copyright header.
Signed-off-by: Simone Bordet
---
.../java/embedded/SelectorManagerDocs.java | 24 +++++++++----------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/jetty-documentation/src/main/java/embedded/SelectorManagerDocs.java b/jetty-documentation/src/main/java/embedded/SelectorManagerDocs.java
index 8350b6c120b..243f6b66ecb 100644
--- a/jetty-documentation/src/main/java/embedded/SelectorManagerDocs.java
+++ b/jetty-documentation/src/main/java/embedded/SelectorManagerDocs.java
@@ -1,19 +1,19 @@
//
-// ========================================================================
-// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
-// ------------------------------------------------------------------------
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
-// The Eclipse Public License is available at
-// http://www.eclipse.org/legal/epl-v10.html
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
//
-// The Apache License v2.0 is available at
-// http://www.opensource.org/licenses/apache2.0.php
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
//
-// You may elect to redistribute this code under either of these licenses.
-// ========================================================================
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
//
package embedded;
From e3d670d61d2ef0f83cfdb832833cd05b64d7de8f Mon Sep 17 00:00:00 2001
From: Greg Wilkins
Date: Mon, 30 Mar 2020 16:23:26 +0200
Subject: [PATCH 015/101] Issue #4713 Async dispatch with query. (#4721)
* Issue #4713 Async dispatch with query.
+ Preserve the entire URI with query when startAsync(req,res) is used.
+ merge any query string from dispatch path with either original query or preserved query from forward
Signed-off-by: Greg Wilkins
* Issue #4713 asyncDispatch with query parameters
Signed-off-by: Greg Wilkins
---
.../java/org/eclipse/jetty/http/HttpURI.java | 6 +-
.../jetty/server/AsyncContextEvent.java | 29 +++--
.../org/eclipse/jetty/server/Request.java | 20 ++-
.../java/org/eclipse/jetty/server/Server.java | 76 ++++++++++--
.../jetty/servlet/AsyncServletTest.java | 116 +++++++++---------
.../java/org/eclipse/jetty/util/URIUtil.java | 14 +++
6 files changed, 170 insertions(+), 91 deletions(-)
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
index 2bde2c1deaa..2c112940f8b 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
@@ -760,15 +760,15 @@ public class HttpURI
_decodedPath = path;
}
- public void setPathQuery(String path)
+ public void setPathQuery(String pathQuery)
{
_uri = null;
_path = null;
_decodedPath = null;
_param = null;
_fragment = null;
- if (path != null)
- parse(State.PATH, path);
+ if (pathQuery != null)
+ parse(State.PATH, pathQuery);
}
public void setQuery(String query)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java
index 4da7cc87e67..c4bc0095aee 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java
@@ -25,6 +25,7 @@ import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
+import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.util.thread.Scheduler;
@@ -32,6 +33,7 @@ public class AsyncContextEvent extends AsyncEvent implements Runnable
{
private final Context _context;
private final AsyncContextState _asyncContext;
+ private final HttpURI _baseURI;
private volatile HttpChannelState _state;
private ServletContext _dispatchContext;
private String _dispatchPath;
@@ -39,11 +41,17 @@ public class AsyncContextEvent extends AsyncEvent implements Runnable
private Throwable _throwable;
public AsyncContextEvent(Context context, AsyncContextState asyncContext, HttpChannelState state, Request baseRequest, ServletRequest request, ServletResponse response)
+ {
+ this (context, asyncContext, state, baseRequest, request, response, null);
+ }
+
+ public AsyncContextEvent(Context context, AsyncContextState asyncContext, HttpChannelState state, Request baseRequest, ServletRequest request, ServletResponse response, HttpURI baseURI)
{
super(null, request, response, null);
_context = context;
_asyncContext = asyncContext;
_state = state;
+ _baseURI = baseURI;
// If we haven't been async dispatched before
if (baseRequest.getAttribute(AsyncContext.ASYNC_REQUEST_URI) == null)
@@ -74,6 +82,11 @@ public class AsyncContextEvent extends AsyncEvent implements Runnable
}
}
+ public HttpURI getBaseURI()
+ {
+ return _baseURI;
+ }
+
public ServletContext getSuspendedContext()
{
return _context;
@@ -94,14 +107,6 @@ public class AsyncContextEvent extends AsyncEvent implements Runnable
return _dispatchContext == null ? _context : _dispatchContext;
}
- /**
- * @return The path in the context (encoded with possible query string)
- */
- public String getPath()
- {
- return _dispatchPath;
- }
-
public void setTimeoutTask(Scheduler.Task task)
{
_timeoutTask = task;
@@ -137,6 +142,14 @@ public class AsyncContextEvent extends AsyncEvent implements Runnable
_dispatchContext = context;
}
+ /**
+ * @return The path in the context (encoded with possible query string)
+ */
+ public String getDispatchPath()
+ {
+ return _dispatchPath;
+ }
+
/**
* @param path encoded URI
*/
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
index 6abada24f58..07f2d318be4 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
@@ -1598,7 +1598,10 @@ public class Request implements HttpServletRequest
{
MetaData.Request metadata = _metaData;
if (metadata != null)
+ {
metadata.setURI(uri);
+ _queryParameters = null;
+ }
}
public UserIdentity getUserIdentity()
@@ -2178,17 +2181,8 @@ public class Request implements HttpServletRequest
HttpChannelState state = getHttpChannelState();
if (_async == null)
_async = new AsyncContextState(state);
- AsyncContextEvent event = new AsyncContextEvent(_context, _async, state, this, servletRequest, servletResponse);
+ AsyncContextEvent event = new AsyncContextEvent(_context, _async, state, this, servletRequest, servletResponse, getHttpURI());
event.setDispatchContext(getServletContext());
-
- String uri = unwrap(servletRequest).getRequestURI();
- if (_contextPath != null && uri.startsWith(_contextPath))
- uri = uri.substring(_contextPath.length());
- else
- // TODO probably need to strip encoded context from requestURI, but will do this for now:
- uri = URIUtil.encodePath(URIUtil.addPaths(getServletPath(), getPathInfo()));
-
- event.setDispatchPath(uri);
state.startAsync(event);
return _async;
}
@@ -2391,7 +2385,7 @@ public class Request implements HttpServletRequest
setQueryString(oldQuery);
else if (oldQuery == null)
setQueryString(newQuery);
- else
+ else if (oldQueryParams.keySet().stream().anyMatch(newQueryParams.keySet()::contains))
{
// Build the new merged query string, parameters in the
// new query string hide parameters in the old query string.
@@ -2413,6 +2407,10 @@ public class Request implements HttpServletRequest
}
setQueryString(mergedQuery.toString());
}
+ else
+ {
+ setQueryString(newQuery + '&' + oldQuery);
+ }
}
}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
index d9ade6e27d4..1566d6ba2f7 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
@@ -47,6 +47,8 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.MultiException;
+import org.eclipse.jetty.util.MultiMap;
+import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.Uptime;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
@@ -563,22 +565,74 @@ public class Server extends HandlerWrapper implements Attributes
{
final HttpChannelState state = channel.getRequest().getHttpChannelState();
final AsyncContextEvent event = state.getAsyncContextEvent();
-
final Request baseRequest = channel.getRequest();
- final String path = event.getPath();
+ final HttpURI baseUri = event.getBaseURI();
+ String encodedPathQuery = event.getDispatchPath();
- if (path != null)
+ if (encodedPathQuery == null && baseUri == null)
{
- // this is a dispatch with a path
- ServletContext context = event.getServletContext();
- String query = baseRequest.getQueryString();
- baseRequest.setURIPathQuery(URIUtil.addEncodedPaths(context == null ? null : URIUtil.encodePath(context.getContextPath()), path));
- HttpURI uri = baseRequest.getHttpURI();
- baseRequest.setPathInfo(uri.getDecodedPath());
- if (uri.getQuery() != null)
- baseRequest.mergeQueryParameters(query, uri.getQuery(), true); //we have to assume dispatch path and query are UTF8
+ // Simple case, no request modification or merging needed
+ handleAsync(channel, event, baseRequest);
+ return;
}
+ // this is a dispatch with either a provided URI and/or a dispatched path
+ // We will have to modify the request and then revert
+ final ServletContext context = event.getServletContext();
+ final HttpURI oldUri = baseRequest.getHttpURI();
+ final String oldQuery = baseRequest.getQueryString();
+ final MultiMap oldQueryParams = baseRequest.getQueryParameters();
+ try
+ {
+ baseRequest.resetParameters();
+ HttpURI newUri = baseUri == null ? new HttpURI(oldUri) : baseUri;
+ if (encodedPathQuery == null)
+ {
+ baseRequest.setHttpURI(newUri);
+ }
+ else
+ {
+ if (context != null && !StringUtil.isEmpty(context.getContextPath()))
+ encodedPathQuery = URIUtil.addEncodedPaths(URIUtil.encodePath(context.getContextPath()), encodedPathQuery);
+
+ if (newUri.getQuery() == null)
+ {
+ // parse new path and query
+ newUri.setPathQuery(encodedPathQuery);
+ baseRequest.setHttpURI(newUri);
+ }
+ else
+ {
+ // do we have a new query in the encodedPathQuery
+ int q = encodedPathQuery.indexOf('?');
+ if (q < 0)
+ {
+ // No query, so we can just set the encoded path
+ newUri.setPath(encodedPathQuery);
+ baseRequest.setHttpURI(newUri);
+ }
+ else
+ {
+ newUri.setPath(encodedPathQuery.substring(0, q));
+ baseRequest.setHttpURI(newUri);
+ baseRequest.mergeQueryParameters(oldQuery, encodedPathQuery.substring(q + 1), true);
+ }
+ }
+ }
+
+ baseRequest.setPathInfo(newUri.getDecodedPath());
+ handleAsync(channel, event, baseRequest);
+ }
+ finally
+ {
+ baseRequest.setHttpURI(oldUri);
+ baseRequest.setQueryParameters(oldQueryParams);
+ baseRequest.resetParameters();
+ }
+ }
+
+ private void handleAsync(HttpChannel channel, AsyncContextEvent event, Request baseRequest) throws IOException, ServletException
+ {
final String target = baseRequest.getPathInfo();
final HttpServletRequest request = Request.unwrap(event.getSuppliedRequest());
final HttpServletResponse response = Response.unwrap(event.getSuppliedResponse());
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java
index ac16a759a6a..f04cffee271 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java
@@ -163,7 +163,7 @@ public class AsyncServletTest
String response = process("sleep=200", null);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?sleep=200",
"initial"));
assertContains("SLEPT", response);
assertFalse(__history.contains("onTimeout"));
@@ -173,7 +173,7 @@ public class AsyncServletTest
@Test
public void testNonAsync() throws Exception
{
- String response = process("", null);
+ String response = process(null, null);
assertThat(response, Matchers.startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
"REQUEST /ctx/path/info",
@@ -186,7 +186,7 @@ public class AsyncServletTest
public void testAsyncNotSupportedNoAsync() throws Exception
{
_expectedCode = "200 ";
- String response = process("noasync", "", null);
+ String response = process("noasync", null, null);
assertThat(response, Matchers.startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
"REQUEST /ctx/noasync/info",
@@ -205,9 +205,9 @@ public class AsyncServletTest
String response = process("noasync", "start=200", null);
assertThat(response, Matchers.startsWith("HTTP/1.1 500 "));
assertThat(__history, contains(
- "REQUEST /ctx/noasync/info",
+ "REQUEST /ctx/noasync/info?start=200",
"initial",
- "ERROR /ctx/error/custom",
+ "ERROR /ctx/error/custom?start=200",
"!initial"
));
@@ -224,11 +224,11 @@ public class AsyncServletTest
String response = process("start=200", null);
assertThat(response, Matchers.startsWith("HTTP/1.1 500 Server Error"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?start=200",
"initial",
"start",
"onTimeout",
- "ERROR /ctx/error/custom",
+ "ERROR /ctx/error/custom?start=200",
"!initial",
"onComplete"));
@@ -241,12 +241,12 @@ public class AsyncServletTest
String response = process("start=200&timeout=dispatch", null);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?start=200&timeout=dispatch",
"initial",
"start",
"onTimeout",
"dispatch",
- "ASYNC /ctx/path/info",
+ "ASYNC /ctx/path/info?start=200&timeout=dispatch",
"!initial",
"onComplete"));
@@ -260,12 +260,12 @@ public class AsyncServletTest
String response = process("start=200&timeout=error", null);
assertThat(response, startsWith("HTTP/1.1 500 Server Error"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?start=200&timeout=error",
"initial",
"start",
"onTimeout",
"error",
- "ERROR /ctx/error/custom",
+ "ERROR /ctx/error/custom?start=200&timeout=error",
"!initial",
"onComplete"));
@@ -278,7 +278,7 @@ public class AsyncServletTest
String response = process("start=200&timeout=complete", null);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?start=200&timeout=complete",
"initial",
"start",
"onTimeout",
@@ -294,11 +294,11 @@ public class AsyncServletTest
String response = process("start=200&dispatch=10", null);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?start=200&dispatch=10",
"initial",
"start",
"dispatch",
- "ASYNC /ctx/path/info",
+ "ASYNC /ctx/path/info?start=200&dispatch=10",
"!initial",
"onComplete"));
assertFalse(__history.contains("onTimeout"));
@@ -310,11 +310,11 @@ public class AsyncServletTest
String response = process("start=200&dispatch=0", null);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?start=200&dispatch=0",
"initial",
"start",
"dispatch",
- "ASYNC /ctx/path/info",
+ "ASYNC /ctx/path/info?start=200&dispatch=0",
"!initial",
"onComplete"));
}
@@ -326,11 +326,11 @@ public class AsyncServletTest
String response = process("start=200&throw=1", null);
assertThat(response, startsWith("HTTP/1.1 500 Server Error"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?start=200&throw=1",
"initial",
"start",
"onError",
- "ERROR /ctx/error/custom",
+ "ERROR /ctx/error/custom?start=200&throw=1",
"!initial",
"onComplete"));
assertContains("ERROR DISPATCH: /ctx/error/custom", response);
@@ -342,7 +342,7 @@ public class AsyncServletTest
String response = process("start=200&complete=50", null);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?start=200&complete=50",
"initial",
"start",
"complete",
@@ -358,7 +358,7 @@ public class AsyncServletTest
String response = process("start=200&complete=0", null);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?start=200&complete=0",
"initial",
"start",
"complete",
@@ -374,16 +374,16 @@ public class AsyncServletTest
String response = process("start=1000&dispatch=10&start2=1000&dispatch2=10", null);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?start=1000&dispatch=10&start2=1000&dispatch2=10",
"initial",
"start",
"dispatch",
- "ASYNC /ctx/path/info",
+ "ASYNC /ctx/path/info?start=1000&dispatch=10&start2=1000&dispatch2=10",
"!initial",
"onStartAsync",
"start",
"dispatch",
- "ASYNC /ctx/path/info",
+ "ASYNC /ctx/path/info?start=1000&dispatch=10&start2=1000&dispatch2=10",
"!initial",
"onComplete"));
assertContains("DISPATCHED", response);
@@ -395,11 +395,11 @@ public class AsyncServletTest
String response = process("start=1000&dispatch=10&start2=1000&complete2=10", null);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?start=1000&dispatch=10&start2=1000&complete2=10",
"initial",
"start",
"dispatch",
- "ASYNC /ctx/path/info",
+ "ASYNC /ctx/path/info?start=1000&dispatch=10&start2=1000&complete2=10",
"!initial",
"onStartAsync",
"start",
@@ -415,16 +415,16 @@ public class AsyncServletTest
String response = process("start=1000&dispatch=10&start2=10", null);
assertThat(response, startsWith("HTTP/1.1 500 Server Error"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?start=1000&dispatch=10&start2=10",
"initial",
"start",
"dispatch",
- "ASYNC /ctx/path/info",
+ "ASYNC /ctx/path/info?start=1000&dispatch=10&start2=10",
"!initial",
"onStartAsync",
"start",
"onTimeout",
- "ERROR /ctx/error/custom",
+ "ERROR /ctx/error/custom?start=1000&dispatch=10&start2=10",
"!initial",
"onComplete"));
assertContains("ERROR DISPATCH: /ctx/error/custom", response);
@@ -436,16 +436,16 @@ public class AsyncServletTest
String response = process("start=10&start2=1000&dispatch2=10", null);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?start=10&start2=1000&dispatch2=10",
"initial",
"start",
"onTimeout",
- "ERROR /ctx/error/custom",
+ "ERROR /ctx/error/custom?start=10&start2=1000&dispatch2=10",
"!initial",
"onStartAsync",
"start",
"dispatch",
- "ASYNC /ctx/path/info",
+ "ASYNC /ctx/path/info?start=10&start2=1000&dispatch2=10",
"!initial",
"onComplete"));
assertContains("DISPATCHED", response);
@@ -457,11 +457,11 @@ public class AsyncServletTest
String response = process("start=10&start2=1000&complete2=10", null);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?start=10&start2=1000&complete2=10",
"initial",
"start",
"onTimeout",
- "ERROR /ctx/error/custom",
+ "ERROR /ctx/error/custom?start=10&start2=1000&complete2=10",
"!initial",
"onStartAsync",
"start",
@@ -478,16 +478,16 @@ public class AsyncServletTest
String response = process("start=10&start2=10", null);
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?start=10&start2=10",
"initial",
"start",
"onTimeout",
- "ERROR /ctx/path/error",
+ "ERROR /ctx/path/error?start=10&start2=10",
"!initial",
"onStartAsync",
"start",
"onTimeout",
- "ERROR /ctx/path/error",
+ "ERROR /ctx/path/error?start=10&start2=10",
"!initial",
"onComplete")); // Error Page Loop!
assertContains("AsyncContext timeout", response);
@@ -499,11 +499,11 @@ public class AsyncServletTest
String response = process("wrap=true&start=200&dispatch=20", null);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?wrap=true&start=200&dispatch=20",
"initial",
"start",
"dispatch",
- "ASYNC /ctx/path/info",
+ "ASYNC /ctx/path/info?wrap=true&start=200&dispatch=20",
"wrapped REQ RSP",
"!initial",
"onComplete"));
@@ -516,11 +516,11 @@ public class AsyncServletTest
String response = process("start=200&dispatch=20&path=/p%20th3", null);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?start=200&dispatch=20&path=/p%20th3",
"initial",
"start",
"dispatch",
- "ASYNC /ctx/p%20th3",
+ "ASYNC /ctx/p%20th3?start=200&dispatch=20&path=/p%20th3",
"!initial",
"onComplete"));
assertContains("DISPATCHED", response);
@@ -532,13 +532,13 @@ public class AsyncServletTest
String response = process("fwd", "start=200&dispatch=20", null);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "FWD REQUEST /ctx/fwd/info",
- "FORWARD /ctx/path1",
+ "FWD REQUEST /ctx/fwd/info?start=200&dispatch=20",
+ "FORWARD /ctx/path1?forward=true&start=200&dispatch=20",
"initial",
"start",
"dispatch",
- "FWD ASYNC /ctx/fwd/info",
- "FORWARD /ctx/path1",
+ "FWD ASYNC /ctx/fwd/info?start=200&dispatch=20",
+ "FORWARD /ctx/path1?forward=true&start=200&dispatch=20",
"!initial",
"onComplete"));
assertContains("DISPATCHED", response);
@@ -550,12 +550,12 @@ public class AsyncServletTest
String response = process("fwd", "start=200&dispatch=20&path=/path2", null);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "FWD REQUEST /ctx/fwd/info",
- "FORWARD /ctx/path1",
+ "FWD REQUEST /ctx/fwd/info?start=200&dispatch=20&path=/path2",
+ "FORWARD /ctx/path1?forward=true&start=200&dispatch=20&path=/path2",
"initial",
"start",
"dispatch",
- "ASYNC /ctx/path2",
+ "ASYNC /ctx/path2?start=200&dispatch=20&path=/path2",
"!initial",
"onComplete"));
assertContains("DISPATCHED", response);
@@ -567,12 +567,12 @@ public class AsyncServletTest
String response = process("fwd", "wrap=true&start=200&dispatch=20", null);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "FWD REQUEST /ctx/fwd/info",
- "FORWARD /ctx/path1",
+ "FWD REQUEST /ctx/fwd/info?wrap=true&start=200&dispatch=20",
+ "FORWARD /ctx/path1?forward=true&wrap=true&start=200&dispatch=20",
"initial",
"start",
"dispatch",
- "ASYNC /ctx/path1",
+ "ASYNC /ctx/path1?forward=true&wrap=true&start=200&dispatch=20",
"wrapped REQ RSP",
"!initial",
"onComplete"));
@@ -585,12 +585,12 @@ public class AsyncServletTest
String response = process("fwd", "wrap=true&start=200&dispatch=20&path=/path2", null);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "FWD REQUEST /ctx/fwd/info",
- "FORWARD /ctx/path1",
+ "FWD REQUEST /ctx/fwd/info?wrap=true&start=200&dispatch=20&path=/path2",
+ "FORWARD /ctx/path1?forward=true&wrap=true&start=200&dispatch=20&path=/path2",
"initial",
"start",
"dispatch",
- "ASYNC /ctx/path2",
+ "ASYNC /ctx/path2?forward=true&wrap=true&start=200&dispatch=20&path=/path2",
"wrapped REQ RSP",
"!initial",
"onComplete"));
@@ -619,12 +619,12 @@ public class AsyncServletTest
__latch.await(1, TimeUnit.SECONDS);
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(__history, contains(
- "REQUEST /ctx/path/info",
+ "REQUEST /ctx/path/info?start=2000&dispatch=1500",
"initial",
"start",
"async-read=10",
"dispatch",
- "ASYNC /ctx/path/info",
+ "ASYNC /ctx/path/info?start=2000&dispatch=1500",
"!initial",
"onComplete"));
}
@@ -685,10 +685,10 @@ public class AsyncServletTest
@Override
public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
{
- historyAdd("FWD " + request.getDispatcherType() + " " + request.getRequestURI());
+ historyAdd("FWD " + request.getDispatcherType() + " " + URIUtil.addPathQuery(request.getRequestURI(), request.getQueryString()));
if (request instanceof ServletRequestWrapper || response instanceof ServletResponseWrapper)
historyAdd("wrapped" + ((request instanceof ServletRequestWrapper) ? " REQ" : "") + ((response instanceof ServletResponseWrapper) ? " RSP" : ""));
- request.getServletContext().getRequestDispatcher("/path1").forward(request, response);
+ request.getServletContext().getRequestDispatcher("/path1?forward=true").forward(request, response);
}
}
@@ -711,7 +711,7 @@ public class AsyncServletTest
// ignored
}
- historyAdd(request.getDispatcherType() + " " + request.getRequestURI());
+ historyAdd(request.getDispatcherType() + " " + URIUtil.addPathQuery(request.getRequestURI(),request.getQueryString()));
if (request instanceof ServletRequestWrapper || response instanceof ServletResponseWrapper)
historyAdd("wrapped" + ((request instanceof ServletRequestWrapper) ? " REQ" : "") + ((response instanceof ServletResponseWrapper) ? " RSP" : ""));
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java
index 1909e102242..6ae6f609cd9 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java
@@ -714,6 +714,20 @@ public class URIUtil
return buf.toString();
}
+ /** Add a path and a query string
+ * @param path The path which may already contain contain a query
+ * @param query The query string or null if no query to be added
+ * @return The path with any non null query added after a '?' or '&' as appropriate.
+ */
+ public static String addPathQuery(String path, String query)
+ {
+ if (query == null)
+ return path;
+ if (path.indexOf('?') >= 0)
+ return path + '&' + query;
+ return path + '?' + query;
+ }
+
/**
* Given a URI, attempt to get the last segment.
*
From e913e7970f58e75133bb2e1f983b3531a9587292 Mon Sep 17 00:00:00 2001
From: Greg Wilkins
Date: Mon, 30 Mar 2020 16:38:42 +0200
Subject: [PATCH 016/101] Fixes #4542 Root pathspec mapping pathInfo (#4705)
* Fixes #4542 Root pathspec mapping pathInfo
For the "" root pathspec, the pathinfo should always be the full path and the matched path is ""
Signed-off-by: Greg Wilkins
* updates from review
Signed-off-by: Greg Wilkins
---
.../jetty/http/pathmap/PathSpecGroup.java | 2 +-
.../jetty/http/pathmap/ServletPathSpec.java | 48 ++++++++-----------
.../jetty/http/pathmap/PathMappingsTest.java | 4 +-
.../http/pathmap/ServletPathSpecTest.java | 6 ++-
4 files changed, 29 insertions(+), 31 deletions(-)
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathSpecGroup.java b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathSpecGroup.java
index 0d1a165b96d..54c37dcd72c 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathSpecGroup.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathSpecGroup.java
@@ -39,7 +39,7 @@ public enum PathSpecGroup
*
*
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/client-concepts.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/client-io-arch.adoc
similarity index 54%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/client/client-concepts.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/client/client-io-arch.adoc
index bf63bce5013..f2afffd946e 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/client-concepts.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/client-io-arch.adoc
@@ -16,32 +16,25 @@
// ========================================================================
//
-[[client-concepts]]
-=== Client Libraries Concepts
+[[client-io-arch]]
+=== Client Libraries Architecture
-The Jetty client libraries implement a network client speaking different protocols
-such as HTTP/1.1, HTTP/2, WebSocket and FastCGI.
+The Jetty client libraries provide the basic components and APIs to implement
+a network client.
-It is possible to implement your own custom protocol on top of the Jetty client
-libraries.
+They build on the common link:#io-arch[Jetty I/O Architecture] and provide client
+specific concepts (such as establishing a connection to a server).
-NOTE: TODO: perhaps add a section about this.
+There are conceptually two layers that compose the Jetty client libraries:
-There are conceptually three layers that compose the Jetty client libraries, from
-more abstract to more concrete:
+. link:#client-io-arch-network[The network layer], that handles the low level
+I/O and deals with buffers, threads, etc.
+. link:#client-io-arch-protocol[The protocol layer], that handles the parsing
+of bytes read from the network and the generation of bytes to write to the
+network.
-. The API layer, that exposes semantic APIs to applications so that they can write
-code such as "GET me the resource at this URI".
-. The protocol layer, where the API request is converted into the appropriate
-protocol bytes, for example encrypted HTTP/2.
-. The infrastructure layer, that handles the low level I/O and deals with network,
-buffer, threads, etc.
-
-Let's look at these layers starting from the more concrete (and low level) one
-and build up to the more abstract layer.
-
-[[client-concepts-infrastructure]]
-==== Client Libraries Infrastructure Layer
+[[client-io-arch-network]]
+==== Client Libraries Network Layer
The Jetty client libraries use the common I/O design described in
link:#io-arch[this section].
@@ -78,7 +71,7 @@ include::../{doc_code}/embedded/client/ClientConnectorDocs.java[tags=typical]
----
A more advanced example that customizes the `ClientConnector` by overriding
-factory methods:
+some of its methods:
[source,java,indent=0]
----
@@ -86,7 +79,7 @@ include::../{doc_code}/embedded/client/ClientConnectorDocs.java[tags=advanced]
----
Since `ClientConnector` is the component that handles the low-level network, it
-is also the component where you want to configure the low-leven network configuration.
+is also the component where you want to configure the low-level network configuration.
The most common parameters are:
@@ -122,28 +115,82 @@ Please refer to the `ClientConnector`
link:{JDURL}/org/eclipse/jetty/io/ClientConnector.html[javadocs]
for the complete list of configurable parameters.
-Once the `ClientConnector` is configured and started, it can be used to connect
-to the server via `ClientConnector.connect(SocketAddress, Map)`
-which in turn will call `SocketChannel.connect(SocketAddress)`.
+[[client-io-arch-protocol]]
+==== Client Libraries Protocol Layer
-When establishing a TCP connection to a server, applications need to tell
+The protocol layer builds on top of the network layer to generate the
+bytes to be written to the network and to parse the bytes read from the
+network.
+
+Recall from link:#io-arch-connection[this section] that Jetty uses the
+`Connection` abstraction to produce and interpret the network bytes.
+
+On the client side, a `ClientConnectionFactory` implementation is the
+component that creates `Connection` instances based on the protocol that
+the client wants to "speak" with the server.
+
+Applications use `ClientConnector.connect(SocketAddress, Map)`
+to establish a TCP connection to the server, and must tell
`ClientConnector` how to create the `Connection` for that particular
-TCP connection.
-This is done via a
-link:{JDURL}/org/eclipse/jetty/io/ClientConnectionFactory.html[`ClientConnectionFactory`].
-that must be passed in the context `Map` as follows:
+TCP connection, and how to notify back the application when the connection
+creation succeeds or fails.
+
+This is done by passing a
+link:{JDURL}/org/eclipse/jetty/io/ClientConnectionFactory.html[`ClientConnectionFactory`]
+(that creates `Connection` instances) and a
+link:{JDURL}/org/eclipse/jetty/util/Promise.html[`Promise`] (that is notified
+of connection creation success or failure) in the context `Map` as follows:
[source,java,indent=0]
----
include::../{doc_code}/embedded/client/ClientConnectorDocs.java[tags=connect]
----
-TODO: expand on what is the API to use, what parameters the context Map must
-have, and basically how we can write a generic network client with it.
+When a `Connection` is created successfully, its `onOpen()` method is invoked,
+and then the promise is completed successfully.
-[[client-concepts-protocol]]
-==== Client Libraries Protocol Layer
+It is now possible to write a super-simple `telnet` client that reads and writes
+string lines:
-The protocol layer builds on top of the infrastructure layer to generate the
-bytes to be written to the network and to parse the bytes received from the
-network.
+[source,java,indent=0]
+----
+include::../{doc_code}/embedded/client/ClientConnectorDocs.java[tags=telnet]
+----
+
+Note how a very basic "telnet" API that applications could use is implemented
+in the form of the `onLine(Consumer)` for the non-blocking receiving
+side and `writeLine(String, Callback)` for the non-blocking sending side.
+Note also how the `onFillable()` method implements some basic "parsing"
+by looking up the `\n` character in the buffer.
+
+NOTE: The "telnet" client above looks like a super-simple HTTP client because
+HTTP/1.0 can be seen as a line-based protocol. HTTP/1.0 was used just as an
+example, but we could have used any other line-based protocol such as
+link:https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol[SMTP],
+provided that the server was able to understand it.
+
+This is very similar to what the Jetty client implementation does for real
+network protocols.
+Real network protocols are of course more complicated and so is the implementation
+code that handles them, but the general ideas are similar.
+
+The Jetty client implementation provides a number of `ClientConnectionFactory`
+implementations that can be composed to produce and interpret the network bytes.
+
+For example, it is simple to modify the above example to use the TLS protocol
+so that you will be able to connect to the server on port `443`, typically
+reserved for the encrypted HTTP protocol.
+
+The differences between the clear-text version and the TLS encrypted version
+are minimal:
+
+[source,java,indent=0]
+----
+include::../{doc_code}/embedded/client/ClientConnectorDocs.java[tags=tlsTelnet]
+----
+
+The differences with the clear-text version are only:
+
+* Change the port from `80` to `443`.
+* Wrap the `ClientConnectionFactory` with `SslClientConnectionFactory`.
+* Unwrap the `SslConnection` to access `TelnetConnection`.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/client.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/client.adoc
index 4673ae1e6b5..ac1bd6e9170 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/client.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/client.adoc
@@ -17,12 +17,15 @@
//
[[client]]
-== Jetty Client Libraries
+== Client Libraries
-The Eclipse Jetty Project provides not only server-side libraries so that you
-can embed a server in your code, but it also provides client-side libraries
-that allow you to embed a client - for example a HTTP client invoking a third
-party HTTP service - in your application.
+The Eclipse Jetty Project provides also provides client-side libraries
+that allow you to embed a client in your applications.
+A typical example is an application that needs to contact a third party
+service via HTTP (for example a REST service).
+Another example is a proxy application that receives HTTP requests and
+forwards them as FCGI requests to a PHP application such as WordPress,
+or receives HTTP/1.1 requests and converts them to HTTP/2.
The client libraries are designed to be non-blocking and offer both synchronous
and asynchronous APIs and come with a large number of configuration options.
@@ -32,4 +35,9 @@ There are primarily two client libraries:
* link:#client-http[The HTTP client library]
* link:#client-websocket[The WebSocket client library]
-include::client-concepts.adoc[]
+If you are interested in the low-level details of how the Eclipse Jetty
+client libraries work, or are interested in writing a custom protocol,
+look at the link:#client-io-arch[Client I/O Architecture].
+
+include::http/client-http.adoc[]
+include::client-io-arch.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/http-client-api.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-api.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/http-client-api.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-api.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/http-client-authentication.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-authentication.adoc
similarity index 97%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/http-client-authentication.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-authentication.adoc
index 8b7515f315f..4e3e48704ae 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/http-client-authentication.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-authentication.adoc
@@ -16,7 +16,7 @@
// ========================================================================
//
-[[http-client-authentication]]
+[[client-http-authentication]]
=== Authentication Support
Jetty's HTTP client supports the `BASIC` and `DIGEST` authentication mechanisms defined by link:https://tools.ietf.org/html/rfc7235[RFC 7235].
@@ -88,4 +88,4 @@ authn.apply(request);
request.send();
----
-See also the link:#http-client-proxy-authentication[proxy authentication section] for further information about how authentication works with HTTP proxies.
+See also the link:#client-http-proxy-authentication[proxy authentication section] for further information about how authentication works with HTTP proxies.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-configuration.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-configuration.adoc
new file mode 100644
index 00000000000..e9a6f0ab7b1
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-configuration.adoc
@@ -0,0 +1,86 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+[[client-http-configuration]]
+=== HttpClient Configuration
+
+`HttpClient` has a quite large number of configuration parameters.
+Please refer to the `HttpClient`
+link:{JDURL}/org/eclipse/jetty/client/HttpClient.html[javadocs]
+for the complete list of configurable parameters.
+The most common parameters are:
+
+* `HttpClient.idleTimeout`: same as `ClientConnector.idleTimeout`
+described in link:#client-io-arch-network[this section].
+* `HttpClient.connectBlocking`: same as `ClientConnector.connectBlocking`
+described in link:#client-io-arch-network[this section].
+* `HttpClient.connectTimeout`: same as `ClientConnector.connectTimeout`
+described in link:#client-io-arch-network[this section].
+* `HttpClient.maxConnectionsPerDestination`: the max number of TCP
+connections that are opened for a particular destination (defaults to 64).
+* `HttpClient.maxRequestsQueuedPerDestination`: the max number of requests
+queued (defaults to 1024).
+
+[[client-http-configuration-tls]]
+==== HttpClient TLS Configuration
+
+`HttpClient` supports HTTPS requests out-of-the-box like a browser does.
+
+The support for HTTPS request is provided by a `SslContextFactory.Client`,
+typically configured in the `ClientConnector`.
+If not explicitly configured, the `ClientConnector` will allocate a default
+one when started.
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=tlsExplicit]
+----
+
+The default `SslContextFactory.Client` verifies the certificate sent by the
+server by verifying the certificate chain.
+This means that requests to public websites that have a valid certificates
+(such as ``https://google.com``) will work out-of-the-box.
+
+However, requests made to sites (typically ``localhost``) that have invalid
+(for example, expired or with a wrong host) or self-signed certificates will
+fail (like they will in a browser).
+
+Certificate validation is performed at two levels: at the TLS implementation
+level (in the JDK) and - optionally - at the application level.
+
+By default, certificate validation at the TLS level is enabled, while
+certificate validation at the application level is disabled.
+
+You can configure the `SslContextFactory.Client` to skip certificate validation
+at the TLS level:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=tlsNoValidation]
+----
+
+You can enable certificate validation at the application level:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=tlsAppValidation]
+----
+
+Please refer to the `SslContextFactory.Client`
+link:{JDURL}/org/eclipse/jetty/util/ssl/SslContextFactory.Client.html[javadocs]
+for the complete list of configurable parameters.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/http-client-cookie.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-cookie.adoc
similarity index 99%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/http-client-cookie.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-cookie.adoc
index dbe3193b5c8..3802218cdc8 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/http-client-cookie.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-cookie.adoc
@@ -16,7 +16,7 @@
// ========================================================================
//
-[[http-client-cookie]]
+[[client-http-cookie]]
=== Cookies Support
Jetty HTTP client supports cookies out of the box.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc
new file mode 100644
index 00000000000..34f8c78547f
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc
@@ -0,0 +1,175 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+[[client-http-intro]]
+=== HttpClient Introduction
+
+The Jetty HTTP client module provides easy-to-use APIs and utility classes to perform HTTP (or HTTPS) requests.
+
+Jetty's HTTP client is non-blocking and asynchronous.
+It offers an asynchronous API that never blocks for I/O, making it very efficient in thread utilization and well suited for high performance scenarios such as load testing or parallel computation.
+
+However, when all you need to do is to perform a `GET` request to a resource, Jetty's HTTP client offers also a synchronous API; a programming interface
+where the thread that issued the request blocks until the request/response conversation is complete.
+
+Jetty's HTTP client supports link:#http-client-transport[different transports]: HTTP/1.1, FastCGI and HTTP/2.
+This means that the semantic of a HTTP request (that is, " `GET` me the resource `/index.html` ") can be carried over the network in different formats.
+The most common and default format is HTTP/1.1.
+That said, Jetty's HTTP client can carry the same request using the FastCGI format or the HTTP/2 format.
+
+The FastCGI transport is heavily used in Jetty's link:#fastcgi[FastCGI support] that allows Jetty to work as a reverse proxy to PHP (exactly like Apache or Nginx do) and therefore be able to serve - for example - WordPress websites.
+
+The HTTP/2 transport allows Jetty's HTTP client to perform requests using HTTP/2 to HTTP/2 enabled web sites, see also Jetty's link:#http2[HTTP/2 support].
+
+Out of the box features that you get with the Jetty HTTP client include:
+
+* Redirect support - redirect codes such as 302 or 303 are automatically followed.
+* Cookies support - cookies sent by servers are stored and sent back to servers in matching requests.
+* Authentication support - HTTP "Basic" and "Digest" authentications are supported, others are pluggable.
+* Forward proxy support - HTTP proxying and SOCKS4 proxying.
+
+[[client-http-start]]
+==== Starting HttpClient
+
+The main class is named `org.eclipse.jetty.client.HttpClient`.
+
+You can think of a `HttpClient` instance as a browser instance.
+Like a browser it can make requests to different domains, it manages redirects, cookies and authentication, you can configure it with a proxy, and
+it provides you with the responses to the requests you make.
+
+In order to use `HttpClient`, you must instantiate it, configure it, and then start it:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=start]
+----
+
+You may create multiple instances of `HttpClient`, but typically one instance is enough for an application.
+There are several reasons for having multiple `HttpClient` instances including, but not limited to:
+
+* You want to specify different configuration parameters (for example, one instance is configured with a forward proxy while another is not).
+* You want the two instances to behave like two different browsers and hence have different cookies, different authentication credentials, etc.
+* You want to use link:#http-client-transport[different transports].
+
+Like browsers, HTTPS requests are supported out-of-the-box, as long as the server
+provides a valid certificate.
+In case the server does not provide a valid certificate (or in case it is self-signed)
+you want to customize ``HttpClient``'s TLS configuration as described in
+link:#client-http-configuration-tls[this section].
+
+[[client-http-stop]]
+==== Stopping HttpClient
+
+It is recommended that when your application stops, you also stop the `HttpClient` instance (or instances) that you are using.
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=stop]
+----
+
+Stopping `HttpClient` makes sure that the memory it holds (for example, authentication credentials, cookies, etc.) is released, and that the thread pool and scheduler are properly stopped allowing all threads used by `HttpClient` to exit.
+
+[[client-http-arch]]
+==== HttpClient Architecture
+
+A `HttpClient` instance can be thought as a browser instance, and it manages the
+following components:
+
+* a `CookieStore` (see link:#client-http-cookie[this section]).
+* a `AuthenticationStore` (see link:#client-http-authentication[this section]).
+* a `ProxyConfiguration` (see link:#client-http-proxy[this section]).
+* a set of _destinations_.
+
+A _destination_ is the client-side component that represent an _origin_ on
+a server, and manages a queue of requests for that origin, and a pool of
+connections to that origin.
+
+An _origin_ may be simply thought as the tuple `(scheme, host, port)` and it
+is where the client connects to in order to communicate with the server.
+However, this is not enough.
+
+If you use `HttpClient` to write a proxy you may have different clients that
+want to contact the same server.
+In this case, you may not want to use the same proxy-to-server connection to
+proxy requests for both clients, for example for authentication reasons: the
+server may associate the connection with authentication credentials and you
+do not want to use the same connection for two different users that have
+different credentials.
+Instead, you want to use different connections for different clients and
+this can be achieved by "tagging" a destination with a tag object that
+represents the remote client (for example, it could be the remote client IP
+address).
+
+Two origins with the same `(scheme, host, port)` but different `tag`
+create two different destinations and therefore two different connection pools.
+However, also this is not enough.
+
+It is possible that a server speaks different protocols on the same `port`.
+A connection may start by speaking one protocol, for example HTTP/1.1, but
+then be upgraded to speak a different protocol, for example HTTP/2.
+After a connection has been upgraded to a second protocol, it cannot speak
+the first protocol anymore, so it can only be used to communicate using
+the second protocol.
+
+Two origins with the same `(scheme, host, port)` but different
+`protocol` create two different destinations and therefore two different
+connection pools.
+
+Therefore an origin is identified by the tuple
+`(scheme, host, port, tag, protocol)`.
+
+[[client-http-request-processing]]
+==== HttpClient Request Processing
+
+When a request is sent, an origin is computed from the request; `HttpClient`
+uses that origin to find (or create if it does not exist) the correspondent
+destination.
+The request is then queued onto the destination, and this causes the
+destination to ask its connection pool for a free connection.
+If a connection is available, it is returned, otherwise a new connection is
+created.
+Once the destination has obtained the connection, it dequeues the request
+and sends it over the connection.
+
+The first request to a destination triggers the opening of the first
+connection.
+A second request with the same origin sent _after_ the first will reuse the
+same connection.
+A second request with the same origin sent _concurrently_ with the first
+request will cause the opening of a second connection.
+The configuration parameter `HttpClient.maxConnectionsPerDestination`
+(see also the link:#client-http-configuration[configuration section]) controls
+the max number of connections that can be opened for a destination.
+
+NOTE: If opening connections to a given origin takes a long time, then
+requests for that origin will queue up in the corresponding destination.
+
+Each connection can handle a limited number of requests.
+For HTTP/1.1, this number is always `1`: there can only be one outstanding
+request for each connection.
+For HTTP/2 this number is determined by the server `max_concurrent_stream`
+setting (typically around `100`, i.e. there can be up to `100` outstanding
+requests for every connection).
+
+When a destination has maxed out its number of connections, and all
+connections have maxed out their number of outstanding requests, more requests
+sent to that destination will be queued.
+When the request queue is full, the request will be failed.
+The configuration parameter `HttpClient.maxRequestsQueuedPerDestination`
+(see also the link:#client-http-configuration[configuration section]) controls
+the max number of requests that can be queued for a destination.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/http-client-proxy.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-proxy.adoc
similarity index 96%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/http-client-proxy.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-proxy.adoc
index 1d5acd92475..13f53584e93 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/http-client-proxy.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-proxy.adoc
@@ -16,7 +16,7 @@
// ========================================================================
//
-[[http-client-proxy]]
+[[client-http-proxy]]
=== Proxy Support
Jetty's HTTP client can be configured to use proxies to connect to destinations.
@@ -44,10 +44,10 @@ You specify the proxy host and port, and optionally also the addresses that you
Configured in this way, `HttpClient` makes requests to the HTTP proxy (for plain-text HTTP requests) or establishes a tunnel via `HTTP CONNECT` (for encrypted HTTPS requests).
-[[http-client-proxy-authentication]]
+[[client-http-proxy-authentication]]
==== Proxy Authentication Support
-Jetty's HTTP client support proxy authentication in the same way it supports link:#http-client-authentication[server authentication].
+Jetty's HTTP client support proxy authentication in the same way it supports link:#client-http-authentication[server authentication].
In the example below, the proxy requires Basic authentication, but the server requires Digest authentication, and therefore:
@@ -100,4 +100,4 @@ Application HttpClient Proxy Server
The application does not receive events related to the responses with code 407 and 401 since they are handled internally by `HttpClient`.
-Similarly to the link:#http-client-authentication[authentication section], the proxy authentication result and the server authentication result can be preempted to avoid, respectively, the 407 and 401 roundtrips.
+Similarly to the link:#client-http-authentication[authentication section], the proxy authentication result and the server authentication result can be preempted to avoid, respectively, the 407 and 401 roundtrips.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/http-client-transport.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-transport.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/http-client-transport.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-transport.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http.adoc
index c1f403bd05e..8d0d71a911e 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http.adoc
@@ -18,3 +18,12 @@
[[client-http]]
=== HTTP Client
+
+include::client-http-intro.adoc[]
+include::client-http-configuration.adoc[]
+include::client-http-api.adoc[]
+include::client-http-cookie.adoc[]
+include::client-http-authentication.adoc[]
+include::client-http-proxy.adoc[]
+include::client-http-transport.adoc[]
+
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc
index e083aa1d6ee..178e04eaaa0 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc
@@ -261,3 +261,5 @@ Otherwise, the write is now `PENDING` and waiting for the callback to be
notified of the completion at a later time.
When the callback is notified of the `write()` completion, it checks whether
the `write()` was `PENDING`, and if it was it resumes reading.
+
+NOTE: TODO: Introduce IteratingCallback?
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/chapter.adoc
deleted file mode 100644
index fe028abc9b0..00000000000
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/chapter.adoc
+++ /dev/null
@@ -1,27 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-//
-// This program and the accompanying materials are made available under
-// the terms of the Eclipse Public License 2.0 which is available at
-// https://www.eclipse.org/legal/epl-2.0
-//
-// This Source Code may also be made available under the following
-// Secondary Licenses when the conditions for such availability set
-// forth in the Eclipse Public License, v. 2.0 are satisfied:
-// the Apache License v2.0 which is available at
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
-// ========================================================================
-//
-
-[[http-client]]
-== HTTP Client
-
-include::http-client-intro.adoc[]
-include::http-client-api.adoc[]
-include::http-client-cookie.adoc[]
-include::http-client-authentication.adoc[]
-include::http-client-proxy.adoc[]
-include::http-client-transport.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/http-client-intro.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/http-client-intro.adoc
deleted file mode 100644
index 3a2f70d9c1b..00000000000
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/server/clients/http/http-client-intro.adoc
+++ /dev/null
@@ -1,105 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-//
-// This program and the accompanying materials are made available under
-// the terms of the Eclipse Public License 2.0 which is available at
-// https://www.eclipse.org/legal/epl-2.0
-//
-// This Source Code may also be made available under the following
-// Secondary Licenses when the conditions for such availability set
-// forth in the Eclipse Public License, v. 2.0 are satisfied:
-// the Apache License v2.0 which is available at
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
-// ========================================================================
-//
-
-[[http-client-intro]]
-=== Introduction
-
-The Jetty HTTP client module provides easy-to-use APIs and utility classes to perform HTTP (or HTTPS) requests.
-
-Jetty's HTTP client is non-blocking and asynchronous.
-It offers an asynchronous API that never blocks for I/O, making it very efficient in thread utilization and well suited for high performance scenarios such as load testing or parallel computation.
-
-However, when all you need to do is to perform a `GET` request to a resource, Jetty's HTTP client offers also a synchronous API; a programming interface
-where the thread that issued the request blocks until the request/response conversation is complete.
-
-Jetty's HTTP client supports different link:#http-client-transport[transports]: HTTP/1.1, FastCGI and HTTP/2.
-This means that the semantic of a HTTP request (that is, " `GET` me the resource `/index.html` ") can be carried over the network in different formats.
-The most common and default format is HTTP/1.1.
-That said, Jetty's HTTP client can carry the same request using the FastCGI format or the new HTTP/2 format.
-
-The FastCGI transport is heavily used in Jetty's link:#fastcgi[FastCGI support] that allows Jetty to work as a reverse proxy to PHP (exactly like Apache or Nginx do) and therefore be able to serve - for example - WordPress websites.
-
-The HTTP/2 transport allows Jetty's HTTP client to perform requests using HTTP/2 to HTTP/2 enabled web sites, see also Jetty's link:#http2[HTTP/2 support].
-
-Out of the box features that you get with the Jetty HTTP client include:
-
-* Redirect support - redirect codes such as 302 or 303 are automatically followed.
-* Cookies support - cookies sent by servers are stored and sent back to servers in matching requests.
-* Authentication support - HTTP "Basic" and "Digest" authentications are supported, others are pluggable.
-* Forward proxy support - HTTP proxying and SOCKS4 proxying.
-
-[[http-client-init]]
-==== Starting HttpClient
-
-The main class is named `org.eclipse.jetty.client.HttpClient`.
-
-You can think of a `HttpClient` instance as a browser instance.
-Like a browser it can make requests to different domains, it manages redirects, cookies and authentication, you can configure it with a proxy, and
-it provides you with the responses to the requests you make.
-
-In order to use `HttpClient`, you must instantiate it, configure it, and then start it:
-
-[source, java, subs="{sub-order}"]
-----
-// Instantiate HttpClient
-HttpClient httpClient = new HttpClient();
-
-// Configure HttpClient, for example:
-httpClient.setFollowRedirects(false);
-
-// Start HttpClient
-httpClient.start();
-----
-
-You may create multiple instances of `HttpClient`, but typically one instance is enough for an application.
-There are several reasons for having multiple `HttpClient` instances including, but not limited to:
-
-* You want to specify different configuration parameters (for example, one instance is configured with a forward proxy while another is not)
-* You want the two instances to behave like two different browsers and hence have different cookies, different authentication credentials...etc.
-* You want to use different transports
-
-When you create a `HttpClient` instance using the parameterless constructor, you will only be able to perform plain HTTP requests and you will not be able to perform HTTPS requests.
-
-In order to perform HTTPS requests, you should create first a link:{JDURL}/org/eclipse/jetty/util/ssl/SslContextFactory.Client.html[`SslContextFactory.Client`], configure it, and pass it to the `HttpClient` constructor.
-When created with a `SslContextFactory`, the `HttpClient` will be able to perform both HTTP and HTTPS requests to any domain.
-
-[source, java, subs="{sub-order}"]
-----
-// Instantiate and configure the SslContextFactory
-SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
-
-// Instantiate HttpClient with the SslContextFactory
-HttpClient httpClient = new HttpClient(sslContextFactory);
-
-// Configure HttpClient, for example:
-httpClient.setFollowRedirects(false);
-
-// Start HttpClient
-httpClient.start();
-----
-
-==== Stopping HttpClient
-
-It is recommended that when your application stops, you also stop the `HttpClient` instance (or instances) that you are using.
-
-[source, java, subs="{sub-order}"]
-----
-httpClient.stop();
-----
-
-Stopping `HttpClient` makes sure that the memory it holds (for example, authentication credentials, cookies, etc.) is released, and that the thread pool and scheduler are properly stopped allowing all threads used by `HttpClient` to exit.
diff --git a/jetty-documentation/src/main/java/embedded/client/ClientConnectorDocs.java b/jetty-documentation/src/main/java/embedded/client/ClientConnectorDocs.java
index 840bb63a0b2..5fcd35c789e 100644
--- a/jetty-documentation/src/main/java/embedded/client/ClientConnectorDocs.java
+++ b/jetty-documentation/src/main/java/embedded/client/ClientConnectorDocs.java
@@ -18,17 +18,27 @@
package embedded.client;
+import java.io.ByteArrayOutputStream;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.SelectorManager;
+import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
+import org.eclipse.jetty.io.ssl.SslConnection;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
@@ -108,9 +118,9 @@ public class ClientConnectorDocs
public void connect() throws Exception
{
// tag::connect[]
- class CustomHTTPConnection extends AbstractConnection
+ class CustomConnection extends AbstractConnection
{
- public CustomHTTPConnection(EndPoint endPoint, Executor executor)
+ public CustomConnection(EndPoint endPoint, Executor executor)
{
super(endPoint, executor);
}
@@ -119,6 +129,7 @@ public class ClientConnectorDocs
public void onOpen()
{
super.onOpen();
+ System.getLogger("connection").log(INFO, "Opened connection {0}", this);
}
@Override
@@ -130,23 +141,289 @@ public class ClientConnectorDocs
ClientConnector clientConnector = new ClientConnector();
clientConnector.start();
+ String host = "serverHost";
+ int port = 8080;
+ SocketAddress address = new InetSocketAddress(host, port);
+
+ // The ClientConnectionFactory that creates CustomConnection instances.
+ ClientConnectionFactory connectionFactory = (endPoint, context) ->
+ {
+ System.getLogger("connection").log(INFO, "Creating connection for {0}", endPoint);
+ return new CustomConnection(endPoint, clientConnector.getExecutor());
+ };
+
+ // The Promise to notify of connection creation success or failure.
+ CompletableFuture connectionPromise = new Promise.Completable<>();
+
+ // Populate the context with the mandatory keys to create and obtain connections.
+ Map context = new HashMap<>();
+ context.put(ClientConnector.CLIENT_CONNECTION_FACTORY_CONTEXT_KEY, connectionFactory);
+ context.put(ClientConnector.CONNECTION_PROMISE_CONTEXT_KEY, connectionPromise);
+ clientConnector.connect(address, context);
+
+ // Use the Connection when it's available.
+
+ // Use it in a non-blocking way via CompletableFuture APIs.
+ connectionPromise.whenComplete((connection, failure) ->
+ {
+ System.getLogger("connection").log(INFO, "Created connection for {0}", connection);
+ });
+
+ // Alternatively, you can block waiting for the connection (or a failure).
+ // CustomConnection connection = connectionPromise.get();
+ // end::connect[]
+ }
+
+ public void telnet() throws Exception
+ {
+ // tag::telnet[]
+ class TelnetConnection extends AbstractConnection
+ {
+ private final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ private Consumer consumer;
+
+ public TelnetConnection(EndPoint endPoint, Executor executor)
+ {
+ super(endPoint, executor);
+ }
+
+ @Override
+ public void onOpen()
+ {
+ super.onOpen();
+
+ // Declare interest for fill events.
+ fillInterested();
+ }
+
+ @Override
+ public void onFillable()
+ {
+ try
+ {
+ ByteBuffer buffer = BufferUtil.allocate(1024);
+ while (true)
+ {
+ int filled = getEndPoint().fill(buffer);
+ if (filled > 0)
+ {
+ while (buffer.hasRemaining())
+ {
+ // Search for newline.
+ byte read = buffer.get();
+ if (read == '\n')
+ {
+ // Notify the consumer of the line.
+ consumer.accept(bytes.toString(StandardCharsets.UTF_8));
+ bytes.reset();
+ }
+ else
+ {
+ bytes.write(read);
+ }
+ }
+ }
+ else if (filled == 0)
+ {
+ // No more bytes to fill, declare
+ // again interest for fill events.
+ fillInterested();
+ return;
+ }
+ else
+ {
+ // The other peer closed the
+ // connection, close it back.
+ getEndPoint().close();
+ return;
+ }
+ }
+ }
+ catch (Exception x)
+ {
+ getEndPoint().close(x);
+ }
+ }
+
+ public void onLine(Consumer consumer)
+ {
+ this.consumer = consumer;
+ }
+
+ public void writeLine(String line, Callback callback)
+ {
+ line = line + "\r\n";
+ getEndPoint().write(callback, ByteBuffer.wrap(line.getBytes(StandardCharsets.UTF_8)));
+ }
+ }
+
+ ClientConnector clientConnector = new ClientConnector();
+ clientConnector.start();
+
String host = "wikipedia.org";
int port = 80;
SocketAddress address = new InetSocketAddress(host, port);
ClientConnectionFactory connectionFactory = (endPoint, context) ->
- {
- System.getLogger("connection").log(INFO, "Creating connection for {0}", endPoint);
- return new CustomHTTPConnection(endPoint, clientConnector.getExecutor());
- };
+ new TelnetConnection(endPoint, clientConnector.getExecutor());
+
+ CompletableFuture connectionPromise = new Promise.Completable<>();
+
Map context = new HashMap<>();
context.put(ClientConnector.CLIENT_CONNECTION_FACTORY_CONTEXT_KEY, connectionFactory);
+ context.put(ClientConnector.CONNECTION_PROMISE_CONTEXT_KEY, connectionPromise);
clientConnector.connect(address, context);
- // end::connect[]
+
+ connectionPromise.whenComplete((connection, failure) ->
+ {
+ if (failure == null)
+ {
+ // Register a listener that receives string lines.
+ connection.onLine(line -> System.getLogger("app").log(INFO, "line: {0}", line));
+
+ // Write a line.
+ connection.writeLine("" +
+ "GET / HTTP/1.0\r\n" +
+ "", Callback.NOOP);
+ }
+ else
+ {
+ failure.printStackTrace();
+ }
+ });
+ // end::telnet[]
+ }
+
+ public void tlsTelnet() throws Exception
+ {
+ // tag::tlsTelnet[]
+ class TelnetConnection extends AbstractConnection
+ {
+ private final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ private Consumer consumer;
+
+ public TelnetConnection(EndPoint endPoint, Executor executor)
+ {
+ super(endPoint, executor);
+ }
+
+ @Override
+ public void onOpen()
+ {
+ super.onOpen();
+
+ // Declare interest for fill events.
+ fillInterested();
+ }
+
+ @Override
+ public void onFillable()
+ {
+ try
+ {
+ ByteBuffer buffer = BufferUtil.allocate(1024);
+ while (true)
+ {
+ int filled = getEndPoint().fill(buffer);
+ if (filled > 0)
+ {
+ while (buffer.hasRemaining())
+ {
+ // Search for newline.
+ byte read = buffer.get();
+ if (read == '\n')
+ {
+ // Notify the consumer of the line.
+ consumer.accept(bytes.toString(StandardCharsets.UTF_8));
+ bytes.reset();
+ }
+ else
+ {
+ bytes.write(read);
+ }
+ }
+ }
+ else if (filled == 0)
+ {
+ // No more bytes to fill, declare
+ // again interest for fill events.
+ fillInterested();
+ return;
+ }
+ else
+ {
+ // The other peer closed the
+ // connection, close it back.
+ getEndPoint().close();
+ return;
+ }
+ }
+ }
+ catch (Exception x)
+ {
+ getEndPoint().close(x);
+ }
+ }
+
+ public void onLine(Consumer consumer)
+ {
+ this.consumer = consumer;
+ }
+
+ public void writeLine(String line, Callback callback)
+ {
+ line = line + "\r\n";
+ getEndPoint().write(callback, ByteBuffer.wrap(line.getBytes(StandardCharsets.UTF_8)));
+ }
+ }
+
+ ClientConnector clientConnector = new ClientConnector();
+ clientConnector.start();
+
+ // Use port 443 to contact the server using encrypted HTTP.
+ String host = "wikipedia.org";
+ int port = 443;
+ SocketAddress address = new InetSocketAddress(host, port);
+
+ ClientConnectionFactory connectionFactory = (endPoint, context) ->
+ new TelnetConnection(endPoint, clientConnector.getExecutor());
+
+ // Wrap the "telnet" ClientConnectionFactory with the SslClientConnectionFactory.
+ connectionFactory = new SslClientConnectionFactory(clientConnector.getSslContextFactory(),
+ clientConnector.getByteBufferPool(), clientConnector.getExecutor(), connectionFactory);
+
+ // We will obtain a SslConnection now.
+ CompletableFuture connectionPromise = new Promise.Completable<>();
+
+ Map context = new HashMap<>();
+ context.put(ClientConnector.CLIENT_CONNECTION_FACTORY_CONTEXT_KEY, connectionFactory);
+ context.put(ClientConnector.CONNECTION_PROMISE_CONTEXT_KEY, connectionPromise);
+ clientConnector.connect(address, context);
+
+ connectionPromise.whenComplete((sslConnection, failure) ->
+ {
+ if (failure == null)
+ {
+ // Unwrap the SslConnection to access the "line" APIs in TelnetConnection.
+ TelnetConnection connection = (TelnetConnection)sslConnection.getDecryptedEndPoint().getConnection();
+ // Register a listener that receives string lines.
+ connection.onLine(line -> System.getLogger("app").log(INFO, "line: {0}", line));
+
+ // Write a line.
+ connection.writeLine("" +
+ "GET / HTTP/1.0\r\n" +
+ "", Callback.NOOP);
+ }
+ else
+ {
+ failure.printStackTrace();
+ }
+ });
+ // end::tlsTelnet[]
}
public static void main(String[] args) throws Exception
{
- new ClientConnectorDocs().connect();
+ new ClientConnectorDocs().tlsTelnet();
}
}
diff --git a/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java b/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
new file mode 100644
index 00000000000..f531283b41a
--- /dev/null
+++ b/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
@@ -0,0 +1,82 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package embedded.client.http;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
+import org.eclipse.jetty.io.ClientConnector;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+public class HTTPClientDocs
+{
+ public void start() throws Exception
+ {
+ // tag::start[]
+ // Instantiate HttpClient.
+ HttpClient httpClient = new HttpClient();
+
+ // Configure HttpClient, for example:
+ httpClient.setFollowRedirects(false);
+
+ // Start HttpClient.
+ httpClient.start();
+ // end::start[]
+ }
+
+ public void stop() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+ // tag::stop[]
+ // Stop HttpClient.
+ httpClient.stop();
+ // end::stop[]
+ }
+
+ public void tlsExplicit() throws Exception
+ {
+ // tag::tlsExplicit[]
+ SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
+
+ ClientConnector clientConnector = new ClientConnector();
+ clientConnector.setSslContextFactory(sslContextFactory);
+
+ HttpClient httpClient = new HttpClient(new HttpClientTransportDynamic(clientConnector));
+ httpClient.start();
+ // end::tlsExplicit[]
+ }
+
+ public void tlsNoValidation() throws Exception
+ {
+ // tag::tlsNoValidation[]
+ SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
+ // Disable certificate validation at the TLS level.
+ sslContextFactory.setEndpointIdentificationAlgorithm(null);
+ // end::tlsNoValidation[]
+ }
+
+ public void tlsAppValidation() throws Exception
+ {
+ // tag::tlsAppValidation[]
+ SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
+ // Only allow subdomains of domain.com.
+ sslContextFactory.setHostnameVerifier((hostName, session) -> hostName.endsWith(".domain.com"));
+ // end::tlsAppValidation[]
+ }
+}
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnector.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnector.java
index abe77a5673e..5d215abf78a 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnector.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnector.java
@@ -30,6 +30,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
+import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.ssl.SslContextFactory;
@@ -281,15 +282,7 @@ public class ClientConnector extends ContainerLifeCycle
protected void safeClose(Closeable closeable)
{
- try
- {
- if (closeable != null)
- closeable.close();
- }
- catch (Throwable x)
- {
- LOG.trace("IGNORED", x);
- }
+ IO.close(closeable);
}
protected void configure(SocketChannel channel) throws IOException
@@ -330,6 +323,18 @@ public class ClientConnector extends ContainerLifeCycle
return factory.newConnection(endPoint, context);
}
+ @Override
+ public void connectionOpened(Connection connection, Object context)
+ {
+ super.connectionOpened(connection, context);
+ @SuppressWarnings("unchecked")
+ Map contextMap = (Map)context;
+ @SuppressWarnings("unchecked")
+ Promise promise = (Promise)contextMap.get(CONNECTION_PROMISE_CONTEXT_KEY);
+ if (promise != null)
+ promise.succeeded(connection);
+ }
+
@Override
protected void connectionFailed(SelectableChannel channel, Throwable failure, Object attachment)
{
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java
index afa3410f882..fbdd71ad77b 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java
@@ -268,12 +268,13 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable
private void createEndPoint(SelectableChannel channel, SelectionKey selectionKey) throws IOException
{
EndPoint endPoint = _selectorManager.newEndPoint(channel, this, selectionKey);
- Connection connection = _selectorManager.newConnection(channel, endPoint, selectionKey.attachment());
+ Object context = selectionKey.attachment();
+ Connection connection = _selectorManager.newConnection(channel, endPoint, context);
endPoint.setConnection(connection);
selectionKey.attach(endPoint);
endPoint.onOpen();
endPointOpened(endPoint);
- _selectorManager.connectionOpened(connection);
+ _selectorManager.connectionOpened(connection, context);
if (LOG.isDebugEnabled())
LOG.debug("Created {}", endPoint);
}
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/SelectorManager.java b/jetty-io/src/main/java/org/eclipse/jetty/io/SelectorManager.java
index fb427e68291..1bcd0080ed8 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/SelectorManager.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/SelectorManager.java
@@ -302,8 +302,10 @@ public abstract class SelectorManager extends ContainerLifeCycle implements Dump
*
Callback method invoked when a connection is opened.
*
* @param connection the connection just opened
+ * @param context the attachment associated with the creation of the connection
+ * @see #newConnection(SelectableChannel, EndPoint, Object)
*/
- public void connectionOpened(Connection connection)
+ public void connectionOpened(Connection connection, Object context)
{
try
{
From 929ce34640716fc6231142828672005722e5d108 Mon Sep 17 00:00:00 2001
From: Joakim Erdfelt
Date: Tue, 31 Mar 2020 14:58:17 -0500
Subject: [PATCH 021/101] Issue #4529 - Fixing HTML error page from showing
servlet info if configured not to do so
Signed-off-by: Joakim Erdfelt
---
.../org/eclipse/jetty/server/handler/ErrorHandler.java | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java
index 98fdb08b596..7d11d60bdd4 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java
@@ -416,7 +416,10 @@ public class ErrorHandler extends AbstractHandler
htmlRow(writer, "URI", uri);
htmlRow(writer, "STATUS", status);
htmlRow(writer, "MESSAGE", message);
- htmlRow(writer, "SERVLET", request.getAttribute(Dispatcher.ERROR_SERVLET_NAME));
+ if (isShowServlet())
+ {
+ htmlRow(writer, "SERVLET", request.getAttribute(Dispatcher.ERROR_SERVLET_NAME));
+ }
Throwable cause = (Throwable)request.getAttribute(Dispatcher.ERROR_EXCEPTION);
while (cause != null)
{
@@ -457,7 +460,7 @@ public class ErrorHandler extends AbstractHandler
while (cause != null)
{
writer.printf("CAUSED BY %s%n", cause);
- if (_showStacks && !_disableStacks)
+ if (isShowStacks() && !_disableStacks)
{
cause.printStackTrace(writer);
}
From b5956b975d2158d1178634a78e869555c3822423 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Tue, 31 Mar 2020 23:12:48 +0200
Subject: [PATCH 022/101] Improvements to the Jetty client documentation.
Signed-off-by: Simone Bordet
---
.../client/http/client-http-api.adoc | 353 ++++++----------
.../client/http/client-http.adoc | 2 +-
.../embedded/client/http/HTTPClientDocs.java | 399 +++++++++++++++++-
3 files changed, 532 insertions(+), 222 deletions(-)
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-api.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-api.adoc
index 6d0cd736ae4..4765fb47a18 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-api.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-api.adoc
@@ -16,17 +16,19 @@
// ========================================================================
//
-[[http-client-api]]
-=== API Usage
+[[client-http-api]]
+=== HttpClient API Usage
-[[http-client-blocking]]
-==== Blocking APIs
+`HttpClient` provides two types of APIs: a blocking API and a non-blocking API.
-The simple way to perform a HTTP request is the following:
+[[client-http-blocking]]
+==== HttpClient Blocking APIs
-[source, java, subs="{sub-order}"]
+The simpler way to perform a HTTP request is the following:
+
+[source,java,indent=0]
----
-ContentResponse response = httpClient.GET("http://domain.com/path?query");
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=simpleBlockingGet]
----
The method `HttpClient.GET(...)` performs a HTTP `GET` request to the given URI and returns a `ContentResponse` when the request/response conversation completes successfully.
@@ -36,22 +38,16 @@ The content length is limited by default to 2 MiB; for larger content see xref:h
If you want to customize the request, for example by issuing a `HEAD` request instead of a `GET`, and simulating a browser user agent, you can do it in this way:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-ContentResponse response = httpClient.newRequest("http://domain.com/path?query")
- .method(HttpMethod.HEAD)
- .agent("Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:17.0) Gecko/20100101 Firefox/17.0")
- .send();
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=headFluent]
----
This is a shorthand for:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-Request request = httpClient.newRequest("http://domain.com/path?query");
-request.method(HttpMethod.HEAD);
-request.agent("Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:17.0) Gecko/20100101 Firefox/17.0");
-ContentResponse response = request.send();
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=headNonFluent]
----
You first create a request object using `httpClient.newRequest(...)`, and then you customize it using the fluent API style (that is, a chained invocation of methods on the request object).
@@ -59,52 +55,45 @@ When the request object is customized, you call `request.send()` that produces t
Simple `POST` requests also have a shortcut method:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-ContentResponse response = httpClient.POST("http://domain.com/entity/1")
- .param("p", "value")
- .send();
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=postFluent]
----
The `POST` parameter values added via the `param()` method are automatically URL-encoded.
-Jetty's HTTP client automatically follows redirects, so it handles the typical web pattern http://en.wikipedia.org/wiki/Post/Redirect/Get[POST/Redirect/GET], and the response object contains the content of the response of the `GET` request.
+Jetty's `HttpClient` automatically follows redirects, so it handles the typical web pattern http://en.wikipedia.org/wiki/Post/Redirect/Get[POST/Redirect/GET], and the response object contains the content of the response of the `GET` request.
Following redirects is a feature that you can enable/disable on a per-request basis or globally.
-File uploads also require one line, and make use of JDK 7′s `java.nio.file` classes:
+File uploads also require one line, and make use of `java.nio.file` classes:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-ContentResponse response = httpClient.newRequest("http://domain.com/upload")
- .method(HttpMethod.POST)
- .file(Paths.get("file_to_upload.txt"), "text/plain")
- .send();
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=fileFluent]
----
It is possible to impose a total timeout for the request/response conversation using the `Request.timeout(...)` method as follows:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-ContentResponse response = httpClient.newRequest("http://domain.com/path?query")
- .timeout(5, TimeUnit.SECONDS)
- .send();
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=totalTimeout]
----
In the example above, when the 5 seconds expire, the request is aborted and a `java.util.concurrent.TimeoutException` is thrown.
-[[http-client-async]]
-==== Non-Blocking APIs
+[[client-http-non-blocking]]
+==== HttpClient Non-Blocking APIs
So far we have shown how to use Jetty HTTP client in a blocking style - that is, the thread that issues the request blocks until the request/response conversation is complete.
-This section will look at Jetty's HTTP client non-blocking, asynchronous APIs that are perfectly suited for large content downloads, for parallel processing of requests/responses and in cases where performance and efficient thread and resource utilization is a key factor.
+This section will look at Jetty's `HttpClient` non-blocking, asynchronous APIs that are perfectly suited for large content downloads, for parallel processing of requests/responses and in cases where performance and efficient thread and resource utilization is a key factor.
The asynchronous APIs rely heavily on listeners that are invoked at various stages of request and response processing.
These listeners are implemented by applications and may perform any kind of logic.
The implementation invokes these listeners in the same thread that is used to process the request or response.
Therefore, if the application code in these listeners takes a long time to execute, the request or response processing is delayed until the listener returns.
-If you need to execute application code that takes long time inside a listener, you must spawn your own thread and remember to deep copy any data provided by the listener that you will need in your code, because when the listener returns the data it provides may be recycled/cleared/destroyed.
+If you need to execute application code that takes long time inside a listener, you must spawn your own thread.
Request and response processing are executed by two different threads and therefore may happen concurrently.
A typical example of this concurrent processing is an echo server, where a large upload may be concurrent with the large download echoed back.
@@ -119,148 +108,74 @@ Response processing continues until either the response is fully processed or un
If it would block for I/O, the thread asks the I/O system to emit an event when the I/O will be ready to continue, then returns.
When such an event is fired, a thread taken from the `HttpClient` thread pool will resume the processing of the response.
-When the request and the response are both fully processed, the thread that finished the last processing (usually the thread that processes the response, but may also be the thread that processes the request - if the request takes more time than the response to be processed) is used to de-queue the next request for the same destination and processes it.
+When the request and the response are both fully processed, the thread that finished the last processing (usually the thread that processes the response, but may also be the thread that processes the request - if the request takes more time than the response to be processed) is used to dequeue the next request for the same destination and processes it.
-A simple asynchronous `GET` request that discards the response content can be written in this way:
+A simple non-blocking `GET` request that discards the response content can be written in this way:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-httpClient.newRequest("http://domain.com/path")
- .send(new Response.CompleteListener()
- {
- @Override
- public void onComplete(Result result)
- {
- // Your logic here
- }
- });
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=simpleNonBlocking]
----
-Method `Request.send(Response.CompleteListener)` returns `void` and does not block; the `Response.CompleteListener` provided as a parameter is notified when the request/response conversation is complete, and the `Result` parameter allows you to access the response object.
-
-You can write the same code using JDK 8′s lambda expressions:
-
-[source, java, subs="{sub-order}"]
-----
-httpClient.newRequest("http://domain.com/path")
- .send(result -> { /* Your logic here */ });
-----
+Method `Request.send(Response.CompleteListener)` returns `void` and does not block; the `Response.CompleteListener` lambda provided as a parameter is notified when the request/response conversation is complete, and the `Result` parameter allows you to access the request and response objects as well as failures, if any.
You can impose a total timeout for the request/response conversation in the same way used by the synchronous API:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-httpClient.newRequest("http://domain.com/path")
- .timeout(3, TimeUnit.SECONDS)
- .send(result -> { /* Your logic here */ });
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=nonBlockingTotalTimeout]
----
The example above will impose a total timeout of 3 seconds on the request/response conversation.
-The HTTP client APIs use listeners extensively to provide hooks for all possible request and response events, and with JDK 8′s lambda expressions they are even more fun to use:
+The HTTP client APIs use listeners extensively to provide hooks for all possible request and response events:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-httpClient.newRequest("http://domain.com/path")
- // Add request hooks
- .onRequestQueued(request -> { ... })
- .onRequestBegin(request -> { ... })
- ... // More request hooks available
-
- // Add response hooks
- .onResponseBegin(response -> { ... })
- .onResponseHeaders(response -> { ... })
- .onResponseContent((response, buffer) -> { ... })
- ... // More response hooks available
-
- .send(result -> { ... });
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=listeners]
----
This makes Jetty HTTP client suitable for HTTP load testing because, for example, you can accurately time every step of the request/response conversation (thus knowing where the request/response time is really spent).
Have a look at the link:{JDURL}/org/eclipse/jetty/client/api/Request.Listener.html[`Request.Listener`] class to know about request events, and to the link:{JDURL}/org/eclipse/jetty/client/api/Response.Listener.html[`Response.Listener`] class to know about response events.
-[[http-client-content]]
-==== Content Handling
+[[client-http-content]]
+==== HttpClient Content Handling
-[[http-client-request-content]]
+[[client-http-content-request]]
===== Request Content Handling
-Jetty's HTTP client provides a number of utility classes off the shelf to handle request content.
+Jetty's `HttpClient` provides a number of utility classes off the shelf to handle request content.
You can provide request content as `String`, `byte[]`, `ByteBuffer`, `java.nio.file.Path`, `InputStream`, and provide your own implementation of `org.eclipse.jetty.client.api.Request.Content`.
Here’s an example that provides the request content using `java.nio.file.Paths`:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-ContentResponse response = httpClient.newRequest("http://domain.com/upload")
- .method(HttpMethod.POST)
- .file(Paths.get("file_to_upload.txt"), "text/plain")
- .send();
-----
-
-This is equivalent to using the `PathRequestContent` utility class:
-
-[source, java, subs="{sub-order}"]
-----
-ContentResponse response = httpClient.newRequest("http://domain.com/upload")
- .method(HttpMethod.POST)
- .body(new PathRequestContent("text/plain", Paths.get("file_to_upload.txt")))
- .send();
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=pathRequestContent]
----
Alternatively, you can use `FileInputStream` via the `InputStreamRequestContent` utility class:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-ContentResponse response = httpClient.newRequest("http://domain.com/upload")
- .method(HttpMethod.POST)
- .body(new InputStreamRequestContent("text/plain", new FileInputStream("file_to_upload.txt")))
- .send();
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=inputStreamRequestContent]
----
-Since `InputStream` is blocking, then also the send of the request will block if the input stream blocks, even in case of usage of the asynchronous `HttpClient` APIs.
+Since `InputStream` is blocking, then also the send of the request will block if the input stream blocks, even in case of usage of the non-blocking `HttpClient` APIs.
-If you have already read the content in memory, you can pass it as a `byte[]` using the `BytesRequestContent` utility class:
+If you have already read the content in memory, you can pass it as a `byte[]` (or a `String`) using the `BytesRequestContent` (or `StringRequestContent`) utility class:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-byte[] bytes = ...;
-ContentResponse response = httpClient.newRequest("http://domain.com/upload")
- .method(HttpMethod.POST)
- .body(new BytesRequestContent("text/plain", bytes))
- .send();
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=bytesStringRequestContent]
----
If the request content is not immediately available, but your application will be notified of the content to send, you can use `AsyncRequestContent` in this way:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-AsyncRequestContent content = new AsyncRequestContent();
-httpClient.newRequest("http://domain.com/upload")
- .method(HttpMethod.POST)
- .body(content)
- .send(new Response.CompleteListener()
- {
- @Override
- public void onComplete(Result result)
- {
- // Your logic here
- }
- });
-
-// Content not available yet here.
-
-...
-
-// An event happens, now content is available.
-byte[] bytes = ...;
-content.offer(ByteBuffer.wrap(bytes));
-
-...
-
-// All content has arrived.
-content.close();
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=asyncRequestContent]
----
While the request content is awaited and consequently uploaded by the client application, the server may be able to respond (at least with the response headers) completely asynchronously.
@@ -270,113 +185,113 @@ This allows fine-grained control of the request/response conversation: for examp
Another way to provide request content is by using an `OutputStreamRequestContent`,
which allows applications to write request content when it is available to the `OutputStream` provided by `OutputStreamRequestContent`:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-OutputStreamRequestContent content = new OutputStreamRequestContent();
-
-// Use try-with-resources to close the OutputStream when all content is written.
-try (OutputStream output = content.getOutputStream())
-{
- client.newRequest("localhost", 8080)
- .method(HttpMethod.POST)
- .body(content)
- .send(new Response.CompleteListener()
- {
- @Override
- public void onComplete(Result result)
- {
- // Your logic here
- }
- });
-
- ...
-
- // Write content.
- byte[] bytes = ...;
- output.write(bytes);
-}
-// End of try-with-resource, output.close() called automatically to signal end of content.
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=outputStreamRequestContent]
----
[[http-client-response-content]]
===== Response Content Handling
-Jetty HTTP client allows applications to handle response content in different ways.
+Jetty's `HttpClient` allows applications to handle response content in different ways.
-The first way is to buffer the response content in memory; this is done when using the blocking APIs (see xref:http-client-blocking[]) and the content is buffered within a `ContentResponse` up to 2 MiB.
+You can buffer the response content in memory; this is done when using the xref:client-http-blocking[blocking APIs] and the content is buffered within a `ContentResponse` up to 2 MiB.
If you want to control the length of the response content (for example limiting to values smaller than the default of 2 MiB), then you can use a `org.eclipse.jetty.client.util.FutureResponseListener` in this way:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-Request request = httpClient.newRequest("http://domain.com/path");
-
-// Limit response content buffer to 512 KiB
-FutureResponseListener listener = new FutureResponseListener(request, 512 * 1024);
-
-request.send(listener);
-
-ContentResponse response = listener.get(5, TimeUnit.SECONDS);
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=futureResponseListener]
----
-If the response content length is exceeded, the response will be aborted, and an exception will be thrown by method `get()`.
+If the response content length is exceeded, the response will be aborted, and an exception will be thrown by method `get(...)`.
-If you are using the asynchronous APIs (see xref:http-client-async[]), you can use the `BufferingResponseListener` utility class:
+You can buffer the response content in memory also using the xref:client-http-non-blocking[non-blocking APIs], via the `BufferingResponseListener` utility class:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-httpClient.newRequest("http://domain.com/path")
- // Buffer response content up to 8 MiB
- .send(new BufferingResponseListener(8 * 1024 * 1024)
- {
- @Override
- public void onComplete(Result result)
- {
- if (!result.isFailed())
- {
- byte[] responseContent = getContent();
- // Your logic here
- }
- }
- });
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=bufferingResponseListener]
----
-The second way is the most efficient (because it avoids content copies) and allows you to specify a `Response.ContentListener`, or a subclass, to handle the content as soon as it arrives.
-In the example below, `Response.Listener.Adapter` is a class that implements both `Response.ContentListener` and `Response.CompleteListener` and can be passed to `Request.send()`.
-Jetty's HTTP client will invoke the `onContent()` method zero or more times (until there is content), and finally invoke the `onComplete()` method.
+If you want to avoid buffering, you can wait for the response and then stream the content using the `InputStreamResponseListener` utility class:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-httpClient .newRequest("http://domain.com/path")
- .send(new Response.Listener.Adapter()
- {
- @Override
- public void onContent(Response response, ByteBuffer buffer)
- {
- // Your logic here
- }
- });
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=inputStreamResponseListener]
----
-The third way allows you to wait for the response and then stream the content using the `InputStreamResponseListener` utility class:
+Finally, let's look at the advanced usage of the response content handling.
-[source, java, subs="{sub-order}"]
+The response content is provided by the `HttpClient` implementation to application
+listeners following a reactive model similar to that of `java.util.concurrent.Flow`.
+
+The listener that follows this model is `Response.DemandedContentListener`.
+
+After the response headers have been processed by the `HttpClient` implementation,
+`Response.DemandedContentListener.onBeforeContent(response, demand)` is
+invoked. This allows the application to control whether to demand the first
+content or not. The default implementation of this method calls `demand.accept(1)`,
+which demands one chunk of content to the implementation.
+The implementation will deliver the chunk of content as soon as it is available.
+
+The chunks of content are delivered to the application by invoking
+`Response.DemandedContentListener.onContent(response, demand, buffer, callback)`.
+Applications implement this method to process the content bytes in the `buffer`.
+Succeeding the `callback` signals to the implementation that the application
+has consumed the `buffer` so that the implementation can dispose/recycle the
+`buffer`. Failing the `callback` signals to the implementation to fail the
+response (no more content will be delivered, and the _response failed_ event
+will be emitted).
+
+IMPORTANT: Succeeding the `callback` must be done only after the `buffer`
+bytes have been consumed. When the `callback` is succeeded, the `HttpClient`
+implementation may reuse the `buffer` and overwrite the bytes with different
+bytes; if the application looks at the `buffer` _after_ having succeeded
+the `callback` is may see other, unrelated, bytes.
+
+The application uses the `demand` object to demand more content chunks.
+Applications will typically demand for just one more content via
+`demand.accept(1)`, but may decide to demand for more via `demand.accept(2)`
+or demand "infinitely" once via `demand.accept(Long.MAX_VALUE)`.
+Applications that demand for more than 1 chunk of content must be prepared
+to receive all the content that they have demanded.
+
+Demanding for content and consuming the content are orthogonal activities.
+
+An application can demand "infinitely" and store aside the pairs
+`(buffer, callback)` to consume them later.
+If not done carefully, this may lead to excessive memory consumption, since
+the ``buffer``s are not consumed.
+Succeeding the ``callback``s will result in the ``buffer``s to be
+disposed/recycled and may be performed at any time.
+
+An application can also demand one chunk of content, consume it (by
+succeeding the associated `callback`) and then _not_ demand for more content
+until a later time.
+
+Subclass `Response.AsyncContentListener` overrides the behavior of
+`Response.DemandedContentListener`; when an application implementing its
+`onContent(response, buffer, callback)` succeeds the `callback`, it
+will have _both_ the effect of disposing/recycling the `buffer` _and_ the
+effect of demanding one more chunk of content.
+
+Subclass `Response.ContentListener` overrides the behavior of
+`Response.AsyncContentListener`; when an application implementing its
+`onContent(response, buffer)` returns from the method itself, it will
+_both_ the effect of disposing/recycling the `buffer` _and_ the effect
+of demanding one more chunk of content.
+
+Previous examples of response content handling were inefficient because they
+involved copying the `buffer` bytes, either to accumulate them aside so that
+the application could use them when the request was completed, or because
+they were provided to an API such as `InputStream` that made use of `byte[]`
+(and therefore a copy from `ByteBuffer` to `byte[]` is necessary).
+
+An application that implements a forwarder between two servers can be
+implemented efficiently by handling the response content without copying
+the `buffer` bytes as in the following example:
+
+[source,java,indent=0]
----
-
-InputStreamResponseListener listener = new InputStreamResponseListener();
-httpClient.newRequest("http://domain.com/path")
- .send(listener);
-
-// Wait for the response headers to arrive
-Response response = listener.get(5, TimeUnit.SECONDS);
-
-// Look at the response
-if (response.getStatus() == HttpStatus.OK_200)
-{
- // Use try-with-resources to close input stream.
- try (InputStream responseContent = listener.getInputStream())
- {
- // Your logic here
- }
-}
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=demandedContentListener]
----
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http.adoc
index 8d0d71a911e..53b5833b1cf 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http.adoc
@@ -20,8 +20,8 @@
=== HTTP Client
include::client-http-intro.adoc[]
-include::client-http-configuration.adoc[]
include::client-http-api.adoc[]
+include::client-http-configuration.adoc[]
include::client-http-cookie.adoc[]
include::client-http-authentication.adoc[]
include::client-http-proxy.adoc[]
diff --git a/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java b/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
index f531283b41a..8549f712e64 100644
--- a/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
+++ b/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
@@ -18,11 +18,40 @@
package embedded.client.http;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.file.Paths;
+import java.util.concurrent.TimeUnit;
+import java.util.function.LongConsumer;
+
import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
+import org.eclipse.jetty.client.util.AsyncRequestContent;
+import org.eclipse.jetty.client.util.BufferingResponseListener;
+import org.eclipse.jetty.client.util.BytesRequestContent;
+import org.eclipse.jetty.client.util.FutureResponseListener;
+import org.eclipse.jetty.client.util.InputStreamRequestContent;
+import org.eclipse.jetty.client.util.InputStreamResponseListener;
+import org.eclipse.jetty.client.util.OutputStreamRequestContent;
+import org.eclipse.jetty.client.util.PathRequestContent;
+import org.eclipse.jetty.client.util.StringRequestContent;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ClientConnector;
+import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.ssl.SslContextFactory;
+import static java.lang.System.Logger.Level.INFO;
+
+@SuppressWarnings("unused")
public class HTTPClientDocs
{
public void start() throws Exception
@@ -62,7 +91,7 @@ public class HTTPClientDocs
// end::tlsExplicit[]
}
- public void tlsNoValidation() throws Exception
+ public void tlsNoValidation()
{
// tag::tlsNoValidation[]
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
@@ -71,7 +100,7 @@ public class HTTPClientDocs
// end::tlsNoValidation[]
}
- public void tlsAppValidation() throws Exception
+ public void tlsAppValidation()
{
// tag::tlsAppValidation[]
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
@@ -79,4 +108,370 @@ public class HTTPClientDocs
sslContextFactory.setHostnameVerifier((hostName, session) -> hostName.endsWith(".domain.com"));
// end::tlsAppValidation[]
}
+
+ public void simpleBlockingGet() throws Exception
+ {
+ // tag::simpleBlockingGet[]
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // Perform a simple GET and wait for the response.
+ ContentResponse response = httpClient.GET("http://domain.com/path?query");
+ // end::simpleBlockingGet[]
+ }
+
+ public void headFluent() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::headFluent[]
+ ContentResponse response = httpClient.newRequest("http://domain.com/path?query")
+ .method(HttpMethod.HEAD)
+ .agent("Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:17.0) Gecko/20100101 Firefox/17.0")
+ .send();
+ // end::headFluent[]
+ }
+
+ public void headNonFluent() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::headNonFluent[]
+ Request request = httpClient.newRequest("http://domain.com/path?query");
+ request.method(HttpMethod.HEAD);
+ request.agent("Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:17.0) Gecko/20100101 Firefox/17.0");
+ ContentResponse response = request.send();
+ // end::headNonFluent[]
+ }
+
+ public void postFluent() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::postFluent[]
+ ContentResponse response = httpClient.POST("http://domain.com/entity/1")
+ .param("p", "value")
+ .send();
+ // end::postFluent[]
+ }
+
+ public void fileFluent() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::fileFluent[]
+ ContentResponse response = httpClient.POST("http://domain.com/upload")
+ .file(Paths.get("file_to_upload.txt"), "text/plain")
+ .send();
+ // end::fileFluent[]
+ }
+
+ public void totalTimeout() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::totalTimeout[]
+ ContentResponse response = httpClient.newRequest("http://domain.com/path?query")
+ .timeout(5, TimeUnit.SECONDS)
+ .send();
+ // end::totalTimeout[]
+ }
+
+ public void simpleNonBlocking() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::simpleNonBlocking[]
+ httpClient.newRequest("http://domain.com/path")
+ .send(result ->
+ {
+ // Your logic here
+ });
+ // end::simpleNonBlocking[]
+ }
+
+ public void nonBlockingTotalTimeout() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::nonBlockingTotalTimeout[]
+ httpClient.newRequest("http://domain.com/path")
+ .timeout(3, TimeUnit.SECONDS)
+ .send(result ->
+ {
+ /* Your logic here */
+ });
+ // end::nonBlockingTotalTimeout[]
+ }
+
+ // @checkstyle-disable-check : LeftCurly
+ public void listeners() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::listeners[]
+ httpClient.newRequest("http://domain.com/path")
+ // Add request hooks.
+ .onRequestQueued(request -> { /* ... */ })
+ .onRequestBegin(request -> { /* ... */ })
+ .onRequestHeaders(request -> { /* ... */ })
+ .onRequestCommit(request -> { /* ... */ })
+ .onRequestContent((request, content) -> { /* ... */ })
+ .onRequestFailure((request, failure) -> { /* ... */ })
+ .onRequestSuccess(request -> { /* ... */ })
+ // Add response hooks.
+ .onResponseBegin(response -> { /* ... */ })
+ .onResponseHeader((response, field) -> true)
+ .onResponseHeaders(response -> { /* ... */ })
+ .onResponseContentAsync((response, buffer, callback) -> callback.succeeded())
+ .onResponseFailure((response, failure) -> { /* ... */ })
+ .onResponseSuccess(response -> { /* ... */ })
+ // Result hook.
+ .send(result -> { /* ... */ });
+ // end::listeners[]
+ }
+
+ public void pathRequestContent() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::pathRequestContent[]
+ ContentResponse response = httpClient.POST("http://domain.com/upload")
+ .body(new PathRequestContent("text/plain", Paths.get("file_to_upload.txt")))
+ .send();
+ // end::pathRequestContent[]
+ }
+
+ public void inputStreamRequestContent() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::inputStreamRequestContent[]
+ ContentResponse response = httpClient.POST("http://domain.com/upload")
+ .body(new InputStreamRequestContent("text/plain", new FileInputStream("file_to_upload.txt")))
+ .send();
+ // end::inputStreamRequestContent[]
+ }
+
+ public void bytesStringRequestContent() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ byte[] bytes = new byte[1024];
+ String string = new String(bytes);
+ // tag::bytesStringRequestContent[]
+ ContentResponse bytesResponse = httpClient.POST("http://domain.com/upload")
+ .body(new BytesRequestContent("text/plain", bytes))
+ .send();
+
+ ContentResponse stringResponse = httpClient.POST("http://domain.com/upload")
+ .body(new StringRequestContent("text/plain", string))
+ .send();
+ // end::bytesStringRequestContent[]
+ }
+
+ public void asyncRequestContent() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::asyncRequestContent[]
+ AsyncRequestContent content = new AsyncRequestContent();
+ httpClient.POST("http://domain.com/upload")
+ .body(content)
+ .send(result ->
+ {
+ // Your logic here
+ });
+
+ // Content not available yet here.
+
+ // An event happens in some other class, in some other thread.
+ class ContentPublisher
+ {
+ void publish(ByteBufferPool bufferPool, byte[] bytes, boolean lastContent)
+ {
+ // Wrap the bytes into a new ByteBuffer.
+ ByteBuffer buffer = ByteBuffer.wrap(bytes);
+
+ // Offer the content, and release the ByteBuffer
+ // to the pool when the Callback is completed.
+ content.offer(buffer, Callback.from(() -> bufferPool.release(buffer)));
+
+ // Close AsyncRequestContent when all the content is arrived.
+ if (lastContent)
+ content.close();
+ }
+ }
+ // end::asyncRequestContent[]
+ }
+
+ public void outputStreamRequestContent() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::outputStreamRequestContent[]
+ OutputStreamRequestContent content = new OutputStreamRequestContent();
+
+ // Use try-with-resources to close the OutputStream when all content is written.
+ try (OutputStream output = content.getOutputStream())
+ {
+ httpClient.POST("http://localhost:8080/")
+ .body(content)
+ .send(result ->
+ {
+ // Your logic here
+ });
+
+ // Content not available yet here.
+
+ // Content is now available.
+ byte[] bytes = new byte[]{'h', 'e', 'l', 'l', 'o'};
+ output.write(bytes);
+ }
+ // End of try-with-resource, output.close() called automatically to signal end of content.
+ // end::outputStreamRequestContent[]
+ }
+
+ public void futureResponseListener() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::futureResponseListener[]
+ Request request = httpClient.newRequest("http://domain.com/path");
+
+ // Limit response content buffer to 512 KiB.
+ FutureResponseListener listener = new FutureResponseListener(request, 512 * 1024);
+
+ request.send(listener);
+
+ // Wait at most 5 seconds for request+response to complete.
+ ContentResponse response = listener.get(5, TimeUnit.SECONDS);
+ // end::futureResponseListener[]
+ }
+
+ public void bufferingResponseListener() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::bufferingResponseListener[]
+ httpClient.newRequest("http://domain.com/path")
+ // Buffer response content up to 8 MiB
+ .send(new BufferingResponseListener(8 * 1024 * 1024)
+ {
+ @Override
+ public void onComplete(Result result)
+ {
+ if (!result.isFailed())
+ {
+ byte[] responseContent = getContent();
+ // Your logic here
+ }
+ }
+ });
+ // end::bufferingResponseListener[]
+ }
+
+ public void inputStreamResponseListener() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::inputStreamResponseListener[]
+ InputStreamResponseListener listener = new InputStreamResponseListener();
+ httpClient.newRequest("http://domain.com/path")
+ .send(listener);
+
+ // Wait for the response headers to arrive.
+ Response response = listener.get(5, TimeUnit.SECONDS);
+
+ // Look at the response before streaming the content.
+ if (response.getStatus() == HttpStatus.OK_200)
+ {
+ // Use try-with-resources to close input stream.
+ try (InputStream responseContent = listener.getInputStream())
+ {
+ // Your logic here
+ }
+ }
+ else
+ {
+ response.abort(new IOException("Unexpected HTTP response"));
+ }
+ // end::inputStreamResponseListener[]
+ }
+
+ public void demandedContentListener() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ String host1 = "localhost";
+ String host2 = "localhost";
+ int port1 = 8080;
+ int port2 = 8080;
+ // tag::demandedContentListener[]
+ // Prepare a request to server1, the source.
+ Request request1 = httpClient.newRequest(host1, port1)
+ .path("/source");
+
+ // Prepare a request to server2, the sink.
+ AsyncRequestContent content2 = new AsyncRequestContent();
+ Request request2 = httpClient.newRequest(host2, port2)
+ .path("/sink")
+ .body(content2);
+
+ request1.onResponseContentDemanded(new Response.DemandedContentListener()
+ {
+ @Override
+ public void onBeforeContent(Response response, LongConsumer demand)
+ {
+ request2.onRequestCommit(request ->
+ {
+ // Only when the request to server2 has been sent,
+ // then demand response content from server1.
+ demand.accept(1);
+ });
+
+ // Send the request to server2.
+ request2.send(result -> System.getLogger("forwarder").log(INFO, "Forwarding to server2 complete"));
+ }
+
+ @Override
+ public void onContent(Response response, LongConsumer demand, ByteBuffer content, Callback callback)
+ {
+ // When response content is received from server1, forward it to server2.
+ content2.offer(content, Callback.from(() ->
+ {
+ // When the request content to server2 is sent,
+ // succeed the callback to recycle the buffer.
+ callback.succeeded();
+ // Then demand more response content from server1.
+ demand.accept(1);
+ }, callback::failed));
+ }
+ });
+
+ // When the response content from server1 is complete,
+ // complete also the request content to server2.
+ request1.onResponseSuccess(response -> content2.close());
+
+ // Send the request to server1.
+ request1.send(result -> System.getLogger("forwarder").log(INFO, "Sourcing from server1 complete"));
+ // end::demandedContentListener[]
+ }
}
From ec38e99630d83b737499a046ac0f06fe6b1ccc6a Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Wed, 1 Apr 2020 10:06:35 +0200
Subject: [PATCH 023/101] Fixed handling of ClientConnector promises.
Signed-off-by: Simone Bordet
---
.../jetty/client/AbstractConnectorHttpClientTransport.java | 2 +-
.../main/java/org/eclipse/jetty/http2/client/HTTP2Client.java | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractConnectorHttpClientTransport.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractConnectorHttpClientTransport.java
index 129cdcb3748..d0b41f16f88 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractConnectorHttpClientTransport.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractConnectorHttpClientTransport.java
@@ -72,7 +72,7 @@ public abstract class AbstractConnectorHttpClientTransport extends AbstractHttpC
context.put(ClientConnector.CLIENT_CONNECTION_FACTORY_CONTEXT_KEY, destination.getClientConnectionFactory());
@SuppressWarnings("unchecked")
Promise promise = (Promise)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
- context.put(ClientConnector.CONNECTION_PROMISE_CONTEXT_KEY, promise);
+ context.put(ClientConnector.CONNECTION_PROMISE_CONTEXT_KEY, Promise.from(ioConnection -> {}, promise::failed));
connector.connect(address, context);
}
}
diff --git a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java
index 8ea720523c3..fd45d8c934f 100644
--- a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java
+++ b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java
@@ -359,7 +359,7 @@ public class HTTP2Client extends ContainerLifeCycle
public void connect(SocketAddress address, ClientConnectionFactory factory, Session.Listener listener, Promise promise, Map context)
{
context = contextFrom(factory, listener, promise, context);
- context.put(ClientConnector.CONNECTION_PROMISE_CONTEXT_KEY, promise);
+ context.put(ClientConnector.CONNECTION_PROMISE_CONTEXT_KEY, Promise.from(ioConnection -> {}, promise::failed));
connector.connect(address, context);
}
@@ -372,7 +372,7 @@ public class HTTP2Client extends ContainerLifeCycle
public void accept(SocketChannel channel, ClientConnectionFactory factory, Session.Listener listener, Promise promise)
{
Map context = contextFrom(factory, listener, promise, null);
- context.put(ClientConnector.CONNECTION_PROMISE_CONTEXT_KEY, promise);
+ context.put(ClientConnector.CONNECTION_PROMISE_CONTEXT_KEY, Promise.from(ioConnection -> {}, promise::failed));
connector.accept(channel, context);
}
From 5edff904feef6405ee97c0bddfaa37e722e580f0 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Wed, 1 Apr 2020 15:15:45 +0200
Subject: [PATCH 024/101] Fixes #4735 - Get env variables in PHP scripts served
through FastCGIProxyServlet.
Introduced fastCGI.envNames to specify a comma separated list of
environment variable names that are forwarded to the PHP process.
Signed-off-by: Simone Bordet
---
jetty-fcgi/fcgi-server/pom.xml | 7 ++----
.../server/proxy/FastCGIProxyServlet.java | 25 ++++++++++++++++++-
2 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/jetty-fcgi/fcgi-server/pom.xml b/jetty-fcgi/fcgi-server/pom.xml
index 3fea6cd08de..add0f2a11d0 100644
--- a/jetty-fcgi/fcgi-server/pom.xml
+++ b/jetty-fcgi/fcgi-server/pom.xml
@@ -14,10 +14,6 @@
${project.groupId}.server
-
-
-
-
javax.servlet
@@ -38,6 +34,7 @@
jetty-server${project.version}
+
org.eclipse.jettyjetty-servlet
@@ -52,7 +49,7 @@
org.eclipse.jetty
- jetty-alpn-server
+ jetty-alpn-java-server${project.version}test
diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java
index fe3c0e19171..6422245d95e 100644
--- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java
+++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java
@@ -19,11 +19,14 @@
package org.eclipse.jetty.fcgi.server.proxy;
import java.net.URI;
+import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
@@ -48,7 +51,7 @@ import org.eclipse.jetty.util.ProcessorUtils;
* that is sent to the FastCGI server specified in the {@code proxyTo}
* init-param.
*
- * This servlet accepts two additional init-params:
+ * This servlet accepts these additional {@code init-param}s:
*
*
{@code scriptRoot}, mandatory, that must be set to the directory where
* the application that must be served via FastCGI is installed and corresponds to
@@ -62,6 +65,8 @@ import org.eclipse.jetty.util.ProcessorUtils;
*
*
{@code fastCGI.HTTPS}, optional, defaults to false, that specifies whether
* to force the FastCGI {@code HTTPS} parameter to the value {@code on}
+ *
{@code fastCGI.envNames}, optional, a comma separated list of environment variable
+ * names read via {@link System#getenv(String)} that are forwarded as FastCGI parameters.
*
*
* @see TryFilesFilter
@@ -73,6 +78,7 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
public static final String ORIGINAL_URI_ATTRIBUTE_INIT_PARAM = "originalURIAttribute";
public static final String ORIGINAL_QUERY_ATTRIBUTE_INIT_PARAM = "originalQueryAttribute";
public static final String FASTCGI_HTTPS_INIT_PARAM = "fastCGI.HTTPS";
+ public static final String FASTCGI_ENV_NAMES_INIT_PARAM = "fastCGI.envNames";
private static final String REMOTE_ADDR_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".remoteAddr";
private static final String REMOTE_PORT_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".remotePort";
@@ -87,6 +93,7 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
private String originalURIAttribute;
private String originalQueryAttribute;
private boolean fcgiHTTPS;
+ private Set fcgiEnvNames;
@Override
public void init() throws ServletException
@@ -102,6 +109,15 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
originalQueryAttribute = getInitParameter(ORIGINAL_QUERY_ATTRIBUTE_INIT_PARAM);
fcgiHTTPS = Boolean.parseBoolean(getInitParameter(FASTCGI_HTTPS_INIT_PARAM));
+
+ fcgiEnvNames = Collections.emptySet();
+ String envNames = getInitParameter(FASTCGI_ENV_NAMES_INIT_PARAM);
+ if (envNames != null)
+ {
+ fcgiEnvNames = Stream.of(envNames.split(","))
+ .map(String::trim)
+ .collect(Collectors.toSet());
+ }
}
@Override
@@ -195,6 +211,13 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
protected void customizeFastCGIHeaders(Request proxyRequest, HttpFields fastCGIHeaders)
{
+ for (String envName : fcgiEnvNames)
+ {
+ String envValue = System.getenv(envName);
+ if (envValue != null)
+ fastCGIHeaders.put(envName, envValue);
+ }
+
fastCGIHeaders.remove("HTTP_PROXY");
fastCGIHeaders.put(FCGI.Headers.REMOTE_ADDR, (String)proxyRequest.getAttributes().get(REMOTE_ADDR_ATTRIBUTE));
From af895966fa05e4422288391125f470ddb77f6e37 Mon Sep 17 00:00:00 2001
From: Greg Wilkins
Date: Wed, 1 Apr 2020 17:55:33 +0200
Subject: [PATCH 025/101] Issue #4711 trailers
Standard trailers cannot be set if committed or HTTP/1.0
Signed-off-by: Greg Wilkins
---
.../src/main/java/org/eclipse/jetty/server/Response.java | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
index e023a1f1c78..4a8eea246a9 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
@@ -1229,7 +1229,10 @@ public class Response implements HttpServletResponse
@Override
public void setTrailerFields(Supplier
----
-
-==== Configuring Form Limits for the Server
-
-If a context does not have specific form limits configured, then the server attributes are inspected to see if a server wide limit has been set on the size or keys.
-The following XML shows how these attributes can be set in `jetty.xml`:
-
-[source, xml, subs="{sub-order}"]
-----
-
-
- ...
-
-
- org.eclipse.jetty.server.Request.maxFormContentSize
- 100000
-
-
- org.eclipse.jetty.server.Request.maxFormKeys
- 2000
-
-
-
-----
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
index 9d8d03a73a4..764cc78a5f8 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
@@ -93,19 +93,24 @@ import org.eclipse.jetty.util.resource.Resource;
/**
* ContextHandler.
*
+ *
* This handler wraps a call to handle by setting the context and servlet path, plus setting the context classloader.
- *
+ *
*
- * If the context init parameter "org.eclipse.jetty.server.context.ManagedAttributes" is set to a comma separated list of names, then they are treated as
+ * If the context init parameter {@code org.eclipse.jetty.server.context.ManagedAttributes} is set to a comma separated list of names, then they are treated as
* context attribute names, which if set as attributes are passed to the servers Container so that they may be managed with JMX.
+ *
*
- * The maximum size of a form that can be processed by this context is controlled by the system properties org.eclipse.jetty.server.Request.maxFormKeys and
- * org.eclipse.jetty.server.Request.maxFormContentSize. These can also be configured with {@link #setMaxFormContentSize(int)} and {@link #setMaxFormKeys(int)}
+ * The maximum size of a form that can be processed by this context is controlled by the system properties {@code org.eclipse.jetty.server.Request.maxFormKeys} and
+ * {@code org.eclipse.jetty.server.Request.maxFormContentSize}. These can also be configured with {@link #setMaxFormContentSize(int)} and {@link #setMaxFormKeys(int)}
+ *
*
- * This servers executor is made available via a context attributed "org.eclipse.jetty.server.Executor".
+ * The executor is made available via a context attributed {@code org.eclipse.jetty.server.Executor}.
+ *
*
* By default, the context is created with alias checkers for {@link AllowSymLinkAliasChecker} (unix only) and {@link ApproveNonExistentDirectoryAliases}. If
* these alias checkers are not required, then {@link #clearAliasChecks()} or {@link #setAliasChecks(List)} should be called.
+ *
*/
@ManagedObject("URI Context")
public class ContextHandler extends ScopedHandler implements Attributes, Graceful
From ea06ba18656accd25ccc2d8be283dd5b3696b351 Mon Sep 17 00:00:00 2001
From: Olivier Lamy
Date: Thu, 2 Apr 2020 08:06:10 +1000
Subject: [PATCH 028/101] Issue #4699 fix jar files from multimodules not
scanned (#4742)
* Issue #4699 fix jar files from multimodules not scanned
Signed-off-by: olivier lamy
* fix license headers
Signed-off-by: olivier lamy
---
.../MyLibrary/pom.xml | 21 ++++
.../main/java/jettyissue/MyAnnotation.java | 29 +++++
.../MyServletContainerInitializer.java | 32 +++++
.../javax.servlet.ServletContainerInitializer | 1 +
.../MyWebApp/pom.xml | 112 ++++++++++++++++++
.../MyWebApp/src/config/context.xml | 7 ++
.../MyWebApp/src/config/jetty.xml | 39 ++++++
.../src/main/java/jettyissue/NormalClass.java | 24 ++++
.../MyWebApp/src/main/webapp/index.html | 10 ++
.../invoker.properties | 1 +
.../src/it/jetty-run-mojo-jar-scan-it/pom.xml | 32 +++++
.../postbuild.groovy | 3 +
.../jetty/maven/plugin/JettyRunMojo.java | 12 +-
13 files changed, 312 insertions(+), 11 deletions(-)
create mode 100644 jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/pom.xml
create mode 100644 jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyAnnotation.java
create mode 100644 jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyServletContainerInitializer.java
create mode 100644 jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer
create mode 100644 jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/pom.xml
create mode 100644 jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/context.xml
create mode 100644 jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/jetty.xml
create mode 100644 jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/java/jettyissue/NormalClass.java
create mode 100644 jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/webapp/index.html
create mode 100644 jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/invoker.properties
create mode 100644 jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/pom.xml
create mode 100644 jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/postbuild.groovy
diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/pom.xml b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/pom.xml
new file mode 100644
index 00000000000..34b03a21e11
--- /dev/null
+++ b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/pom.xml
@@ -0,0 +1,21 @@
+
+
+
+ jetty-issue
+ org.mehdi
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ MyLibrary
+
+
+
+ javax.servlet
+ javax.servlet-api
+ provided
+
+
+
diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyAnnotation.java b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyAnnotation.java
new file mode 100644
index 00000000000..ae234730a87
--- /dev/null
+++ b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyAnnotation.java
@@ -0,0 +1,29 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package jettyissue;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface MyAnnotation {
+}
diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyServletContainerInitializer.java b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyServletContainerInitializer.java
new file mode 100644
index 00000000000..bcab61c875d
--- /dev/null
+++ b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyServletContainerInitializer.java
@@ -0,0 +1,32 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package jettyissue;
+
+import javax.servlet.ServletContainerInitializer;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.HandlesTypes;
+import java.util.Set;
+
+@HandlesTypes({MyAnnotation.class})
+public class MyServletContainerInitializer implements ServletContainerInitializer {
+ public void onStartup(Set> c, ServletContext ctx) throws ServletException {
+ System.out.println("STARTED"+c);
+ }
+}
diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer
new file mode 100644
index 00000000000..9e9784f1616
--- /dev/null
+++ b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer
@@ -0,0 +1 @@
+jettyissue.MyServletContainerInitializer
diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/pom.xml b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/pom.xml
new file mode 100644
index 00000000000..f09564cf1a0
--- /dev/null
+++ b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/pom.xml
@@ -0,0 +1,112 @@
+
+
+
+ jetty-issue
+ org.mehdi
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ MyWebApp
+ jar
+
+
+ ${project.build.directory}/jetty-run-mojo.txt
+
+
+
+
+ javax.servlet
+ javax.servlet-api
+ provided
+
+
+ org.mehdi
+ MyLibrary
+
+
+
+ org.eclipse.jetty
+ jetty-client
+ test
+
+
+ org.apache.commons
+ commons-lang3
+ test
+
+
+ org.eclipse.jetty
+ jetty-maven-plugin
+ tests
+ test-jar
+ test
+
+
+ *
+ *
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ ${jetty.port.file}
+ /setbycontextxml
+ false
+ false
+ false
+ ${project.groupId}:${project.artifactId}
+
+
+ org.eclipse.jetty:jetty-maven-plugin
+
+
+
+
+ org.eclipse.jetty
+ jetty-maven-plugin
+
+
+ start-jetty
+ test-compile
+
+ start
+
+
+
+
+ jetty.port.file
+ ${jetty.port.file}
+
+
+ true
+ ${basedir}/src/config/jetty.xml
+ ${basedir}/src/config/context.xml
+ true
+
+ jar
+
+
+
+
+
+
+
+
+
diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/context.xml b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/context.xml
new file mode 100644
index 00000000000..3eb5570a37d
--- /dev/null
+++ b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/context.xml
@@ -0,0 +1,7 @@
+
+
+
+
+ /setbycontextxml
+
+
diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/jetty.xml b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/jetty.xml
new file mode 100644
index 00000000000..9193b42df99
--- /dev/null
+++ b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/jetty.xml
@@ -0,0 +1,39 @@
+
+
+
+
+ https
+
+ 32768
+ 8192
+ 8192
+ 1024
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 30000
+
+
+
+
diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/java/jettyissue/NormalClass.java b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/java/jettyissue/NormalClass.java
new file mode 100644
index 00000000000..9cea88de8a8
--- /dev/null
+++ b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/java/jettyissue/NormalClass.java
@@ -0,0 +1,24 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package jettyissue;
+
+
+@MyAnnotation
+public class NormalClass {
+}
diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/webapp/index.html b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/webapp/index.html
new file mode 100644
index 00000000000..b7b5cdc61de
--- /dev/null
+++ b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/webapp/index.html
@@ -0,0 +1,10 @@
+
+
+
+
+ Title
+
+
+ Hello World!
+
+
diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/invoker.properties b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/invoker.properties
new file mode 100644
index 00000000000..e0222d4d54e
--- /dev/null
+++ b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/invoker.properties
@@ -0,0 +1 @@
+invoker.goals = test
\ No newline at end of file
diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/pom.xml b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/pom.xml
new file mode 100644
index 00000000000..1380e37256e
--- /dev/null
+++ b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/pom.xml
@@ -0,0 +1,32 @@
+
+
+ 4.0.0
+
+ org.eclipse.jetty.its
+ it-parent-pom
+ 0.0.1-SNAPSHOT
+
+
+
+ org.mehdi
+ jetty-issue
+ pom
+ 1.0-SNAPSHOT
+
+ MyLibrary
+ MyWebApp
+
+
+
+
+
+ org.mehdi
+ MyLibrary
+ ${project.version}
+
+
+
+
+
diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/postbuild.groovy b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/postbuild.groovy
new file mode 100644
index 00000000000..87a9e7b26cc
--- /dev/null
+++ b/jetty-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/postbuild.groovy
@@ -0,0 +1,3 @@
+File buildLog = new File( basedir, 'build.log' )
+assert buildLog.text.contains( 'Started Jetty Server' )
+assert buildLog.text.contains( 'STARTED[class jettyissue.NormalClass]')
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java
index 39b5a43bf6b..a826dc23c17 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java
@@ -257,7 +257,7 @@ public class JettyRunMojo extends AbstractJettyMojo
webApp.setTestClasses(testClassesDirectory);
MavenProjectHelper mavenProjectHelper = new MavenProjectHelper(project);
- List webInfLibs = getWebInfLibArtifacts(project).stream()
+ List webInfLibs = getWebInfLibArtifacts(project.getArtifacts()).stream()
.map(a ->
{
Path p = mavenProjectHelper.getArtifactPath(a);
@@ -479,16 +479,6 @@ public class JettyRunMojo extends AbstractJettyMojo
.collect(Collectors.toList());
}
- private Collection getWebInfLibArtifacts(MavenProject mavenProject)
- {
- String type = mavenProject.getArtifact().getType();
- if (!"war".equalsIgnoreCase(type) && !"zip".equalsIgnoreCase(type))
- {
- return Collections.emptyList();
- }
- return getWebInfLibArtifacts(mavenProject.getArtifacts());
- }
-
private boolean canPutArtifactInWebInfLib(Artifact artifact)
{
if ("war".equalsIgnoreCase(artifact.getType()))
From 5c38ae3549c2706329036402c4a016ae7775bfb4 Mon Sep 17 00:00:00 2001
From: olivier lamy
Date: Tue, 31 Mar 2020 12:53:49 +1000
Subject: [PATCH 029/101] Issue #4727 setConfigurationDiscovered true per
default
Signed-off-by: olivier lamy
---
.../jetty/annotations/TestAnnotationConfiguration.java | 7 ++++---
.../main/java/org/eclipse/jetty/webapp/WebAppContext.java | 2 +-
.../org/eclipse/jetty/webapp/MetaInfConfigurationTest.java | 6 +++---
3 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java
index 13753551f7c..2e169e62266 100644
--- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java
+++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java
@@ -118,25 +118,25 @@ public class TestAnnotationConfiguration
@Test
public void testAnnotationScanControl() throws Exception
{
- //check that a 2.5 webapp won't discover annotations
+ //check that a 2.5 webapp with configurationDiscovered will discover annotations
TestableAnnotationConfiguration config25 = new TestableAnnotationConfiguration();
WebAppContext context25 = new WebAppContext();
context25.setClassLoader(Thread.currentThread().getContextClassLoader());
context25.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE);
context25.setAttribute(AnnotationConfiguration.MAX_SCAN_WAIT, 0);
+ context25.setConfigurationDiscovered(false);
context25.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web25)));
context25.getServletContext().setEffectiveMajorVersion(2);
context25.getServletContext().setEffectiveMinorVersion(5);
config25.configure(context25);
config25.assertAnnotationDiscovery(false);
- //check that a 2.5 webapp with configurationDiscovered will discover annotations
+ //check that a 2.5 webapp discover annotations
TestableAnnotationConfiguration config25b = new TestableAnnotationConfiguration();
WebAppContext context25b = new WebAppContext();
context25b.setClassLoader(Thread.currentThread().getContextClassLoader());
context25b.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE);
context25b.setAttribute(AnnotationConfiguration.MAX_SCAN_WAIT, 0);
- context25b.setConfigurationDiscovered(true);
context25b.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web25)));
context25b.getServletContext().setEffectiveMajorVersion(2);
context25b.getServletContext().setEffectiveMinorVersion(5);
@@ -293,6 +293,7 @@ public class TestAnnotationConfiguration
AnnotationConfiguration config = new AnnotationConfiguration();
WebAppContext context = new WebAppContext();
List scis;
+ context.setConfigurationDiscovered(false);
context.setClassLoader(webAppLoader);
context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web25)));
context.getMetaData().setWebInfClassesResources(classes);
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
index 5b55d08712f..284d1332247 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
@@ -209,7 +209,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
private Map _resourceAliases;
private boolean _ownClassLoader = false;
- private boolean _configurationDiscovered = false;
+ private boolean _configurationDiscovered = true;
private boolean _allowDuplicateFragmentNames = false;
private boolean _throwUnavailableOnStartupException = false;
diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/MetaInfConfigurationTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/MetaInfConfigurationTest.java
index 599872a30d6..c28aa703713 100644
--- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/MetaInfConfigurationTest.java
+++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/MetaInfConfigurationTest.java
@@ -84,20 +84,20 @@ public class MetaInfConfigurationTest
File web31 = MavenTestingUtils.getTestResourceFile("web31.xml");
File web31false = MavenTestingUtils.getTestResourceFile("web31false.xml");
- //test a 2.5 webapp will not look for fragments by default
+ //test a 2.5 webapp will not look for fragments as manually configured
MetaInfConfiguration meta25 = new TestableMetaInfConfiguration(MetaInfConfiguration.__allScanTypes,
Arrays.asList(MetaInfConfiguration.METAINF_TLDS, MetaInfConfiguration.METAINF_RESOURCES));
WebAppContext context25 = new WebAppContext();
+ context25.setConfigurationDiscovered(false);
context25.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web25)));
context25.getServletContext().setEffectiveMajorVersion(2);
context25.getServletContext().setEffectiveMinorVersion(5);
meta25.preConfigure(context25);
- //test a 2.5 webapp will look for fragments if configurationDiscovered==true
+ //test a 2.5 webapp will look for fragments as configurationDiscovered default true
MetaInfConfiguration meta25b = new TestableMetaInfConfiguration(MetaInfConfiguration.__allScanTypes,
MetaInfConfiguration.__allScanTypes);
WebAppContext context25b = new WebAppContext();
- context25b.setConfigurationDiscovered(true);
context25b.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web25)));
context25b.getServletContext().setEffectiveMajorVersion(2);
context25b.getServletContext().setEffectiveMinorVersion(5);
From e3bab289241f9db54bd498087b6e46762fb505c2 Mon Sep 17 00:00:00 2001
From: olivier lamy
Date: Wed, 1 Apr 2020 12:07:48 +1000
Subject: [PATCH 030/101] document new default behaviour with annotations
scanning
Signed-off-by: olivier lamy
---
.../distribution-guide/annotations/using-annotations.adoc | 5 ++---
.../quickstart-guide/upgrading/upgrading-9.4-to.10.0.adoc | 7 ++++++-
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/jetty-documentation/src/main/asciidoc/distribution-guide/annotations/using-annotations.adoc b/jetty-documentation/src/main/asciidoc/distribution-guide/annotations/using-annotations.adoc
index 93eb15c82ef..765bb61a22a 100644
--- a/jetty-documentation/src/main/asciidoc/distribution-guide/annotations/using-annotations.adoc
+++ b/jetty-documentation/src/main/asciidoc/distribution-guide/annotations/using-annotations.adoc
@@ -121,9 +121,8 @@ As is the case with annotation scanning, the link:#using-extra-classpath-method[
____
[NOTE]
-As of Jetty-9.4.4, unless the `web.xml` is version 3.0 or greater, only `ServletContainerInitializers` that are on the container classpath will be discovered.
-Users wishing to use `ServletContainerInitializers` from within the webapp with older versions of `web.xml` must either upgrade their `web.xml` version, or call `WebAppContext.setConfigurationDiscovered(true)` either programmatically or in xml.
-Upgrading the `web.xml` version is preferable.
+As of Jetty 10, Annotations will be discovered even for old versions of `web.xml` (2.5).
+Users wishing to not use `ServletContainerInitializers` from within the webapp with older versions of `web.xml` must call `WebAppContext.setConfigurationDiscovered(false)` either programmatically or in xml.
____
===== Controlling the order of ServletContainerInitializer invocation
diff --git a/jetty-documentation/src/main/asciidoc/quickstart-guide/upgrading/upgrading-9.4-to.10.0.adoc b/jetty-documentation/src/main/asciidoc/quickstart-guide/upgrading/upgrading-9.4-to.10.0.adoc
index 0b3653c10c9..fb0da833c5b 100644
--- a/jetty-documentation/src/main/asciidoc/quickstart-guide/upgrading/upgrading-9.4-to.10.0.adoc
+++ b/jetty-documentation/src/main/asciidoc/quickstart-guide/upgrading/upgrading-9.4-to.10.0.adoc
@@ -23,9 +23,14 @@ It is not comprehensive, but covers many of the major changes included in the re
==== Required Java Version
-Jetty 10 requires, at a minimum, Java 9 to function.
+Jetty 10 requires, at a minimum, Java 11 to function.
Items such as the Java Platform Module System (JPMS), which Jetty 10 supports, are not available in earlier versions of Java.
+==== ServletContainerInitializers
+
+As of Jetty 10, Annotations will be discovered even for old versions of `web.xml` (2.5).
+Users wishing to not use `ServletContainerInitializers` from within the webapp with older versions of `web.xml` must call `WebAppContext.setConfigurationDiscovered(false)` either programmatically or in xml.
+
==== Removed Classes
//TODO - Insert major removed/refactored classes from Jetty-9.x.x to Jetty-10.0.x
From f14560a22803d1b1ad66072a027564e821c891eb Mon Sep 17 00:00:00 2001
From: olivier lamy
Date: Wed, 1 Apr 2020 22:11:34 +1000
Subject: [PATCH 031/101] fix documentation
Signed-off-by: olivier lamy
---
.../distribution-guide/annotations/using-annotations.adoc | 2 +-
.../quickstart-guide/upgrading/upgrading-9.4-to.10.0.adoc | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/jetty-documentation/src/main/asciidoc/distribution-guide/annotations/using-annotations.adoc b/jetty-documentation/src/main/asciidoc/distribution-guide/annotations/using-annotations.adoc
index 765bb61a22a..091a68dd4b0 100644
--- a/jetty-documentation/src/main/asciidoc/distribution-guide/annotations/using-annotations.adoc
+++ b/jetty-documentation/src/main/asciidoc/distribution-guide/annotations/using-annotations.adoc
@@ -122,7 +122,7 @@ As is the case with annotation scanning, the link:#using-extra-classpath-method[
____
[NOTE]
As of Jetty 10, Annotations will be discovered even for old versions of `web.xml` (2.5).
-Users wishing to not use `ServletContainerInitializers` from within the webapp with older versions of `web.xml` must call `WebAppContext.setConfigurationDiscovered(false)` either programmatically or in xml.
+Users wishing not to use annotations from the webapp classpath must call `WebAppContext.setConfigurationDiscovered(false)` either programmatically or in xml.
____
===== Controlling the order of ServletContainerInitializer invocation
diff --git a/jetty-documentation/src/main/asciidoc/quickstart-guide/upgrading/upgrading-9.4-to.10.0.adoc b/jetty-documentation/src/main/asciidoc/quickstart-guide/upgrading/upgrading-9.4-to.10.0.adoc
index fb0da833c5b..7b78916a4d2 100644
--- a/jetty-documentation/src/main/asciidoc/quickstart-guide/upgrading/upgrading-9.4-to.10.0.adoc
+++ b/jetty-documentation/src/main/asciidoc/quickstart-guide/upgrading/upgrading-9.4-to.10.0.adoc
@@ -29,7 +29,7 @@ Items such as the Java Platform Module System (JPMS), which Jetty 10 supports, a
==== ServletContainerInitializers
As of Jetty 10, Annotations will be discovered even for old versions of `web.xml` (2.5).
-Users wishing to not use `ServletContainerInitializers` from within the webapp with older versions of `web.xml` must call `WebAppContext.setConfigurationDiscovered(false)` either programmatically or in xml.
+Users wishing not to use annotations from the webapp classpath with older versions of `web.xml` must call `WebAppContext.setConfigurationDiscovered(false)` either programmatically or in xml.
==== Removed Classes
From 512f31b3af45127d46c710fdadf60d2c22475a79 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Thu, 2 Apr 2020 10:39:23 +0200
Subject: [PATCH 032/101] Improvements to the Jetty client documentation,
cookies section.
Signed-off-by: Simone Bordet
---
jetty-documentation/pom.xml | 4 +
.../client/http/client-http-cookie.adoc | 76 +++++++---------
.../client/http/client-http-intro.adoc | 12 +++
.../embedded/client/http/HTTPClientDocs.java | 89 +++++++++++++++++++
4 files changed, 135 insertions(+), 46 deletions(-)
diff --git a/jetty-documentation/pom.xml b/jetty-documentation/pom.xml
index 000675ded84..2c5a472ec73 100644
--- a/jetty-documentation/pom.xml
+++ b/jetty-documentation/pom.xml
@@ -49,6 +49,10 @@
+
+ org.eclipse.jetty.toolchain
+ jetty-servlet-api
+ org.eclipse.jettyjetty-client
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-cookie.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-cookie.adoc
index 3802218cdc8..dc060ebf5cf 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-cookie.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-cookie.adoc
@@ -17,103 +17,87 @@
//
[[client-http-cookie]]
-=== Cookies Support
+=== HttpClient Cookie Support
-Jetty HTTP client supports cookies out of the box.
+Jetty's `HttpClient` supports cookies out of the box.
The `HttpClient` instance receives cookies from HTTP responses and stores them in a `java.net.CookieStore`, a class that is part of the JDK.
When new requests are made, the cookie store is consulted and if there are matching cookies (that is, cookies that are not expired and that match domain and path of the request) then they are added to the requests.
Applications can programmatically access the cookie store to find the cookies that have been set:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-CookieStore cookieStore = httpClient.getCookieStore();
-List cookies = cookieStore.get(URI.create("http://domain.com/path"));
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=getCookies]
----
Applications can also programmatically set cookies as if they were returned from a HTTP response:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-CookieStore cookieStore = httpClient.getCookieStore();
-HttpCookie cookie = new HttpCookie("foo", "bar");
-cookie.setDomain("domain.com");
-cookie.setPath("/");
-cookie.setMaxAge(TimeUnit.DAYS.toSeconds(1));
-cookieStore.add(URI.create("http://domain.com"), cookie);
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=setCookie]
----
-Cookies may be added only for a particular request:
+Cookies may be added explicitly only for a particular request:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-ContentResponse response = httpClient.newRequest("http://domain.com/path")
- .cookie(new HttpCookie("foo", "bar"))
- .send();
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=requestCookie]
----
You can remove cookies that you do not want to be sent in future HTTP requests:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-CookieStore cookieStore = httpClient.getCookieStore();
-URI uri = URI.create("http://domain.com");
-List cookies = cookieStore.get(uri);
-for (HttpCookie cookie : cookies)
- cookieStore.remove(uri, cookie);
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=removeCookie]
----
-If you want to totally disable cookie handling, you can install a `HttpCookieStore.Empty` instance in this way:
+If you want to totally disable cookie handling, you can install a
+`HttpCookieStore.Empty`. This must be done when `HttpClient` is used in a
+proxy application, in this way:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-httpClient.setCookieStore(new HttpCookieStore.Empty());
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=emptyCookieStore]
----
You can enable cookie filtering by installing a cookie store that performs the filtering logic in this way:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-httpClient.setCookieStore(new GoogleOnlyCookieStore());
-
-public class GoogleOnlyCookieStore extends HttpCookieStore
-{
- @Override
- public void add(URI uri, HttpCookie cookie)
- {
- if (uri.getHost().endsWith("google.com"))
- super.add(uri, cookie);
- }
-}
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=filteringCookieStore]
----
The example above will retain only cookies that come from the `google.com` domain or sub-domains.
+// TODO: move this section to server-side
==== Special Characters in Cookies
Jetty is compliant with link:https://tools.ietf.org/html/rfc6265[RFC6265], and as such care must be taken when setting a cookie value that includes special characters such as `;`.
Previously, Version=1 cookies defined in link:https://tools.ietf.org/html/rfc2109[RFC2109] (and continued in link:https://tools.ietf.org/html/rfc2965[RFC2965]) allowed for special/reserved characters to be enclosed within double quotes when declared in a `Set-Cookie` response header:
-[source, java, subs="{sub-order}"]
+[source,subs="{sub-order}"]
----
Set-Cookie: foo="bar;baz";Version=1;Path="/secur"
----
-This was added to the HTTP Response header as follows:
+This was added to the HTTP Response as follows:
-[source, java, subs="{sub-order}"]
+[source,java,subs="{sub-order}"]
----
-Cookie cookie = new Cookie("foo", "bar;baz");
-cookie.setPath("/secur");
-response.addCookie(cookie);
+protected void service(HttpServletRequest request, HttpServletResponse response)
+{
+ javax.servlet.http.Cookie cookie = new Cookie("foo", "bar;baz");
+ cookie.setPath("/secur");
+ response.addCookie(cookie);
+}
----
The introduction of RFC6265 has rendered this approach no longer possible; users are now required to encode cookie values that use these special characters.
This can be done utilizing `javax.servlet.http.Cookie` as follows:
-[source, java, subs="{sub-order}"]
+[source,java,subs="{sub-order}"]
----
-Cookie cookie = new Cookie("foo", URLEncoder.encode("bar;baz", "utf-8"));
+javax.servlet.http.Cookie cookie = new Cookie("foo", URLEncoder.encode("bar;baz", "UTF-8"));
----
Jetty validates all cookie names and values being added to the `HttpServletResponse` via the `addCookie(Cookie)` method.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc
index 34f8c78547f..6492e3fe1b8 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc
@@ -46,6 +46,18 @@ Out of the box features that you get with the Jetty HTTP client include:
[[client-http-start]]
==== Starting HttpClient
+The Jetty artifact that provides the main HTTP client implementation is `jetty-client`.
+The Maven artifact coordinates are the following:
+
+[source,xml,subs="{sub-order}"]
+----
+
+ org.eclipse.jetty
+ jetty-client
+ {version}
+
+----
+
The main class is named `org.eclipse.jetty.client.HttpClient`.
You can think of a `HttpClient` instance as a browser instance.
diff --git a/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java b/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
index 8549f712e64..d36b9fa9618 100644
--- a/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
+++ b/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
@@ -22,8 +22,12 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.CookieStore;
+import java.net.HttpCookie;
+import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.file.Paths;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.LongConsumer;
@@ -47,6 +51,7 @@ import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.HttpCookieStore;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import static java.lang.System.Logger.Level.INFO;
@@ -474,4 +479,88 @@ public class HTTPClientDocs
request1.send(result -> System.getLogger("forwarder").log(INFO, "Sourcing from server1 complete"));
// end::demandedContentListener[]
}
+
+ public void getCookies() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::getCookies[]
+ CookieStore cookieStore = httpClient.getCookieStore();
+ List cookies = cookieStore.get(URI.create("http://domain.com/path"));
+ // end::getCookies[]
+ }
+
+ public void setCookie() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::setCookie[]
+ CookieStore cookieStore = httpClient.getCookieStore();
+ HttpCookie cookie = new HttpCookie("foo", "bar");
+ cookie.setDomain("domain.com");
+ cookie.setPath("/");
+ cookie.setMaxAge(TimeUnit.DAYS.toSeconds(1));
+ cookieStore.add(URI.create("http://domain.com"), cookie);
+ // end::setCookie[]
+ }
+
+ public void requestCookie() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::requestCookie[]
+ ContentResponse response = httpClient.newRequest("http://domain.com/path")
+ .cookie(new HttpCookie("foo", "bar"))
+ .send();
+ // end::requestCookie[]
+ }
+
+ public void removeCookie() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::removeCookie[]
+ CookieStore cookieStore = httpClient.getCookieStore();
+ URI uri = URI.create("http://domain.com");
+ List cookies = cookieStore.get(uri);
+ for (HttpCookie cookie : cookies)
+ {
+ cookieStore.remove(uri, cookie);
+ }
+ // end::removeCookie[]
+ }
+
+ public void emptyCookieStore() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::emptyCookieStore[]
+ httpClient.setCookieStore(new HttpCookieStore.Empty());
+ // end::emptyCookieStore[]
+ }
+
+ public void filteringCookieStore() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::filteringCookieStore[]
+ class GoogleOnlyCookieStore extends HttpCookieStore
+ {
+ @Override
+ public void add(URI uri, HttpCookie cookie)
+ {
+ if (uri.getHost().endsWith("google.com"))
+ super.add(uri, cookie);
+ }
+ }
+
+ httpClient.setCookieStore(new GoogleOnlyCookieStore());
+ // end::filteringCookieStore[]
+ }
}
From 9f38e433d40b1281e08b9358893e4e9cd26b121c Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Thu, 2 Apr 2020 12:19:23 +0200
Subject: [PATCH 033/101] Improvements to the Jetty client documentation,
authentication section.
Signed-off-by: Simone Bordet
---
.../http/client-http-authentication.adoc | 100 ++++++++++--------
.../embedded/client/http/HTTPClientDocs.java | 56 ++++++++++
2 files changed, 111 insertions(+), 45 deletions(-)
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-authentication.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-authentication.adoc
index 4e3e48704ae..fcdd2fc3b01 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-authentication.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-authentication.adoc
@@ -17,75 +17,85 @@
//
[[client-http-authentication]]
-=== Authentication Support
+=== HttpClient Authentication Support
-Jetty's HTTP client supports the `BASIC` and `DIGEST` authentication mechanisms defined by link:https://tools.ietf.org/html/rfc7235[RFC 7235].
+Jetty's `HttpClient` supports the `BASIC` and `DIGEST` authentication
+mechanisms defined by link:https://tools.ietf.org/html/rfc7235[RFC 7235],
+as well as the SPNEGO authentication mechanism defined in
+link:https://tools.ietf.org/html/rfc4559[RFC 4559].
-You can configure authentication credentials in the HTTP client instance as follows:
+The HTTP _conversation_ - the sequence of related HTTP requests - for a
+request that needs authentication is the following:
-[source, java, subs="{sub-order}"]
+[plantuml]
----
-URI uri = new URI("http://domain.com/secure");
-String realm = "MyRealm";
-String user = "username";
-String pass = "password";
+skinparam backgroundColor transparent
+skinparam monochrome true
+skinparam shadowing false
-// Add authentication credentials
-AuthenticationStore auth = httpClient.getAuthenticationStore();
-auth.addAuthentication(new BasicAuthentication(uri, realm, user, pass));
+participant Application
+participant HttpClient
+participant Server
-ContentResponse response = httpClient
- .newRequest(uri)
- .send()
- .get(5, TimeUnit.SECONDS);
+Application -> Server: GET /path
+Server -> HttpClient: 401 + WWW-Authenticate
+HttpClient -> Server: GET + Authentication
+Server -> Application: 200 OK
----
-Jetty's HTTP client tests authentication credentials against the challenge(s) the server issues (see our section here on link:#configuring-security-secure-passwords[secure password obfuscation]), and if they match it automatically sends the right authentication headers to the server for authentication.
-If the authentication is successful, it caches the result and reuses it for subsequent requests for the same domain and matching URIs.
+Upon receiving a HTTP 401 response code, `HttpClient` looks at the
+`WWW-Authenticate` response header (the server _challenge_) and then tries to
+match configured authentication credentials to produce an `Authentication`
+header that contains the authentication credentials to access the resource.
-The HTTP conversation for a successful match is the following:
+You can configure authentication credentials in the `HttpClient` instance as
+follows:
+[source,java,indent=0]
----
-Application HttpClient Server
- | | |
- |--- GET ---|------------ GET ----------->|
- | | |
- | |<-- 401 + WWW-Authenticate --|
- | | |
- | |--- GET + Authentication --->|
- | | |
- |<-- 200 ---|------------ 200 ------------|
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=addAuthentication]
----
-The application does not receive events related to the response with code 401, they are handled internally by `HttpClient` which produces a request similar to the original but with the correct `Authorization` header, and then relays the response with code 200 to the application.
+``Authentication``s are matched against the server challenge first by
+mechanism (e.g. `BASIC` or `DIGEST`), then by realm and then by URI.
-Successful authentications are cached, but it is possible to clear them in order to force authentication again:
+If an `Authentication` match is found, the application does not receive events
+related to the HTTP 401 response. These events are handled internally by
+`HttpClient` which produces another (internal) request similar to the original
+request but with an additional `Authorization` header.
-[source, java, subs="{sub-order}"]
+If the authentication is successful, the server responds with a HTTP 200 and
+`HttpClient` caches the `Authentication.Result` so that subsequent requests
+for a matching URI will not incur in the additional rountrip caused by the
+HTTP 401 response.
+
+It is possible to clear ``Authentication.Result``s in order to force
+authentication again:
+
+[source,java,indent=0]
----
-httpClient.getAuthenticationStore().clearAuthenticationResults();
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=clearResults]
----
-Authentications may be preempted to avoid the additional roundtrip due to the server challenge in this way:
+Authentication results may be preempted to avoid the additional roundtrip
+due to the server challenge in this way:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-AuthenticationStore auth = httpClient.getAuthenticationStore();
-URI uri = URI.create("http://domain.com/secure");
-auth.addAuthenticationResult(new BasicAuthentication.BasicResult(uri, "username", "password"));
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=preemptedResult]
----
-In this way, requests for the given URI are enriched by `HttpClient` immediately with the `Authorization` header, and the server should respond with a 200 and the resource content rather than with the 401 and the challenge.
+In this way, requests for the given URI are enriched immediately with the
+`Authorization` header, and the server should respond with HTTP 200 (and the
+resource content) rather than with the 401 and the challenge.
-It is also possible to preempt the authentication for a single request only, in this way:
+It is also possible to preempt the authentication for a single request only,
+in this way:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-URI uri = URI.create("http://domain.com/secure");
-Authentication.Result authn = new BasicAuthentication.BasicResult(uri, "username", "password")
-Request request = httpClient.newRequest(uri);
-authn.apply(request);
-request.send();
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=requestPreemptedResult]
----
-See also the link:#client-http-proxy-authentication[proxy authentication section] for further information about how authentication works with HTTP proxies.
+See also the link:#client-http-proxy-authentication[proxy authentication section]
+for further information about how authentication works with HTTP proxies.
diff --git a/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java b/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
index d36b9fa9618..3e44f7c6bb8 100644
--- a/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
+++ b/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
@@ -32,12 +32,15 @@ import java.util.concurrent.TimeUnit;
import java.util.function.LongConsumer;
import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.Authentication;
+import org.eclipse.jetty.client.api.AuthenticationStore;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
import org.eclipse.jetty.client.util.AsyncRequestContent;
+import org.eclipse.jetty.client.util.BasicAuthentication;
import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.BytesRequestContent;
import org.eclipse.jetty.client.util.FutureResponseListener;
@@ -563,4 +566,57 @@ public class HTTPClientDocs
httpClient.setCookieStore(new GoogleOnlyCookieStore());
// end::filteringCookieStore[]
}
+
+ public void addAuthentication() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::addAuthentication[]
+ // Add authentication credentials.
+ AuthenticationStore auth = httpClient.getAuthenticationStore();
+
+ URI uri1 = new URI("http://mydomain.com/secure");
+ auth.addAuthentication(new BasicAuthentication(uri1, "MyRealm", "userName1", "password1"));
+
+ URI uri2 = new URI("http://otherdomain.com/admin");
+ auth.addAuthentication(new BasicAuthentication(uri1, "AdminRealm", "admin", "password"));
+ // end::addAuthentication[]
+ }
+
+ public void clearResults() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::clearResults[]
+ httpClient.getAuthenticationStore().clearAuthenticationResults();
+ // end::clearResults[]
+ }
+
+ public void preemptedResult() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::preemptedResult[]
+ AuthenticationStore auth = httpClient.getAuthenticationStore();
+ URI uri = URI.create("http://domain.com/secure");
+ auth.addAuthenticationResult(new BasicAuthentication.BasicResult(uri, "username", "password"));
+ // end::preemptedResult[]
+ }
+
+ public void requestPreemptedResult() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::requestPreemptedResult[]
+ URI uri = URI.create("http://domain.com/secure");
+ Authentication.Result authn = new BasicAuthentication.BasicResult(uri, "username", "password");
+ Request request = httpClient.newRequest(uri);
+ authn.apply(request);
+ request.send();
+ // end::requestPreemptedResult[]
+ }
}
From eaf9d43a0bc4772ede477b2719a16b45a9f13dd4 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Thu, 2 Apr 2020 13:22:54 +0200
Subject: [PATCH 034/101] Improvements to the Jetty client documentation, proxy
section.
Signed-off-by: Simone Bordet
---
.../client/http/client-http-proxy.adoc | 109 ++++++++----------
.../embedded/client/http/HTTPClientDocs.java | 47 ++++++++
2 files changed, 96 insertions(+), 60 deletions(-)
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-proxy.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-proxy.adoc
index 13f53584e93..88554725612 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-proxy.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-proxy.adoc
@@ -17,87 +17,76 @@
//
[[client-http-proxy]]
-=== Proxy Support
+=== HttpClient Proxy Support
-Jetty's HTTP client can be configured to use proxies to connect to destinations.
+Jetty's `HttpClient` can be configured to use proxies to connect to destinations.
-Two types of proxies are available out of the box: a HTTP proxy (provided by class `org.eclipse.jetty.client.HttpProxy`) and a SOCKS 4 proxy (provided by class `org.eclipse.jetty.client.Socks4Proxy`).
+Two types of proxies are available out of the box: a HTTP proxy (provided by
+class `org.eclipse.jetty.client.HttpProxy`) and a SOCKS 4 proxy (provided by
+class `org.eclipse.jetty.client.Socks4Proxy`).
Other implementations may be written by subclassing `ProxyConfiguration.Proxy`.
The following is a typical configuration:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-ProxyConfiguration proxyConfig = httpClient.getProxyConfiguration();
-HttpProxy proxy = new HttpProxy("proxyHost", proxyPort);
-
-// Do not proxy requests for localhost:8080
-proxy.getExcludedAddresses().add("localhost:8080");
-
-// add the new proxy to the list of proxies already registered
-proxyConfig.getProxies().add(proxy);
-
-ContentResponse response = httpClient.GET(uri);
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=proxy]
----
-You specify the proxy host and port, and optionally also the addresses that you do not want to be proxied, and then add the proxy configuration on the `ProxyConfiguration` instance.
+You specify the proxy host and proxy port, and optionally also the addresses
+that you do not want to be proxied, and then add the proxy configuration on
+the `ProxyConfiguration` instance.
-Configured in this way, `HttpClient` makes requests to the HTTP proxy (for plain-text HTTP requests) or establishes a tunnel via `HTTP CONNECT` (for encrypted HTTPS requests).
+Configured in this way, `HttpClient` makes requests to the HTTP proxy (for
+plain-text HTTP requests) or establishes a tunnel via HTTP `CONNECT` (for
+encrypted HTTPS requests).
+
+Proxying is supported for both HTTP/1.1 and HTTP/2.
[[client-http-proxy-authentication]]
==== Proxy Authentication Support
-Jetty's HTTP client support proxy authentication in the same way it supports link:#client-http-authentication[server authentication].
+Jetty's `HttpClient` supports proxy authentication in the same way it supports
+link:#client-http-authentication[server authentication].
-In the example below, the proxy requires Basic authentication, but the server requires Digest authentication, and therefore:
+In the example below, the proxy requires `BASIC` authentication, but the server
+requires `DIGEST` authentication, and therefore:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-URI proxyURI = new URI("http://proxy.net:8080");
-URI serverURI = new URI("http://domain.com/secure");
-
-AuthenticationStore auth = httpClient.getAuthenticationStore();
-
-// Proxy credentials.
-auth.addAuthentication(new BasicAuthentication(proxyURI, "ProxyRealm", "proxyUser", "proxyPass"));
-
-// Server credentials.
-auth.addAuthentication(new DigestAuthentication(serverURI, "ServerRealm", "serverUser", "serverPass"));
-
-// Proxy configuration.
-ProxyConfiguration proxyConfig = httpClient.getProxyConfiguration();
-HttpProxy proxy = new HttpProxy("proxy.net", 8080);
-proxyConfig.getProxies().add(proxy);
-
-ContentResponse response = httpClient.newRequest(serverURI)
- .send()
- .get(5, TimeUnit.SECONDS);
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=proxyAuthentication]
----
-The HTTP conversation for successful authentications on both the proxy and the server is the following:
+The HTTP conversation for successful authentications on both the proxy and the
+server is the following:
+[plantuml]
----
-Application HttpClient Proxy Server
- | | | |
- |--- GET -->|------------- GET ------------->| |
- | | | |
- | |<----- 407 + Proxy-Authn -------| |
- | | | |
- | |------ GET + Proxy-Authz ------>| |
- | | | |
- | | |---------- GET --------->|
- | | | |
- | | |<--- 401 + WWW-Authn ----|
- | | | |
- | |<------ 401 + WWW-Authn --------| |
- | | | |
- | |-- GET + Proxy-Authz + Authz -->| |
- | | | |
- | | |------ GET + Authz ----->|
- | | | |
- |<-- 200 ---|<------------ 200 --------------|<--------- 200 ----------|
+skinparam backgroundColor transparent
+skinparam monochrome true
+skinparam shadowing false
+
+participant Application
+participant HttpClient
+participant Proxy
+participant Server
+
+Application -> Proxy: GET /path
+Proxy -> HttpClient: 407 + Proxy-Authenticate
+HttpClient -> Proxy: GET /path + Proxy-Authorization
+Proxy -> Server: GET /path
+Server -> Proxy: 401 + WWW-Authenticate
+Proxy -> HttpClient: 401 + WWW-Authenticate
+HttpClient -> Proxy: GET /path + Proxy-Authorization + Authorization
+Proxy -> Server: GET /path + Authorization
+Server -> Proxy: 200 OK
+Proxy -> HttpClient: 200 OK
+HttpClient -> Application: 200 OK
----
-The application does not receive events related to the responses with code 407 and 401 since they are handled internally by `HttpClient`.
+The application does not receive events related to the responses with code 407
+and 401 since they are handled internally by `HttpClient`.
-Similarly to the link:#client-http-authentication[authentication section], the proxy authentication result and the server authentication result can be preempted to avoid, respectively, the 407 and 401 roundtrips.
+Similarly to the link:#client-http-authentication[authentication section], the
+proxy authentication result and the server authentication result can be
+preempted to avoid, respectively, the 407 and 401 roundtrips.
diff --git a/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java b/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
index 3e44f7c6bb8..decd799c0d4 100644
--- a/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
+++ b/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
@@ -32,6 +32,8 @@ import java.util.concurrent.TimeUnit;
import java.util.function.LongConsumer;
import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpProxy;
+import org.eclipse.jetty.client.ProxyConfiguration;
import org.eclipse.jetty.client.api.Authentication;
import org.eclipse.jetty.client.api.AuthenticationStore;
import org.eclipse.jetty.client.api.ContentResponse;
@@ -43,6 +45,7 @@ import org.eclipse.jetty.client.util.AsyncRequestContent;
import org.eclipse.jetty.client.util.BasicAuthentication;
import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.BytesRequestContent;
+import org.eclipse.jetty.client.util.DigestAuthentication;
import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.client.util.InputStreamRequestContent;
import org.eclipse.jetty.client.util.InputStreamResponseListener;
@@ -619,4 +622,48 @@ public class HTTPClientDocs
request.send();
// end::requestPreemptedResult[]
}
+
+ public void proxy() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::proxy[]
+ HttpProxy proxy = new HttpProxy("proxyHost", 8888);
+
+ // Do not proxy requests for localhost:8080.
+ proxy.getExcludedAddresses().add("localhost:8080");
+
+ // Add the new proxy to the list of proxies already registered.
+ ProxyConfiguration proxyConfig = httpClient.getProxyConfiguration();
+ proxyConfig.getProxies().add(proxy);
+
+ ContentResponse response = httpClient.GET("http://domain.com/path");
+ // end::proxy[]
+ }
+
+ public void proxyAuthentication() throws Exception
+ {
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // tag::proxyAuthentication[]
+ AuthenticationStore auth = httpClient.getAuthenticationStore();
+
+ // Proxy credentials.
+ URI proxyURI = new URI("http://proxy.net:8080");
+ auth.addAuthentication(new BasicAuthentication(proxyURI, "ProxyRealm", "proxyUser", "proxyPass"));
+
+ // Server credentials.
+ URI serverURI = new URI("http://domain.com/secure");
+ auth.addAuthentication(new DigestAuthentication(serverURI, "ServerRealm", "serverUser", "serverPass"));
+
+ // Proxy configuration.
+ ProxyConfiguration proxyConfig = httpClient.getProxyConfiguration();
+ HttpProxy proxy = new HttpProxy("proxy.net", 8080);
+ proxyConfig.getProxies().add(proxy);
+
+ ContentResponse response = httpClient.newRequest(serverURI).send();
+ // end::proxyAuthentication[]
+ }
}
From 51c42f2849b5e6a3db9dfd935e58e489418394e5 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Fri, 3 Apr 2020 15:48:31 +0200
Subject: [PATCH 035/101] Improvements to the Jetty client documentation,
protocols section.
Signed-off-by: Simone Bordet
---
.../alpn/client/ALPNClientConnection.java | 5 +-
.../client/ALPNClientConnectionFactory.java | 8 -
.../java/client/JDK9ClientALPNProcessor.java | 7 +-
.../dynamic/HttpClientTransportDynamic.java | 64 ++++---
.../http/HttpClientConnectionFactory.java | 28 ++-
jetty-documentation/pom.xml | 9 +-
.../http2/introduction.adoc | 2 +-
.../client/http/client-http-api.adoc | 4 +-
.../client/http/client-http-transport.adoc | 179 +++++++++++++-----
.../examples/embedded-minimal-servlet.adoc | 2 +-
.../examples/embedded-one-webapp.adoc | 2 +-
.../embedded/client/http/HTTPClientDocs.java | 134 +++++++++++++
.../ClientConnectionFactoryOverHTTP2.java | 34 +++-
.../jetty/io/ClientConnectionFactory.java | 23 +--
.../tests/WebSocketOverHTTP2Test.java | 16 +-
.../HttpClientTransportDynamicTest.java | 73 ++++---
.../client/ProxyWithDynamicTransportTest.java | 10 +-
17 files changed, 445 insertions(+), 155 deletions(-)
diff --git a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java
index 3b46fbf013e..295242a908f 100644
--- a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java
+++ b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java
@@ -44,9 +44,6 @@ public class ALPNClientConnection extends NegotiatingClientConnection
public void selected(String protocol)
{
- if (protocol == null || !protocols.contains(protocol))
- close();
- else
- completed(protocol);
+ completed(protocol);
}
}
diff --git a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java
index 900013fa1f4..63c2ef63231 100644
--- a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java
+++ b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java
@@ -110,12 +110,4 @@ public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFact
}
throw new IllegalStateException("No ALPNProcessor for " + engine);
}
-
- public static class ALPN extends Info
- {
- public ALPN(Executor executor, ClientConnectionFactory factory, List protocols)
- {
- super(List.of("alpn"), new ALPNClientConnectionFactory(executor, factory, protocols));
- }
- }
}
diff --git a/jetty-alpn/jetty-alpn-java-client/src/main/java/org/eclipse/jetty/alpn/java/client/JDK9ClientALPNProcessor.java b/jetty-alpn/jetty-alpn-java-client/src/main/java/org/eclipse/jetty/alpn/java/client/JDK9ClientALPNProcessor.java
index 3a455ffb024..c1c392242fa 100644
--- a/jetty-alpn/jetty-alpn-java-client/src/main/java/org/eclipse/jetty/alpn/java/client/JDK9ClientALPNProcessor.java
+++ b/jetty-alpn/jetty-alpn-java-client/src/main/java/org/eclipse/jetty/alpn/java/client/JDK9ClientALPNProcessor.java
@@ -75,8 +75,11 @@ public class JDK9ClientALPNProcessor implements ALPNProcessor.Client
{
String protocol = alpnConnection.getSSLEngine().getApplicationProtocol();
if (LOG.isDebugEnabled())
- LOG.debug("selected protocol {}", protocol);
- alpnConnection.selected(protocol);
+ LOG.debug("selected protocol '{}'", protocol);
+ if (protocol != null && !protocol.isEmpty())
+ alpnConnection.selected(protocol);
+ else
+ alpnConnection.selected(null);
}
}
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/dynamic/HttpClientTransportDynamic.java b/jetty-client/src/main/java/org/eclipse/jetty/client/dynamic/HttpClientTransportDynamic.java
index 732d6d843ef..2ab93cdd6a8 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/dynamic/HttpClientTransportDynamic.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/dynamic/HttpClientTransportDynamic.java
@@ -19,12 +19,14 @@
package org.eclipse.jetty.client.dynamic;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.eclipse.jetty.alpn.client.ALPNClientConnection;
import org.eclipse.jetty.alpn.client.ALPNClientConnectionFactory;
@@ -37,6 +39,7 @@ import org.eclipse.jetty.client.MultiplexConnectionPool;
import org.eclipse.jetty.client.MultiplexHttpDestination;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.http.HttpClientConnectionFactory;
+import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnector;
@@ -105,7 +108,7 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
factoryInfos = new Info[]{HttpClientConnectionFactory.HTTP11};
this.factoryInfos = Arrays.asList(factoryInfos);
this.protocols = Arrays.stream(factoryInfos)
- .flatMap(info -> info.getProtocols().stream())
+ .flatMap(info -> Stream.concat(info.getProtocols(false).stream(), info.getProtocols(true).stream()))
.distinct()
.map(p -> p.toLowerCase(Locale.ENGLISH))
.collect(Collectors.toList());
@@ -117,9 +120,9 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
@Override
public Origin newOrigin(HttpRequest request)
{
- boolean ssl = HttpClient.isSchemeSecure(request.getScheme());
+ boolean secure = HttpClient.isSchemeSecure(request.getScheme());
String http1 = "http/1.1";
- String http2 = ssl ? "h2" : "h2c";
+ String http2 = secure ? "h2" : "h2c";
List protocols = List.of();
if (request.isVersionExplicit())
{
@@ -130,16 +133,23 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
}
else
{
- if (ssl)
+ if (secure)
{
// There may be protocol negotiation, so preserve the order
// of protocols chosen by the application.
// We need to keep multiple protocols in case the protocol
// is negotiated: e.g. [http/1.1, h2] negotiates [h2], but
// here we don't know yet what will be negotiated.
+ List http = List.of("http/1.1", "h2c", "h2");
protocols = this.protocols.stream()
- .filter(p -> p.equals(http1) || p.equals(http2))
- .collect(Collectors.toList());
+ .filter(http::contains)
+ .collect(Collectors.toCollection(ArrayList::new));
+
+ // The http/1.1 upgrade to http/2 over TLS implicitly
+ // "negotiates" [h2c], so we need to remove [h2]
+ // because we don't want to negotiate using ALPN.
+ if (request.getHeaders().contains(HttpHeader.UPGRADE, "h2c"))
+ protocols.remove("h2");
}
else
{
@@ -149,7 +159,7 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
}
Origin.Protocol protocol = null;
if (!protocols.isEmpty())
- protocol = new Origin.Protocol(protocols, ssl && protocols.contains(http2));
+ protocol = new Origin.Protocol(protocols, secure && protocols.contains(http2));
return getHttpClient().createOrigin(request, protocol);
}
@@ -164,32 +174,33 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
{
HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
Origin.Protocol protocol = destination.getOrigin().getProtocol();
- ClientConnectionFactory.Info factoryInfo;
+ ClientConnectionFactory factory;
if (protocol == null)
{
// Use the default ClientConnectionFactory.
- factoryInfo = factoryInfos.get(0);
+ factory = factoryInfos.get(0).getClientConnectionFactory();
}
else
{
if (destination.isSecure() && protocol.isNegotiate())
{
- factoryInfo = new ALPNClientConnectionFactory.ALPN(getClientConnector().getExecutor(), this::newNegotiatedConnection, protocol.getProtocols());
+ factory = new ALPNClientConnectionFactory(getClientConnector().getExecutor(), this::newNegotiatedConnection, protocol.getProtocols());
}
else
{
- factoryInfo = findClientConnectionFactoryInfo(protocol.getProtocols())
- .orElseThrow(() -> new IOException("Cannot find " + ClientConnectionFactory.class.getSimpleName() + " for " + protocol));
+ factory = findClientConnectionFactoryInfo(protocol.getProtocols(), destination.isSecure())
+ .orElseThrow(() -> new IOException("Cannot find " + ClientConnectionFactory.class.getSimpleName() + " for " + protocol))
+ .getClientConnectionFactory();
}
}
- return factoryInfo.getClientConnectionFactory().newConnection(endPoint, context);
+ return factory.newConnection(endPoint, context);
}
public void upgrade(EndPoint endPoint, Map context)
{
HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
Origin.Protocol protocol = destination.getOrigin().getProtocol();
- Info info = findClientConnectionFactoryInfo(protocol.getProtocols())
+ Info info = findClientConnectionFactoryInfo(protocol.getProtocols(), destination.isSecure())
.orElseThrow(() -> new IllegalStateException("Cannot find " + ClientConnectionFactory.class.getSimpleName() + " to upgrade to " + protocol));
info.upgrade(endPoint, context);
}
@@ -200,13 +211,22 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
{
ALPNClientConnection alpnConnection = (ALPNClientConnection)endPoint.getConnection();
String protocol = alpnConnection.getProtocol();
- if (LOG.isDebugEnabled())
- LOG.debug("ALPN negotiated {} among {}", protocol, alpnConnection.getProtocols());
- if (protocol == null)
- throw new IOException("Could not negotiate protocol among " + alpnConnection.getProtocols());
- List protocols = List.of(protocol);
- Info factoryInfo = findClientConnectionFactoryInfo(protocols)
+ Info factoryInfo;
+ if (protocol != null)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("ALPN negotiated {} among {}", protocol, alpnConnection.getProtocols());
+ List protocols = List.of(protocol);
+ factoryInfo = findClientConnectionFactoryInfo(protocols, true)
.orElseThrow(() -> new IOException("Cannot find " + ClientConnectionFactory.class.getSimpleName() + " for negotiated protocol " + protocol));
+ }
+ else
+ {
+ // Server does not support ALPN, let's try the first protocol.
+ factoryInfo = factoryInfos.get(0);
+ if (LOG.isDebugEnabled())
+ LOG.debug("No ALPN protocol, using {}", factoryInfo);
+ }
return factoryInfo.getClientConnectionFactory().newConnection(endPoint, context);
}
catch (Throwable failure)
@@ -216,10 +236,10 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
}
}
- private Optional findClientConnectionFactoryInfo(List protocols)
+ private Optional findClientConnectionFactoryInfo(List protocols, boolean secure)
{
return factoryInfos.stream()
- .filter(info -> info.matches(protocols))
+ .filter(info -> info.matches(protocols, secure))
.findFirst();
}
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpClientConnectionFactory.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpClientConnectionFactory.java
index 418616175f6..2063596c359 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpClientConnectionFactory.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpClientConnectionFactory.java
@@ -21,12 +21,16 @@ package org.eclipse.jetty.client.http;
import java.util.List;
import java.util.Map;
+import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.EndPoint;
public class HttpClientConnectionFactory implements ClientConnectionFactory
{
- public static final Info HTTP11 = new Info(List.of("http/1.1"), new HttpClientConnectionFactory());
+ /**
+ *
Representation of the {@code HTTP/1.1} application protocol used by {@link HttpClientTransportDynamic}.
+ */
+ public static final Info HTTP11 = new HTTP11(new HttpClientConnectionFactory());
@Override
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map context)
@@ -34,4 +38,26 @@ public class HttpClientConnectionFactory implements ClientConnectionFactory
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, context);
return customize(connection, context);
}
+
+ private static class HTTP11 extends Info
+ {
+ private static final List protocols = List.of("http/1.1");
+
+ private HTTP11(ClientConnectionFactory factory)
+ {
+ super(factory);
+ }
+
+ @Override
+ public List getProtocols(boolean secure)
+ {
+ return protocols;
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s@%x%s", getClass().getSimpleName(), hashCode(), protocols);
+ }
+ }
}
diff --git a/jetty-documentation/pom.xml b/jetty-documentation/pom.xml
index 2c5a472ec73..3ad33457458 100644
--- a/jetty-documentation/pom.xml
+++ b/jetty-documentation/pom.xml
@@ -59,8 +59,13 @@
${project.version}
- org.eclipse.jetty
- jetty-io
+ org.eclipse.jetty.http2
+ http2-http-client-transport
+ ${project.version}
+
+
+ org.eclipse.jetty.fcgi
+ fcgi-client${project.version}
diff --git a/jetty-documentation/src/main/asciidoc/distribution-guide/http2/introduction.adoc b/jetty-documentation/src/main/asciidoc/distribution-guide/http2/introduction.adoc
index 92fa37c7e8c..2c81f182022 100644
--- a/jetty-documentation/src/main/asciidoc/distribution-guide/http2/introduction.adoc
+++ b/jetty-documentation/src/main/asciidoc/distribution-guide/http2/introduction.adoc
@@ -50,5 +50,5 @@ The Jetty HTTP/2 implementation consists of the following sub-projects (each pro
2. `http2-hpack`: Contains the HTTP/2 HPACK implementation for HTTP header compression.
3. `http2-server`: Provides the server-side implementation of HTTP/2.
4. `http2-client`: Provides the implementation of HTTP/2 client with a low level HTTP/2 API, dealing with HTTP/2 streams, frames, etc.
-5. `http2-http-client-transport`: Provides the implementation of the HTTP/2 transport for `HttpClient` (see xref:http-client[]).
+5. `http2-http-client-transport`: Provides the implementation of the HTTP/2 transport for `HttpClient` (see xref:client-http[this section]).
Applications can use the higher level API provided by `HttpClient` to send HTTP requests and receive HTTP responses, and the HTTP/2 transport will take care of converting them in HTTP/2 format (see also https://webtide.com/http2-support-for-httpclient/[this blog entry]).
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-api.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-api.adoc
index 4765fb47a18..b369bb33e91 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-api.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-api.adoc
@@ -34,7 +34,7 @@ include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=simpleBl
The method `HttpClient.GET(...)` performs a HTTP `GET` request to the given URI and returns a `ContentResponse` when the request/response conversation completes successfully.
The `ContentResponse` object contains the HTTP response information: status code, headers and possibly content.
-The content length is limited by default to 2 MiB; for larger content see xref:http-client-response-content[].
+The content length is limited by default to 2 MiB; for larger content see xref:client-http-content-response[].
If you want to customize the request, for example by issuing a `HEAD` request instead of a `GET`, and simulating a browser user agent, you can do it in this way:
@@ -190,7 +190,7 @@ which allows applications to write request content when it is available to the `
include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=outputStreamRequestContent]
----
-[[http-client-response-content]]
+[[client-http-content-response]]
===== Response Content Handling
Jetty's `HttpClient` allows applications to handle response content in different ways.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-transport.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-transport.adoc
index 21870cc9460..518483296b4 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-transport.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-transport.adoc
@@ -16,21 +16,25 @@
// ========================================================================
//
-[[http-client-transport]]
-=== Pluggable Transports
+[[client-http-transport]]
+=== HttpClient Pluggable Transports
-Jetty's HTTP client can be configured to use different transports to carry the semantic of HTTP requests and responses.
+Jetty's `HttpClient` can be configured to use different transports to carry the
+semantic of HTTP requests and responses.
-This means that the intention of a client to request resource `/index.html` using the `GET` method can be carried over the network in different formats.
+This means that the intention of a client to request resource `/index.html`
+using the `GET` method can be carried over the network in different formats.
-A HTTP client transport is the component that is in charge of converting a high-level, semantic, HTTP requests such as "GET resource /index.html" into the specific format understood by the server (for example, HTTP/2), and to convert the server response from the specific format (HTTP/2) into high-level, semantic objects that can be used by applications.
+A `HttpClient` transport is the component that is in charge of converting a
+high-level, semantic, HTTP requests such as "`GET` resource ``/index.html``"
+into the specific format understood by the server (for example, HTTP/2), and to
+convert the server response from the specific format (HTTP/2) into high-level,
+semantic objects that can be used by applications.
-In this way, applications are not aware of the actual protocol being used.
-This allows them to write their logic against a high-level API that hides the details of the specific protocol being used over the network.
+The most common protocol format is HTTP/1.1, a textual protocol with lines
+separated by `\r\n`:
-The most common protocol format is HTTP/1.1, a text-based protocol with lines separated by `\r\n`:
-
-[source, screen, subs="{sub-order}"]
+[source,screen,subs="{sub-order}"]
----
GET /index.html HTTP/1.1\r\n
Host: domain.com\r\n
@@ -40,7 +44,7 @@ Host: domain.com\r\n
However, the same request can be made using FastCGI, a binary protocol:
-[source, screen, subs="{sub-order}"]
+[source,screen,subs="{sub-order}"]
----
x01 x01 x00 x01 x00 x08 x00 x00
x00 x01 x01 x00 x00 x00 x00 x00
@@ -52,64 +56,153 @@ x0C x0B D O C U M E
...
----
-Similarly, HTTP/2 is a binary protocol that transports the same information in a yet different format.
+Similarly, HTTP/2 is a binary protocol that transports the same information
+in a yet different format.
+A protocol may be _negotiated_ between client and server. A request for a
+resource may be sent using one protocol (for example, HTTP/1.1), but the
+response may arrive in a different protocol (for example, HTTP/2).
+
+`HttpClient` supports 3 static transports, each speaking only one protocol:
+link:#client-http-transport-http11[HTTP/1.1],
+link:#client-http-transport-http2[HTTP/2] and
+link:#client-http-transport-fcgi[FastCGI],
+all of them with 2 variants: clear-text and TLS encrypted.
+
+`HttpClient` also supports one
+link:#client-http-transport-dynamic[dynamic transport],
+that can speak different protocols and can select the right protocol by
+negotiating it with the server or by explicit indication from applications.
+
+Applications are typically not aware of the actual protocol being used.
+This allows them to write their logic against a high-level API that hides the
+details of the specific protocol being used over the network.
+
+[[client-http-transport-http11]]
==== HTTP/1.1 Transport
HTTP/1.1 is the default transport.
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-// No transport specified, using default.
-HttpClient client = new HttpClient();
-client.start();
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=defaultTransport]
----
-If you want to customize the HTTP/1.1 transport, you can explicitly configure `HttpClient` in this way:
+If you want to customize the HTTP/1.1 transport, you can explicitly configure
+it in this way:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-int selectors = 1;
-HttpClientTransportOverHTTP transport = new HttpClientTransportOverHTTP(selectors);
-
-HttpClient client = new HttpClient(transport, null);
-client.start();
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=http11Transport]
----
-The example above allows you to customize the number of NIO selectors that `HttpClient` will be using.
-
+[[client-http-transport-http2]]
==== HTTP/2 Transport
The HTTP/2 transport can be configured in this way:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-HTTP2Client h2Client = new HTTP2Client();
-h2Client.setSelectors(1);
-HttpClientTransportOverHTTP2 transport = new HttpClientTransportOverHTTP2(h2Client);
-
-HttpClient client = new HttpClient(transport, null);
-client.start();
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=http2Transport]
----
-`HTTP2Client` is the lower-level client that provides an API based on HTTP/2 concepts such as _sessions_, _streams_ and _frames_ that are specific to HTTP/2.
+`HTTP2Client` is the lower-level client that provides an API based on HTTP/2
+concepts such as _sessions_, _streams_ and _frames_ that are specific to HTTP/2.
+See link:#client-http2[the HTTP/2 client section] for more information.
-`HttpClientTransportOverHTTP2` uses `HTTP2Client` to format high-level semantic HTTP requests ("GET resource /index.html") into the HTTP/2 specific format.
+`HttpClientTransportOverHTTP2` uses `HTTP2Client` to format high-level semantic
+HTTP requests (like "GET resource /index.html") into the HTTP/2 specific format.
+[[client-http-transport-fcgi]]
==== FastCGI Transport
The FastCGI transport can be configured in this way:
-[source, java, subs="{sub-order}"]
+[source,java,indent=0]
----
-int selectors = 1;
-String scriptRoot = "/var/www/wordpress";
-HttpClientTransportOverFCGI transport = new HttpClientTransportOverFCGI(selectors, false, scriptRoot);
-
-HttpClient client = new HttpClient(transport, null);
-client.start();
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=fcgiTransport]
----
-In order to make requests using the FastCGI transport, you need to have a FastCGI server such as https://en.wikipedia.org/wiki/PHP#PHPFPM[PHP-FPM] (see also http://php.net/manual/en/install.fpm.php).
+In order to make requests using the FastCGI transport, you need to have a
+FastCGI server such as https://en.wikipedia.org/wiki/PHP#PHPFPM[PHP-FPM]
+(see also http://php.net/manual/en/install.fpm.php).
-The FastCGI transport is primarily used by Jetty's link:#fastcgi[FastCGI support] to serve PHP pages (WordPress for example).
+The FastCGI transport is primarily used by Jetty's link:#fastcgi[FastCGI support]
+to serve PHP pages (WordPress for example).
+
+[[client-http-transport-dynamic]]
+==== Dynamic Transport
+
+The static transports work well if you know in advance the protocol you want
+to speak with the server, or if the server only supports one protocol (such
+as FastCGI).
+
+With the advent of HTTP/2, however, servers are now able to support multiple
+protocols, at least both HTTP/1.1 and HTTP/2.
+
+The HTTP/2 protocol is typically negotiated between client and server.
+This negotiation can happen via ALPN, a TLS extension that allows the client
+to tell the server the list of protocol that the client supports, so that the
+server can pick one of the client supported protocols that also the server
+supports; or via HTTP/1.1 upgrade by means of the `Upgrade` header.
+
+Applications can configure the dynamic transport with one or more
+_application_ protocols such as HTTP/1.1 or HTTP/2. The implementation will
+take care of using TLS for HTTPS URIs, using ALPN, negotiating protocols,
+upgrading from one protocol to another, etc.
+
+By default, the dynamic transport only speaks HTTP/1.1:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=dynamicDefault]
+----
+
+The dynamic transport can be configured with just one protocol, making it
+equivalent to the corresponding static transport:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=dynamicOneProtocol]
+----
+
+The dynamic transport, however, has been implemented to support multiple
+transports, in particular both HTTP/1.1 and HTTP/2:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=dynamicH1H2]
+----
+
+IMPORTANT: The order in which the protocols are specified to
+`HttpClientTransportDynamic` indicates what is the client preference.
+If the protocol is negotiated via ALPN, it is the server that decides what is
+the protocol to use for the communication, regardless of the client preference.
+If the protocol is not negotiated, the client preference is honored.
+
+Provided that the server supports both HTTP/1.1 and HTTP/2 clear-text, client
+applications can explicitly hint the version they want to use:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=dynamicClearText]
+----
+
+In case of TLS encrypted communication using the HTTPS scheme, things are a
+little more complicated.
+
+If the client application explicitly specifies the HTTP version, then ALPN
+is not used on the client. By specifying the HTTP version explicitly, the
+client application has prior-knowledge of what HTTP version the server
+supports, and therefore ALPN is not needed.
+If the server does not support the HTTP version chosen by the client, then
+the communication will fail.
+
+If the client application does not explicitly specify the HTTP version,
+then ALPN will be used on the client.
+If the server also supports ALPN, then the protocol will be negotiated via
+ALPN and the server will choose the protocol to use.
+If the server does not support ALPN, the client will try to use the first
+protocol configured in `HttpClientTransportDynamic`, and the communication
+may succeed or fail depending on whether the server supports the protocol
+chosen by the client.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-minimal-servlet.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-minimal-servlet.adoc
index 6d440c591fd..068f62faa1d 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-minimal-servlet.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-minimal-servlet.adoc
@@ -22,7 +22,7 @@
This example shows the bare minimum required for deploying a servlet into Jetty.
Note that this is strictly a servlet, not a servlet in the context of a web application, that example comes later.
This is purely just a servlet deployed and mounted on a context and able to process requests.
-This example is excellent for situations where you have a simple servlet that you need to unit test, just mount it on a context and issue requests using your favorite http client library (like our Jetty client found in xref:http-client[]).
+This example is excellent for situations where you have a simple servlet that you need to unit test, just mount it on a context and issue requests using your favorite http client library (like our Jetty client found in xref:client-http[]).
[source, java, subs="{sub-order}"]
----
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-one-webapp.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-one-webapp.adoc
index 048d5ddd4d3..61c06a4d145 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-one-webapp.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-one-webapp.adoc
@@ -21,7 +21,7 @@
This example shows how to deploy a simple webapp with an embedded instance of Jetty.
This is useful when you want to manage the lifecycle of a server programmatically, either within a production application or as a simple way to deploying and debugging a full scale application deployment.
-In many ways it is easier then traditional deployment since you control the classpath yourself, making this easy to wire up in a test case in Maven and issue requests using your favorite http client library (like our Jetty client found in xref:http-client[]).
+In many ways it is easier then traditional deployment since you control the classpath yourself, making this easy to wire up in a test case in Maven and issue requests using your favorite http client library (like our Jetty client found in xref:client-http[]).
[source, java, subs="{sub-order}"]
----
diff --git a/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java b/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
index decd799c0d4..f72655f916f 100644
--- a/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
+++ b/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
@@ -41,6 +41,8 @@ import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
+import org.eclipse.jetty.client.http.HttpClientConnectionFactory;
+import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.util.AsyncRequestContent;
import org.eclipse.jetty.client.util.BasicAuthentication;
import org.eclipse.jetty.client.util.BufferingResponseListener;
@@ -52,9 +54,16 @@ import org.eclipse.jetty.client.util.InputStreamResponseListener;
import org.eclipse.jetty.client.util.OutputStreamRequestContent;
import org.eclipse.jetty.client.util.PathRequestContent;
import org.eclipse.jetty.client.util.StringRequestContent;
+import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI;
+import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.client.http.ClientConnectionFactoryOverHTTP2;
+import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.HttpCookieStore;
@@ -666,4 +675,129 @@ public class HTTPClientDocs
ContentResponse response = httpClient.newRequest(serverURI).send();
// end::proxyAuthentication[]
}
+
+ public void defaultTransport() throws Exception
+ {
+ // tag::defaultTransport[]
+ // No transport specified, using default.
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+ // end::defaultTransport[]
+ }
+
+ public void http11Transport() throws Exception
+ {
+ // tag::http11Transport[]
+ // Configure HTTP/1.1 transport.
+ HttpClientTransportOverHTTP transport = new HttpClientTransportOverHTTP();
+ transport.setHeaderCacheSize(16384);
+
+ HttpClient client = new HttpClient(transport);
+ client.start();
+ // end::http11Transport[]
+ }
+
+ public void http2Transport() throws Exception
+ {
+ // tag::http2Transport[]
+ // The HTTP2Client powers the HTTP/2 transport.
+ HTTP2Client h2Client = new HTTP2Client();
+ h2Client.setInitialSessionRecvWindow(64 * 1024 * 1024);
+
+ // Create and configure the HTTP/2 transport.
+ HttpClientTransportOverHTTP2 transport = new HttpClientTransportOverHTTP2(h2Client);
+ transport.setUseALPN(true);
+
+ HttpClient client = new HttpClient(transport);
+ client.start();
+ // end::http2Transport[]
+ }
+
+ public void fcgiTransport() throws Exception
+ {
+ // tag::fcgiTransport[]
+ String scriptRoot = "/var/www/wordpress";
+ HttpClientTransportOverFCGI transport = new HttpClientTransportOverFCGI(scriptRoot);
+
+ HttpClient client = new HttpClient(transport);
+ client.start();
+ // end::fcgiTransport[]
+ }
+
+ public void dynamicDefault() throws Exception
+ {
+ // tag::dynamicDefault[]
+ // Dynamic transport speaks HTTP/1.1 by default.
+ HttpClientTransportDynamic transport = new HttpClientTransportDynamic();
+
+ HttpClient client = new HttpClient(transport);
+ client.start();
+ // end::dynamicDefault[]
+ }
+
+ public void dynamicOneProtocol() throws Exception
+ {
+ // tag::dynamicOneProtocol[]
+ ClientConnector connector = new ClientConnector();
+
+ // Equivalent to HttpClientTransportOverHTTP.
+ HttpClientTransportDynamic http11Transport = new HttpClientTransportDynamic(connector, HttpClientConnectionFactory.HTTP11);
+
+ // Equivalent to HttpClientTransportOverHTTP2.
+ HTTP2Client http2Client = new HTTP2Client(connector);
+ HttpClientTransportDynamic http2Transport = new HttpClientTransportDynamic(connector, new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client));
+ // end::dynamicOneProtocol[]
+ }
+
+ public void dynamicH1H2() throws Exception
+ {
+ // tag::dynamicH1H2[]
+ ClientConnector connector = new ClientConnector();
+
+ ClientConnectionFactory.Info http1 = HttpClientConnectionFactory.HTTP11;
+
+ HTTP2Client http2Client = new HTTP2Client(connector);
+ ClientConnectionFactoryOverHTTP2.HTTP2 http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
+
+ HttpClientTransportDynamic transport = new HttpClientTransportDynamic(connector, http1, http2);
+
+ HttpClient client = new HttpClient(transport);
+ client.start();
+ // end::dynamicH1H2[]
+ }
+
+ public void dynamicClearText() throws Exception
+ {
+ // tag::dynamicClearText[]
+ ClientConnector connector = new ClientConnector();
+ ClientConnectionFactory.Info http1 = HttpClientConnectionFactory.HTTP11;
+ HTTP2Client http2Client = new HTTP2Client(connector);
+ ClientConnectionFactoryOverHTTP2.HTTP2 http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
+ HttpClientTransportDynamic transport = new HttpClientTransportDynamic(connector, http1, http2);
+ HttpClient client = new HttpClient(transport);
+ client.start();
+
+ // The server supports both HTTP/1.1 and HTTP/2 clear-text on port 8080.
+
+ // Make a clear-text request without explicit version.
+ // The first protocol specified to HttpClientTransportDynamic
+ // is picked, in this example will be HTTP/1.1.
+ ContentResponse http1Response = client.newRequest("host", 8080).send();
+
+ // Make a clear-text request with explicit version.
+ // Clear-text HTTP/2 is used for this request.
+ ContentResponse http2Response = client.newRequest("host", 8080)
+ // Specify the version explicitly.
+ .version(HttpVersion.HTTP_2)
+ .send();
+
+ // Make a clear-text upgrade request from HTTP/1.1 to HTTP/2.
+ // The request will start as HTTP/1.1, but the response will be HTTP/2.
+ ContentResponse upgradedResponse = client.newRequest("host", 8080)
+ .header(HttpHeader.UPGRADE, "h2c")
+ .header(HttpHeader.HTTP2_SETTINGS, "")
+ .header(HttpHeader.CONNECTION, "Upgrade, HTTP2-Settings")
+ .send();
+ // end::dynamicClearText[]
+ }
}
diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/ClientConnectionFactoryOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/ClientConnectionFactoryOverHTTP2.java
index 1f17ae4bfcd..2493fbf6678 100644
--- a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/ClientConnectionFactoryOverHTTP2.java
+++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/ClientConnectionFactoryOverHTTP2.java
@@ -26,6 +26,8 @@ import java.util.Map;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
+import org.eclipse.jetty.client.http.HttpClientConnectionFactory;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.HTTP2ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnectionFactory;
@@ -56,19 +58,25 @@ public class ClientConnectionFactoryOverHTTP2 extends ContainerLifeCycle impleme
return factory.newConnection(endPoint, context);
}
- public static class H2 extends Info
+ /**
+ *
Representation of the {@code HTTP/2} application protocol used by {@link HttpClientTransportDynamic}.
+ *
+ * @see HttpClientConnectionFactory#HTTP11
+ */
+ public static class HTTP2 extends Info
{
- public H2(HTTP2Client client)
- {
- super(List.of("h2"), new ClientConnectionFactoryOverHTTP2(client));
- }
- }
+ private static final List protocols = List.of("h2", "h2c");
+ private static final List h2c = List.of("h2c");
- public static class H2C extends Info
- {
- public H2C(HTTP2Client client)
+ public HTTP2(HTTP2Client client)
{
- super(List.of("h2c"), new ClientConnectionFactoryOverHTTP2(client));
+ super(new ClientConnectionFactoryOverHTTP2(client));
+ }
+
+ @Override
+ public List getProtocols(boolean secure)
+ {
+ return secure ? protocols : h2c;
}
@Override
@@ -119,5 +127,11 @@ public class ClientConnectionFactoryOverHTTP2 extends ContainerLifeCycle impleme
throw new UncheckedIOException(x);
}
}
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s@%x%s", getClass().getSimpleName(), hashCode(), protocols);
+ }
}
}
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnectionFactory.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnectionFactory.java
index 4e555e7878e..d66a5901b38 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnectionFactory.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnectionFactory.java
@@ -66,26 +66,21 @@ public interface ClientConnectionFactory
}
/**
- *
A holder for a list of protocol strings identifying a network protocol
+ *
A holder for a list of protocol strings identifying an application protocol
* (for example {@code ["h2", "h2-17", "h2-16"]}) and a {@link ClientConnectionFactory}
* that creates connections that speak that network protocol.
*/
- public static class Info extends ContainerLifeCycle
+ public abstract static class Info extends ContainerLifeCycle
{
- private final List protocols;
private final ClientConnectionFactory factory;
- public Info(List protocols, ClientConnectionFactory factory)
+ public Info(ClientConnectionFactory factory)
{
- this.protocols = protocols;
this.factory = factory;
addBean(factory);
}
- public List getProtocols()
- {
- return protocols;
- }
+ public abstract List getProtocols(boolean secure);
public ClientConnectionFactory getClientConnectionFactory()
{
@@ -98,20 +93,14 @@ public interface ClientConnectionFactory
* @param candidates the candidates to match against
* @return whether one of the protocols of this class is present in the candidates
*/
- public boolean matches(List candidates)
+ public boolean matches(List candidates, boolean secure)
{
- return protocols.stream().anyMatch(p -> candidates.stream().anyMatch(c -> c.equalsIgnoreCase(p)));
+ return getProtocols(secure).stream().anyMatch(p -> candidates.stream().anyMatch(c -> c.equalsIgnoreCase(p)));
}
public void upgrade(EndPoint endPoint, Map context)
{
throw new UnsupportedOperationException(this + " does not support upgrade to another protocol");
}
-
- @Override
- public String toString()
- {
- return String.format("%s@%x%s", getClass().getSimpleName(), hashCode(), protocols);
- }
}
}
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketOverHTTP2Test.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketOverHTTP2Test.java
index 1cd8a05caa4..cd590d1507f 100644
--- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketOverHTTP2Test.java
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketOverHTTP2Test.java
@@ -152,7 +152,7 @@ public class WebSocketOverHTTP2Test
@Test
public void testWebSocketOverDynamicHTTP2() throws Exception
{
- testWebSocketOverDynamicTransport(clientConnector -> new ClientConnectionFactoryOverHTTP2.H2C(new HTTP2Client(clientConnector)));
+ testWebSocketOverDynamicTransport(clientConnector -> new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector)));
}
private void testWebSocketOverDynamicTransport(Function protocolFn) throws Exception
@@ -184,7 +184,7 @@ public class WebSocketOverHTTP2Test
AbstractHTTP2ServerConnectionFactory h2c = connector.getBean(AbstractHTTP2ServerConnectionFactory.class);
h2c.setConnectProtocolEnabled(false);
- startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.H2C(new HTTP2Client(clientConnector)));
+ startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector)));
EventSocket wsEndPoint = new EventSocket();
URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/ws/echo");
@@ -220,7 +220,7 @@ public class WebSocketOverHTTP2Test
}
});
- startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.H2(new HTTP2Client(clientConnector)));
+ startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector)));
// Connect and send immediately a message, so the message
// arrives to the server while the server is still upgrading.
@@ -242,7 +242,7 @@ public class WebSocketOverHTTP2Test
public void testWebSocketConnectPortDoesNotExist() throws Exception
{
startServer();
- startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.H2(new HTTP2Client(clientConnector)));
+ startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector)));
EventSocket wsEndPoint = new EventSocket();
URI uri = URI.create("ws://localhost:" + (connector.getLocalPort() + 1) + "/ws/echo");
@@ -259,7 +259,7 @@ public class WebSocketOverHTTP2Test
public void testWebSocketNotFound() throws Exception
{
startServer();
- startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.H2(new HTTP2Client(clientConnector)));
+ startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector)));
EventSocket wsEndPoint = new EventSocket();
URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/nothing");
@@ -276,7 +276,7 @@ public class WebSocketOverHTTP2Test
public void testNotNegotiated() throws Exception
{
startServer();
- startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.H2(new HTTP2Client(clientConnector)));
+ startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector)));
EventSocket wsEndPoint = new EventSocket();
URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/ws/null");
@@ -293,7 +293,7 @@ public class WebSocketOverHTTP2Test
public void testThrowFromCreator() throws Exception
{
startServer();
- startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.H2(new HTTP2Client(clientConnector)));
+ startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector)));
CountDownLatch latch = new CountDownLatch(1);
connector.addBean(new HttpChannel.Listener()
@@ -327,7 +327,7 @@ public class WebSocketOverHTTP2Test
public void testServerConnectionClose() throws Exception
{
startServer();
- startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.H2(new HTTP2Client(clientConnector)));
+ startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector)));
EventSocket wsEndPoint = new EventSocket();
URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/ws/connectionClose");
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTransportDynamicTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTransportDynamicTest.java
index 8649870c55a..4ce25b5eb2e 100644
--- a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTransportDynamicTest.java
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTransportDynamicTest.java
@@ -42,7 +42,7 @@ import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
import org.eclipse.jetty.client.http.HttpClientConnectionFactory;
import org.eclipse.jetty.client.util.BufferingResponseListener;
-import org.eclipse.jetty.client.util.BytesContentProvider;
+import org.eclipse.jetty.client.util.BytesRequestContent;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpScheme;
@@ -244,7 +244,7 @@ public class HttpClientTransportDynamicTest
startServer(this::h1H2C, new EmptyServerHandler());
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
- ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
+ ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
startClient(clientConnector, h2c);
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
// .version(HttpVersion.HTTP_2)
@@ -273,14 +273,15 @@ public class HttpClientTransportDynamicTest
clientConnector.setSslContextFactory(newClientSslContextFactory());
HttpClientConnectionFactory.Info h1 = HttpClientConnectionFactory.HTTP11;
HTTP2Client http2Client = new HTTP2Client(clientConnector);
- ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
- HttpClientTransportDynamic transport = new HttpClientTransportDynamic(clientConnector, h1, h2c)
+ ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
+ HttpClientTransportDynamic transport = new HttpClientTransportDynamic(clientConnector, h1, http2)
{
@Override
public Origin newOrigin(HttpRequest request)
{
// Use prior-knowledge, i.e. negotiate==false.
- List protocols = HttpVersion.HTTP_2 == request.getVersion() ? h2c.getProtocols() : h1.getProtocols();
+ boolean secure = HttpClient.isSchemeSecure(request.getScheme());
+ List protocols = HttpVersion.HTTP_2 == request.getVersion() ? http2.getProtocols(secure) : h1.getProtocols(secure);
return new Origin(request.getScheme(), request.getHost(), request.getPort(), request.getTag(), new Origin.Protocol(protocols, false));
}
};
@@ -320,8 +321,8 @@ public class HttpClientTransportDynamicTest
startServer(this::sslAlpnH1H2, new EmptyServerHandler());
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
- ClientConnectionFactory.Info h2 = new ClientConnectionFactoryOverHTTP2.H2(http2Client);
- startClient(clientConnector, HttpClientConnectionFactory.HTTP11, h2);
+ ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
+ startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
// Make a request, should be HTTP/1.1 because of the order of protocols on server.
ContentResponse h1cResponse = client.newRequest("localhost", connector.getLocalPort())
@@ -355,8 +356,8 @@ public class HttpClientTransportDynamicTest
startServer(this::sslAlpnH1, new EmptyServerHandler());
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
- ClientConnectionFactory.Info h2 = new ClientConnectionFactoryOverHTTP2.H2(http2Client);
- startClient(clientConnector, h2, HttpClientConnectionFactory.HTTP11);
+ ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
+ startClient(clientConnector, http2, HttpClientConnectionFactory.HTTP11);
// The client prefers h2 over h1, and use of TLS and ALPN will allow the fallback to h1.
ContentResponse h1cResponse = client.newRequest("localhost", connector.getLocalPort())
@@ -372,8 +373,8 @@ public class HttpClientTransportDynamicTest
startServer(this::h1, new EmptyServerHandler());
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
- ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
- startClient(clientConnector, HttpClientConnectionFactory.HTTP11, h2c);
+ ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
+ startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
// The client forces HTTP/2, but the server cannot speak it, so the request fails.
// There is no fallback to HTTP/1 because the protocol version is set explicitly.
@@ -450,9 +451,8 @@ public class HttpClientTransportDynamicTest
server.start();
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
- ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
- ClientConnectionFactory.Info h2 = new ClientConnectionFactoryOverHTTP2.H2(http2Client);
- startClient(clientConnector, h2, HttpClientConnectionFactory.HTTP11, h2c);
+ ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
+ startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
// Make a clear-text request using HTTP/1.1.
ContentResponse h1cResponse = client.newRequest("localhost", clearConnector.getLocalPort())
@@ -510,8 +510,8 @@ public class HttpClientTransportDynamicTest
});
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
- ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
- startClient(clientConnector, HttpClientConnectionFactory.HTTP11, h2c);
+ ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
+ startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
// Make an upgrade request from HTTP/1.1 to H2C.
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
@@ -584,8 +584,8 @@ public class HttpClientTransportDynamicTest
});
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
- ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
- startClient(clientConnector, HttpClientConnectionFactory.HTTP11, h2c);
+ ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
+ startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
int proxyPort = connector.getLocalPort();
// The proxy speaks both http/1.1 and h2c.
@@ -622,8 +622,8 @@ public class HttpClientTransportDynamicTest
});
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
- ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
- startClient(clientConnector, HttpClientConnectionFactory.HTTP11, h2c);
+ ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
+ startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
// Make an upgrade request from HTTP/1.1 to H2C.
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
@@ -653,8 +653,8 @@ public class HttpClientTransportDynamicTest
});
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
- ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
- startClient(clientConnector, HttpClientConnectionFactory.HTTP11, h2c);
+ ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
+ startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
// Make a POST upgrade request from HTTP/1.1 to H2C.
// We don't support upgrades with request content because
@@ -668,7 +668,7 @@ public class HttpClientTransportDynamicTest
.header(HttpHeader.UPGRADE, "h2c")
.header(HttpHeader.HTTP2_SETTINGS, "")
.header(HttpHeader.CONNECTION, "Upgrade, HTTP2-Settings")
- .content(new BytesContentProvider(bytes))
+ .body(new BytesRequestContent(bytes))
.timeout(5, TimeUnit.SECONDS)
.send(new BufferingResponseListener(bytes.length)
{
@@ -693,8 +693,8 @@ public class HttpClientTransportDynamicTest
startServer(this::h1H2C, new EmptyServerHandler());
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
- ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
- startClient(clientConnector, HttpClientConnectionFactory.HTTP11, h2c);
+ ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
+ startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
// The upgrade request is missing the required HTTP2-Settings header.
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
@@ -719,8 +719,8 @@ public class HttpClientTransportDynamicTest
});
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
- ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
- startClient(clientConnector, HttpClientConnectionFactory.HTTP11, h2c);
+ ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
+ startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
// Make an upgrade request from HTTP/1.1 to H2C.
CountDownLatch latch = new CountDownLatch(1);
@@ -736,4 +736,23 @@ public class HttpClientTransportDynamicTest
assertTrue(latch.await(5, TimeUnit.SECONDS));
}
+
+ @Test
+ public void testClientWithALPNServerWithoutALPN() throws Exception
+ {
+ startServer(this::sslH1H2C, new EmptyServerHandler());
+ ClientConnector clientConnector = new ClientConnector();
+ HTTP2Client http2Client = new HTTP2Client(clientConnector);
+ ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
+ startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
+
+ // Make a request without explicit version, so ALPN is used on the client.
+ // Since the server does not support ALPN, the first protocol is used.
+ ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+ .scheme(HttpScheme.HTTPS.asString())
+ .timeout(5, TimeUnit.SECONDS)
+ .send();
+
+ assertEquals(HttpStatus.OK_200, response.getStatus());
+ }
}
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/ProxyWithDynamicTransportTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/ProxyWithDynamicTransportTest.java
index 7dbd499b063..1988361ca0c 100644
--- a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/ProxyWithDynamicTransportTest.java
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/ProxyWithDynamicTransportTest.java
@@ -180,9 +180,8 @@ public class ProxyWithDynamicTransportTest
{
ClientConnectionFactory.Info h1 = HttpClientConnectionFactory.HTTP11;
HTTP2Client http2Client = new HTTP2Client(clientConnector);
- ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
- ClientConnectionFactory.Info h2 = new ClientConnectionFactoryOverHTTP2.H2(http2Client);
- return new HttpClient(new HttpClientTransportDynamic(clientConnector, h1, h2c, h2));
+ ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
+ return new HttpClient(new HttpClientTransportDynamic(clientConnector, h1, http2));
}
});
context.addServlet(holder, "/*");
@@ -200,9 +199,8 @@ public class ProxyWithDynamicTransportTest
clientConnector.setSslContextFactory(new SslContextFactory.Client(true));
http2Client = new HTTP2Client(clientConnector);
ClientConnectionFactory.Info h1 = HttpClientConnectionFactory.HTTP11;
- ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
- ClientConnectionFactory.Info h2 = new ClientConnectionFactoryOverHTTP2.H2(http2Client);
- client = new HttpClient(new HttpClientTransportDynamic(clientConnector, h1, h2c, h2));
+ ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
+ client = new HttpClient(new HttpClientTransportDynamic(clientConnector, h1, http2));
client.start();
}
From 8f4de31d3779567e87ebb18c60eab7a1a1fbe721 Mon Sep 17 00:00:00 2001
From: Joakim Erdfelt
Date: Fri, 3 Apr 2020 16:54:24 -0500
Subject: [PATCH 036/101] Issue #4745 - update documentation for
centralized-webapp-logging
Signed-off-by: Joakim Erdfelt
---
.../example-logback-centralized-logging.adoc | 132 +++++++++++-------
1 file changed, 82 insertions(+), 50 deletions(-)
diff --git a/jetty-documentation/src/main/asciidoc/administration/logging/example-logback-centralized-logging.adoc b/jetty-documentation/src/main/asciidoc/administration/logging/example-logback-centralized-logging.adoc
index 3becdb3e312..ef5e328d00b 100644
--- a/jetty-documentation/src/main/asciidoc/administration/logging/example-logback-centralized-logging.adoc
+++ b/jetty-documentation/src/main/asciidoc/administration/logging/example-logback-centralized-logging.adoc
@@ -34,74 +34,105 @@ This configuration is essentially the multiple logger configuration with added c
The technique used by this configuration is to provide an link:{JDURL}org/eclipse/jetty/deploy/AppLifeCycle.Binding.html[AppLifeCycle.Binding] against the link:{JDURL}/org/eclipse/jetty/deploy/AppLifeCycle.html[`"deploying"`node] that modifies the
link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#getSystemClasspathPattern()[WebAppContext.getSystemClasspathPattern().add(String)] for the common logging classes.
-See https://github.com/jetty-project/jetty-webapp-logging/blob/master/src/main/java/org/eclipse/jetty/webapp/logging/CentralizedWebAppLoggingBinding.java[org.eclipse.jetty.logging.CentralizedWebAppLoggingBinding] for actual implementation.
+See https://github.com/jetty-project/jetty-webapp-logging/blob/master/jetty-webapp-logging/src/main/java/org/eclipse/jetty/webapp/logging/CentralizedWebAppLoggingBinding.java[org.eclipse.jetty.logging.CentralizedWebAppLoggingBinding] for actual implementation.
A convenient replacement `logging` module has been created to bootstrap your `${jetty.base}` directory for capturing all Jetty server logging from multiple logging frameworks into a single logging output file managed by Logback.
-[source, screen, subs="{sub-order}"]
+[source,screen,subs="{sub-order}"]
....
-[mybase]$ mkdir modules
-[mybase]$ cd modules
-
-[modules]$ curl -O https://raw.githubusercontent.com/jetty-project/logging-modules/master/capture-all/logging.mod
+[mybase]$ curl -O https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-webapp-logging/9.4.27/jetty-webapp-logging-9.4.27-config.jar
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
-100 1416 100 1416 0 0 4241 0 --:--:-- --:--:-- --:--:-- 4252
+100 3402 100 3402 0 0 15823 0 --:--:-- --:--:-- --:--:-- 15750
-[master]$ curl -O https://raw.githubusercontent.com/jetty-project/logging-modules/master/centralized/webapp-logging.mod
- % Total % Received % Xferd Average Speed Time Time Time Current
- Dload Upload Total Spent Left Speed
-100 660 100 660 0 0 2032 0 --:--:-- --:--:-- --:--:-- 2037
-[modules]$ cd ..
+[mybase]$ jar -xf jetty-webapp-logging-9.4.27-config.jar
-[mybase]$ java -jar /opt/jetty-dist/start.jar --add-to-start=logging,webapp-logging
-INFO: logging initialised in ${jetty.base}/start.ini (appended)
-MKDIR: ${jetty.base}/logs
-DOWNLOAD: https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.6.6/slf4j-api-1.6.6.jar to lib/logging/slf4j-api-1.6.6.jar
-DOWNLOAD: https://repo1.maven.org/maven2/org/slf4j/log4j-over-slf4j/1.6.6/log4j-over-slf4j-1.6.6.jar to lib/logging/log4j-over-slf4j-1.6.6.jar
-DOWNLOAD: https://repo1.maven.org/maven2/org/slf4j/jul-to-slf4j/1.6.6/jul-to-slf4j-1.6.6.jar to lib/logging/jul-to-slf4j-1.6.6.jar
-DOWNLOAD: https://repo1.maven.org/maven2/org/slf4j/jcl-over-slf4j/1.6.6/jcl-over-slf4j-1.6.6.jar to lib/logging/jcl-over-slf4j-1.6.6.jar
-DOWNLOAD: https://repo1.maven.org/maven2/ch/qos/logback/logback-core/1.0.7/logback-core-1.0.7.jar to lib/logging/logback-core-1.0.7.jar
-DOWNLOAD: https://repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.0.7/logback-classic-1.0.7.jar to lib/logging/logback-classic-1.0.7.jar
-DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/capture-all/logback.xml to resources/logback.xml
-DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/capture-all/jetty-logging.properties to resources/jetty-logging.properties
-DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/capture-all/jetty-logging.xml to etc/jetty-logging.xml
-INFO: resources initialised transitively
-INFO: resources enabled in ${jetty.base}/start.ini
-INFO: webapp-logging initialised in ${jetty.base}/start.ini (appended)
-DOWNLOAD: https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-webapp-logging/9.0.0/jetty-webapp-logging-9.0.0.jar to lib/webapp-logging/jetty-webapp-logging-9.0.0.jar
-DOWNLOAD: https://raw.githubusercontent.com/jetty-project/jetty-webapp-logging/master/src/main/config/etc/jetty-webapp-logging.xml to etc/jetty-webapp-logging.xml
-DOWNLOAD: https://raw.githubusercontent.com/jetty-project/jetty-webapp-logging/master/src/main/config/etc/jetty-mdc-handler.xml to etc/jetty-mdc-handler.xml
-INFO: deploy initialised transitively
-INFO: deploy enabled in ${jetty.base}/start.ini
-INFO: logging initialised transitively
-INFO: resources initialised transitively
-INFO: resources enabled in ${jetty.base}/start.ini
+[mybase]$ java -jar /opt/jetty-hom/start.jar --create-startd --add-to-start=centralized-webapp-logging
-[mybase]$ java -jar /opt/jetty-dist/start.jar
+ALERT: There are enabled module(s) with licenses.
+The following 2 module(s):
+ + contains software not provided by the Eclipse Foundation!
+ + contains software not covered by the Eclipse Public License!
+ + has not been audited for compliance with its license
+
+ Module: logback-impl
+ + Logback: the reliable, generic, fast and flexible logging framework.
+ + Copyright (C) 1999-2012, QOS.ch. All rights reserved.
+ + This program and the accompanying materials are dual-licensed under
+ + either:
+ + the terms of the Eclipse Public License v1.0
+ + as published by the Eclipse Foundation:
+ + http://www.eclipse.org/legal/epl-v10.html
+ + or (per the licensee's choosing) under
+ + the terms of the GNU Lesser General Public License version 2.1
+ + as published by the Free Software Foundation:
+ + http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
+
+ Module: slf4j-api
+ + SLF4J is distributed under the MIT License.
+ + Copyright (c) 2004-2013 QOS.ch
+ + All rights reserved.
+ + Permission is hereby granted, free of charge, to any person obtaining
+ + a copy of this software and associated documentation files (the
+ + "Software"), to deal in the Software without restriction, including
+ + without limitation the rights to use, copy, modify, merge, publish,
+ + distribute, sublicense, and/or sell copies of the Software, and to
+ + permit persons to whom the Software is furnished to do so, subject to
+ + the following conditions:
+ + The above copyright notice and this permission notice shall be
+ + included in all copies or substantial portions of the Software.
+ + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Proceed (y/N)? y
+INFO : slf4j-api transitively enabled
+INFO : log4j-over-slf4j transitively enabled
+INFO : jcl-slf4j transitively enabled
+INFO : logback-impl transitively enabled
+INFO : jul-slf4j transitively enabled
+INFO : slf4j-logback transitively enabled
+INFO : centralized-webapp-logging initialized in ${jetty.base}/start.d/centralized-webapp-logging.ini
+INFO : logging-logback transitively enabled
+INFO : resources transitively enabled
+MKDIR : ${jetty.base}/lib/slf4j
+DOWNLD: https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar to ${jetty.base}/lib/slf4j/slf4j-api-1.7.25.jar
+MKDIR : ${jetty.base}/lib/logging
+DOWNLD: https://repo1.maven.org/maven2/org/slf4j/log4j-over-slf4j/1.7.25/log4j-over-slf4j-1.7.25.jar to ${jetty.base}/lib/logging/log4j-over-slf4j-1.7.25.jar
+DOWNLD: https://repo1.maven.org/maven2/org/slf4j/jcl-over-slf4j/1.7.25/jcl-over-slf4j-1.7.25.jar to ${jetty.base}/lib/slf4j/jcl-over-slf4j-1.7.25.jar
+MKDIR : ${jetty.base}/lib/logback
+DOWNLD: https://repo1.maven.org/maven2/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar to ${jetty.base}/lib/logback/logback-core-1.2.3.jar
+DOWNLD: https://repo1.maven.org/maven2/org/slf4j/jul-to-slf4j/1.7.25/jul-to-slf4j-1.7.25.jar to ${jetty.base}/lib/slf4j/jul-to-slf4j-1.7.25.jar
+COPY : ${jetty.home}/modules/jul-slf4j/etc/java-util-logging.properties to ${jetty.base}/etc/java-util-logging.properties
+DOWNLD: https://repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar to ${jetty.base}/lib/logback/logback-classic-1.2.3.jar
+MKDIR : ${jetty.base}/logs
+DOWNLD: https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-webapp-logging/9.4.27/jetty-webapp-logging-9.4.27.jar to ${jetty.base}/lib/logging/jetty-webapp-logging-9.4.27.jar
+INFO : Base directory was modified
+
+$
....
-The replacement `logging.mod` performs a number of tasks.
+This replacement `centralized-webapp-logging.mod` performs a number of tasks.
-. `mybase` is a `${jetty.base}` directory.
-. The jetty-distribution is unpacked (and untouched) into `/opt/jetty-dist/` and becomes the `${jetty.home}` directory for this demonstration.
-. The `curl` command downloads the replacement `logging.mod` and puts it into the `${jetty.base}/modules/` directory for use by mybase only.
-. The `start.jar --add-to-start=logging,webapp-logging` command performs a number of steps to make the logging module available to the `${jetty.base}` configuration.
-.. Several entries are added to the `${jetty.base}/start.ini` configuration.
-* `--module=logging` is added to enable the logging module.
-* `--module=webapp-logging` is added to enable the webapp-logging module.
-.. Required `${jetty.base}` directories are created: `${jetty.base}/logs` and `${jetty.base}/resources`.
-.. Required logging libraries are downloaded (if not present already) to the `${jetty.base}/lib/logging/` directory:
+. `mybase` is a `${jetty.base}` directory.
+. The jetty-distribution is unpacked (and untouched) into `/opt/jetty-dist/` and becomes the `${jetty.home}` directory for this demonstration.
+. The `curl` command downloads the replacement config overlay for the `${jetty.base}/modules/` directory to use.
+. The `start.jar --add-to-start=centralized-webapp-logging` command performs a number of steps to make the centralized-webapp-logging module available to the `${jetty.base}` configuration.
+.. A new `${jetty.base}/start.d/centralized-webapp-logging.ini` configuration was created.
+.. Required `${jetty.base}` directories are created: `${jetty.base}/logs` and `${jetty.base}/resources`.
+.. Required logging libraries are downloaded (if not present already) to the `${jetty.base}/lib/logging/` directory:
* `slf4j-api.jar` - API jar for Slf4j (used by most of the rest of the jars)
* `log4j-over-slf4j.jar` - Slf4j jar that captures all log4j emitted logging events
* `jul-to-slf4j.jar` - Slf4j jar that captures all java.util.logging events
* `jcl-over-slf4j.jar` - Slf4j jar that captures all commons-logging events
* `logback-classic.jar` - the Slf4j adapter jar that routes all of the captured logging events to logback itself.
* `logback-core.jar` - the logback implementation jar, that handles all of the filtering and output of the logging events.
-.. Required webapp-logging library is downloaded (if not present already) to the `${jetty.base}/lib/webapp-logging/` directory:
+.. Required webapp-logging library is downloaded (if not present already) to the `${jetty.base}/lib/webapp-logging/` directory:
* `jetty-webapp-logging.jar` - the Jetty side deployment manger app-lifecycle bindings for modifying the `WebAppClassloaders` of deployed webapps.
-.. Required configuration files are downloaded (if not present already) to the `${jetty.base}/resources/` directory: `jetty-logging.properties`, and `logback.xml`.
-.. Required initialization commands are downloaded (if not present already) to the `${jetty.base}/etc/` directory: `jetty-logging.xml`, `jetty-webapp-logging.xml`, and `jetty-mdc-handler.xml`.
At this point the Jetty `mybase` is configured so that the jetty server itself will log using slf4j, and all other logging events from other Jetty Server components (such as database drivers, security layers, jsp, mail, and other 3rd party server components) are routed to logback for filtering and output.
@@ -109,4 +140,5 @@ All webapps deployed via the `DeploymentManager` have their `WebAppClassLoader`
The server classpath can be verified by using the `start.jar --list-config` command.
-In essence, Jetty is now configured to emit its own logging events to slf4j, and various slf4j bridge jars are acting on behalf of log4j, `java.util.logging`, and `commons-logging`, routing all of the logging events to logback (a slf4j adapter) for routing (to console, file, etc...).
+In essence, Jetty is now configured to emit its own logging events to slf4j, and various slf4j bridge jars are acting on behalf of `log4j`, `java.util.logging`, and `commons-logging`, routing all of the logging events to `logback`
+(a slf4j implementation) for routing (to console, file, etc...).
From 6e0b5f387b4dfb0cc5461b4b6581c1fad9602dd7 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Sat, 4 Apr 2020 11:50:48 +0200
Subject: [PATCH 037/101] Improvements to the Jetty client documentation,
connection pool section.
Signed-off-by: Simone Bordet
---
.../http/client-http-authentication.adoc | 8 +-
.../client/http/client-http-intro.adoc | 76 +++++++++++++++++--
.../client/http/client-http-proxy.adoc | 22 +++---
.../embedded/client/http/HTTPClientDocs.java | 47 ++++++++++++
4 files changed, 133 insertions(+), 20 deletions(-)
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-authentication.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-authentication.adoc
index fcdd2fc3b01..40a8addb58d 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-authentication.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-authentication.adoc
@@ -37,10 +37,10 @@ participant Application
participant HttpClient
participant Server
-Application -> Server: GET /path
-Server -> HttpClient: 401 + WWW-Authenticate
-HttpClient -> Server: GET + Authentication
-Server -> Application: 200 OK
+Application -> Server : GET /path
+Server -> HttpClient : 401 + WWW-Authenticate
+HttpClient -> Server : GET + Authentication
+Server -> Application : 200 OK
----
Upon receiving a HTTP 401 response code, `HttpClient` looks at the
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc
index 6492e3fe1b8..acd412130c8 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc
@@ -108,8 +108,8 @@ following components:
* a set of _destinations_.
A _destination_ is the client-side component that represent an _origin_ on
-a server, and manages a queue of requests for that origin, and a pool of
-connections to that origin.
+a server, and manages a queue of requests for that origin, and a
+link:#client-http-connection-pool[pool of connections] to that origin.
An _origin_ may be simply thought as the tuple `(scheme, host, port)` and it
is where the client connects to in order to communicate with the server.
@@ -145,9 +145,75 @@ connection pools.
Therefore an origin is identified by the tuple
`(scheme, host, port, tag, protocol)`.
+[[client-http-connection-pool]]
+==== HttpClient Connection Pooling
+
+A destination manages a `org.eclipse.jetty.client.ConnectionPool`, where
+connections to a particular origin are pooled for performance reasons:
+opening a connection is a costly operation and it's better to reuse them
+for multiple requests.
+
+NOTE: Remember that to select a specific destination you must select a
+specific origin, and that an origin is identified by the tuple
+`(scheme, host, port, tag, protocol)`, so you can have multiple destinations
+for the same `host` and `port`.
+
+You can access the `ConnectionPool` in this way:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=getConnectionPool]
+----
+
+Jetty's client library provides the following `ConnectionPool` implementations:
+
+* `DuplexConnectionPool`, historically the first implementation, only used by
+the HTTP/1.1 transport.
+* `MultiplexConnectionPool`, the generic implementation valid for any transport
+where connections are reused with a MRU (most recently used) algorithm (that is,
+the connections most recently returned to the connection pool are the more
+likely to be used again).
+* `RoundRobinConnectionPool`, similar to `MultiplexConnectionPool` but where
+connections are reused with a round-robin algorithm.
+
+The `ConnectionPool` implementation can be customized for each destination in
+by setting a `ConnectionPool.Factory` on the `HttpClientTransport`:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=setConnectionPool]
+----
+
[[client-http-request-processing]]
==== HttpClient Request Processing
+[plantuml]
+----
+skinparam backgroundColor transparent
+skinparam monochrome true
+skinparam shadowing false
+
+participant Application
+participant Request
+participant HttpClient
+participant Destination
+participant ConnectionPool
+participant Connection
+
+Application -> HttpClient : newRequest()
+HttpClient -> Request **
+Application -> Request : send()
+Request -> HttpClient : send()
+HttpClient -> Destination ** : get or create
+Destination -> ConnectionPool ** : create
+HttpClient -> Destination : send(Request)
+Destination -> Destination : enqueue(Request)
+Destination -> ConnectionPool : acquire()
+ConnectionPool -> Connection ** : create
+Destination -> Destination : dequeue(Request)
+Destination -> Connection : send(Request)
+----
+
When a request is sent, an origin is computed from the request; `HttpClient`
uses that origin to find (or create if it does not exist) the correspondent
destination.
@@ -160,8 +226,8 @@ and sends it over the connection.
The first request to a destination triggers the opening of the first
connection.
-A second request with the same origin sent _after_ the first will reuse the
-same connection.
+A second request with the same origin sent _after_ the first request/response
+cycle is completed will reuse the same connection.
A second request with the same origin sent _concurrently_ with the first
request will cause the opening of a second connection.
The configuration parameter `HttpClient.maxConnectionsPerDestination`
@@ -171,7 +237,7 @@ the max number of connections that can be opened for a destination.
NOTE: If opening connections to a given origin takes a long time, then
requests for that origin will queue up in the corresponding destination.
-Each connection can handle a limited number of requests.
+Each connection can handle a limited number of concurrent requests.
For HTTP/1.1, this number is always `1`: there can only be one outstanding
request for each connection.
For HTTP/2 this number is determined by the server `max_concurrent_stream`
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-proxy.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-proxy.adoc
index 88554725612..cea87ff73a1 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-proxy.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-proxy.adoc
@@ -71,17 +71,17 @@ participant HttpClient
participant Proxy
participant Server
-Application -> Proxy: GET /path
-Proxy -> HttpClient: 407 + Proxy-Authenticate
-HttpClient -> Proxy: GET /path + Proxy-Authorization
-Proxy -> Server: GET /path
-Server -> Proxy: 401 + WWW-Authenticate
-Proxy -> HttpClient: 401 + WWW-Authenticate
-HttpClient -> Proxy: GET /path + Proxy-Authorization + Authorization
-Proxy -> Server: GET /path + Authorization
-Server -> Proxy: 200 OK
-Proxy -> HttpClient: 200 OK
-HttpClient -> Application: 200 OK
+Application -> Proxy : GET /path
+Proxy -> HttpClient : 407 + Proxy-Authenticate
+HttpClient -> Proxy : GET /path + Proxy-Authorization
+Proxy -> Server : GET /path
+Server -> Proxy : 401 + WWW-Authenticate
+Proxy -> HttpClient : 401 + WWW-Authenticate
+HttpClient -> Proxy : GET /path + Proxy-Authorization + Authorization
+Proxy -> Server : GET /path + Authorization
+Server -> Proxy : 200 OK
+Proxy -> HttpClient : 200 OK
+HttpClient -> Application : 200 OK
----
The application does not receive events related to the responses with code 407
diff --git a/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java b/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
index f72655f916f..60161cf3c21 100644
--- a/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
+++ b/jetty-documentation/src/main/java/embedded/client/http/HTTPClientDocs.java
@@ -31,9 +31,13 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.LongConsumer;
+import org.eclipse.jetty.client.ConnectionPool;
import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpClientTransport;
+import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpProxy;
import org.eclipse.jetty.client.ProxyConfiguration;
+import org.eclipse.jetty.client.RoundRobinConnectionPool;
import org.eclipse.jetty.client.api.Authentication;
import org.eclipse.jetty.client.api.AuthenticationStore;
import org.eclipse.jetty.client.api.ContentResponse;
@@ -800,4 +804,47 @@ public class HTTPClientDocs
.send();
// end::dynamicClearText[]
}
+
+ public void getConnectionPool() throws Exception
+ {
+ // tag::getConnectionPool[]
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ ConnectionPool connectionPool = httpClient.getDestinations().stream()
+ // Cast to HttpDestination.
+ .map(HttpDestination.class::cast)
+ // Find the destination by filtering on the Origin.
+ .filter(destination -> destination.getOrigin().getAddress().getHost().equals("domain.com"))
+ .findAny()
+ // Get the ConnectionPool.
+ .map(HttpDestination::getConnectionPool)
+ .orElse(null);
+ // end::getConnectionPool[]
+ }
+
+ public void setConnectionPool() throws Exception
+ {
+ // tag::setConnectionPool[]
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ // The max number of connections in the pool.
+ int maxConnectionsPerDestination = httpClient.getMaxConnectionsPerDestination();
+
+ // The max number of requests per connection (multiplexing).
+ // Start with 1, since this value is dynamically set to larger values if
+ // the transport supports multiplexing requests on the same connection.
+ int maxRequestsPerConnection = 1;
+
+ HttpClientTransport transport = httpClient.getTransport();
+
+ // Set the ConnectionPool.Factory using a lambda.
+ transport.setConnectionPoolFactory(destination ->
+ new RoundRobinConnectionPool(destination,
+ maxConnectionsPerDestination,
+ destination,
+ maxRequestsPerConnection));
+ // end::setConnectionPool[]
+ }
}
From 664cb81e6ddba5ac3d8683864228081f4ba61972 Mon Sep 17 00:00:00 2001
From: Lachlan Roberts
Date: Mon, 6 Apr 2020 07:48:14 +1000
Subject: [PATCH 038/101] Issue #4747 - Default close status code should be
normal status
Signed-off-by: Lachlan Roberts
---
.../jetty/websocket/javax/common/JavaxWebSocketSession.java | 2 +-
.../jetty/websocket/javax/tests/JettySpecificConfigTest.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java
index 9cbe7d6056d..eed6587ab10 100644
--- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java
+++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java
@@ -179,7 +179,7 @@ public class JavaxWebSocketSession implements javax.websocket.Session
@Override
public void close()
{
- close(new CloseReason(CloseReason.CloseCodes.NO_STATUS_CODE, null));
+ close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, null));
}
/**
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/JettySpecificConfigTest.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/JettySpecificConfigTest.java
index 4c48c350898..e1c61dab3a2 100644
--- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/JettySpecificConfigTest.java
+++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/JettySpecificConfigTest.java
@@ -146,7 +146,7 @@ public class JettySpecificConfigTest
// Close the Session.
session.close();
assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
- assertThat(clientEndpoint.closeReason.getCloseCode(), is(CloseReason.CloseCodes.NO_STATUS_CODE));
+ assertThat(clientEndpoint.closeReason.getCloseCode(), is(CloseReason.CloseCodes.NORMAL_CLOSURE));
assertNull(clientEndpoint.error);
}
}
From 2028b99e83f1304388ab572edc40682e3e7a0823 Mon Sep 17 00:00:00 2001
From: Lachlan Roberts
Date: Mon, 6 Apr 2020 11:54:16 +1000
Subject: [PATCH 039/101] Issue #4747 - SessionID should return same String
instance
Using the object hash code is not random enough to use as a unique ID.
Now using UUID.randomUUID() instead which sufficiently random.
Signed-off-by: Lachlan Roberts
---
.../jetty/websocket/javax/common/JavaxWebSocketSession.java | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java
index eed6587ab10..7c40cd411a2 100644
--- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java
+++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java
@@ -28,6 +28,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.websocket.CloseReason;
@@ -62,6 +63,7 @@ public class JavaxWebSocketSession implements javax.websocket.Session
private final AvailableDecoders availableDecoders;
private final AvailableEncoders availableEncoders;
private final Map pathParameters;
+ private final String sessionId;
private Map userProperties;
private List negotiatedExtensions;
@@ -76,8 +78,8 @@ public class JavaxWebSocketSession implements javax.websocket.Session
this.container = container;
this.coreSession = coreSession;
this.frameHandler = frameHandler;
+ this.sessionId = UUID.randomUUID().toString();
this.config = Objects.requireNonNull(endpointConfig);
-
this.availableDecoders = new AvailableDecoders(this.config);
this.availableEncoders = new AvailableEncoders(this.config);
@@ -315,7 +317,7 @@ public class JavaxWebSocketSession implements javax.websocket.Session
@Override
public String getId()
{
- return this.frameHandler.getUpgradeRequest().toString();
+ return sessionId;
}
/**
From cf0e3c530f4ab878c8e0d83449fd9225ed8446f4 Mon Sep 17 00:00:00 2001
From: Jan Bartel
Date: Mon, 6 Apr 2020 10:12:19 +0200
Subject: [PATCH 040/101] Issue #4662 Ensure contextDestroyed called after
filters and servlets destroyed (#4667)
* Issue #4662 Ensure contextDestroyed called after filters and servlets destroyed
Signed-off-by: Jan Bartel
---
.../jetty/server/handler/ContextHandler.java | 128 ++++++++++++-----
.../server/handler/ContextHandlerTest.java | 37 +++++
.../eclipse/jetty/servlet/ServletHandler.java | 52 ++++---
.../servlet/ServletContextHandlerTest.java | 136 ++++++++++++++++++
.../jetty/servlet/ServletLifeCycleTest.java | 8 +-
5 files changed, 303 insertions(+), 58 deletions(-)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
index 764cc78a5f8..725aeb5f40d 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
@@ -182,6 +182,14 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
__serverInfo = serverInfo;
}
+ public enum ContextStatus
+ {
+ NOTSET,
+ INITIALIZED,
+ DESTROYED
+ }
+
+ protected ContextStatus _contextStatus = ContextStatus.NOTSET;
protected Context _scontext;
private final AttributesMap _attributes;
private final Map _initParams;
@@ -828,6 +836,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
// defers the calling of super.doStart()
startContext();
+
+ contextInitialized();
_availability = Availability.AVAILABLE;
LOG.info("Started {}", this);
@@ -886,49 +896,97 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
addEventListener(new ManagedAttributeListener(this, StringUtil.csvSplit(managedAttributes)));
super.doStart();
+ }
+ /**
+ * Call the ServletContextListeners contextInitialized methods.
+ * This can be called from a ServletHandler during the proper sequence
+ * of initializing filters, servlets and listeners. However, if there is
+ * no ServletHandler, the ContextHandler will call this method during
+ * doStart().
+ *
+ * @throws Exception
+ */
+ public void contextInitialized() throws Exception
+ {
// Call context listeners
- _destroyServletContextListeners.clear();
- if (!_servletContextListeners.isEmpty())
+ switch (_contextStatus)
{
- ServletContextEvent event = new ServletContextEvent(_scontext);
- for (ServletContextListener listener : _servletContextListeners)
+ case NOTSET:
{
- callContextInitialized(listener, event);
- _destroyServletContextListeners.add(listener);
+ try
+ {
+ _destroyServletContextListeners.clear();
+ if (!_servletContextListeners.isEmpty())
+ {
+ ServletContextEvent event = new ServletContextEvent(_scontext);
+ for (ServletContextListener listener : _servletContextListeners)
+ {
+ callContextInitialized(listener, event);
+ _destroyServletContextListeners.add(listener);
+ }
+ }
+ }
+ finally
+ {
+ _contextStatus = ContextStatus.INITIALIZED;
+ }
+ break;
}
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Call the ServletContextListeners with contextDestroyed.
+ * This method can be called from a ServletHandler in the
+ * proper sequence of destroying filters, servlets and listeners.
+ * If there is no ServletHandler, the ContextHandler must ensure
+ * these listeners are called instead.
+ *
+ * @throws Exception
+ */
+ public void contextDestroyed() throws Exception
+ {
+ switch (_contextStatus)
+ {
+ case INITIALIZED:
+ {
+ try
+ {
+ //Call context listeners
+ MultiException ex = new MultiException();
+ ServletContextEvent event = new ServletContextEvent(_scontext);
+ Collections.reverse(_destroyServletContextListeners);
+ for (ServletContextListener listener : _destroyServletContextListeners)
+ {
+ try
+ {
+ callContextDestroyed(listener, event);
+ }
+ catch (Exception x)
+ {
+ ex.add(x);
+ }
+ }
+ ex.ifExceptionThrow();
+ }
+ finally
+ {
+ _contextStatus = ContextStatus.DESTROYED;
+ }
+ break;
+ }
+ default:
+ break;
}
}
protected void stopContext() throws Exception
{
- // Call the context listeners
- ServletContextEvent event = new ServletContextEvent(_scontext);
- Collections.reverse(_destroyServletContextListeners);
- MultiException ex = new MultiException();
- for (ServletContextListener listener : _destroyServletContextListeners)
- {
- try
- {
- callContextDestroyed(listener, event);
- }
- catch (Exception x)
- {
- ex.add(x);
- }
- }
-
// stop all the handler hierarchy
- try
- {
- super.doStop();
- }
- catch (Exception x)
- {
- ex.add(x);
- }
-
- ex.ifExceptionThrow();
+ super.doStop();
}
protected void callContextInitialized(ServletContextListener l, ServletContextEvent e)
@@ -945,9 +1003,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
l.contextDestroyed(e);
}
- /*
- * @see org.eclipse.thread.AbstractLifeCycle#doStop()
- */
@Override
protected void doStop() throws Exception
{
@@ -987,6 +1042,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
stopContext();
+ contextDestroyed();
+
// retain only durable listeners
setEventListeners(_durableListeners.toArray(new EventListener[0]));
_durableListeners.clear();
@@ -1019,6 +1076,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
}
finally
{
+ _contextStatus = ContextStatus.NOTSET;
__context.set(oldContext);
exitScope(null);
LOG.info("Stopped {}", this);
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerTest.java
index cd10401fae3..dbc2973e3a8 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerTest.java
@@ -27,6 +27,9 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -445,6 +448,22 @@ public class ContextHandlerTest
assertThat(connector.getResponse("GET /foo/xxx HTTP/1.0\n\n"), Matchers.containsString("ctx='/foo'"));
assertThat(connector.getResponse("GET /foo/bar/xxx HTTP/1.0\n\n"), Matchers.containsString("ctx='/foo/bar'"));
}
+
+ @Test
+ public void testContextInitializationDestruction() throws Exception
+ {
+ Server server = new Server();
+ ContextHandlerCollection contexts = new ContextHandlerCollection();
+ server.setHandler(contexts);
+
+ ContextHandler noServlets = new ContextHandler(contexts, "/noservlets");
+ TestServletContextListener listener = new TestServletContextListener();
+ noServlets.addEventListener(listener);
+ server.start();
+ assertEquals(1, listener.initialized);
+ server.stop();
+ assertEquals(1, listener.destroyed);
+ }
@Test
public void testContextVirtualGetContext() throws Exception
@@ -840,4 +859,22 @@ public class ContextHandlerTest
writer.println("ctx='" + request.getContextPath() + "'");
}
}
+
+ private static class TestServletContextListener implements ServletContextListener
+ {
+ public int initialized = 0;
+ public int destroyed = 0;
+
+ @Override
+ public void contextInitialized(ServletContextEvent sce)
+ {
+ initialized++;
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent sce)
+ {
+ destroyed++;
+ }
+ }
}
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
index 4f51a5605b7..eebc610f0b3 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
@@ -33,6 +33,7 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
+import java.util.function.Consumer;
import java.util.stream.Stream;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
@@ -307,6 +308,9 @@ public class ServletHandler extends ScopedHandler
_servlets = shs;
ServletMapping[] sms = (ServletMapping[])LazyList.toArray(servletMappings, ServletMapping.class);
_servletMappings = sms;
+
+ if (_contextHandler != null)
+ _contextHandler.contextDestroyed();
//Retain only Listeners added via jetty apis (is Source.EMBEDDED)
List listenerHolders = new ArrayList<>();
@@ -733,30 +737,40 @@ public class ServletHandler extends ScopedHandler
public void initialize()
throws Exception
{
- _initialized = true;
-
MultiException mx = new MultiException();
- Stream.concat(Stream.concat(
- Arrays.stream(_filters),
- Arrays.stream(_servlets).sorted()),
- Arrays.stream(_listeners))
- .forEach(h ->
+ Consumer> c = h ->
+ {
+ try
{
- try
+ if (!h.isStarted())
{
- if (!h.isStarted())
- {
- h.start();
- h.initialize();
- }
+ h.start();
+ h.initialize();
}
- catch (Throwable e)
- {
- LOG.debug(Log.EXCEPTION, e);
- mx.add(e);
- }
- });
+ }
+ catch (Throwable e)
+ {
+ LOG.debug(Log.EXCEPTION, e);
+ mx.add(e);
+ }
+ };
+
+ //Start the listeners so we can call them
+ Arrays.stream(_listeners).forEach(c);
+
+ //call listeners contextInitialized
+ if (_contextHandler != null)
+ _contextHandler.contextInitialized();
+
+ //Only set initialized true AFTER the listeners have been called
+ _initialized = true;
+
+ //Start the filters then the servlets
+ Stream.concat(
+ Arrays.stream(_filters),
+ Arrays.stream(_servlets).sorted())
+ .forEach(c);
mx.ifExceptionThrow();
}
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java
index 771c1931571..7b9a4e885e9 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java
@@ -35,6 +35,7 @@ import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.FilterRegistration;
+import javax.servlet.GenericServlet;
import javax.servlet.Servlet;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
@@ -106,6 +107,75 @@ public class ServletContextHandlerTest
private LocalConnector _connector;
private static final AtomicInteger __testServlets = new AtomicInteger();
+ private static int __initIndex = 0;
+ private static int __destroyIndex = 0;
+
+ public class StopTestFilter implements Filter
+ {
+ int _initIndex;
+ int _destroyIndex;
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException
+ {
+ _initIndex = __initIndex++;
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
+ ServletException
+ {
+ }
+
+ @Override
+ public void destroy()
+ {
+ _destroyIndex = __destroyIndex++;
+ }
+ }
+
+ public class StopTestServlet extends GenericServlet
+ {
+ int _initIndex;
+ int _destroyIndex;
+
+ @Override
+ public void destroy()
+ {
+ _destroyIndex = __destroyIndex++;
+ super.destroy();
+ }
+
+ @Override
+ public void init() throws ServletException
+ {
+ _initIndex = __initIndex++;
+ super.init();
+ }
+
+ @Override
+ public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
+ {
+ }
+ }
+
+ public class StopTestListener implements ServletContextListener
+ {
+ int _initIndex;
+ int _destroyIndex;
+
+ @Override
+ public void contextInitialized(ServletContextEvent sce)
+ {
+ _initIndex = __initIndex++;
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent sce)
+ {
+ _destroyIndex = __destroyIndex++;
+ }
+ }
public static class MySCI implements ServletContainerInitializer
{
@@ -405,6 +475,37 @@ public class ServletContextHandlerTest
_server.join();
}
+ @Test
+ public void testDestroyOrder() throws Exception
+ {
+ ContextHandlerCollection contexts = new ContextHandlerCollection();
+ _server.setHandler(contexts);
+
+ ServletContextHandler root = new ServletContextHandler(contexts, "/", ServletContextHandler.SESSIONS);
+ ListenerHolder listenerHolder = new ListenerHolder();
+ StopTestListener stopTestListener = new StopTestListener();
+ listenerHolder.setListener(stopTestListener);
+ root.getServletHandler().addListener(listenerHolder);
+ ServletHolder servletHolder = new ServletHolder();
+ StopTestServlet stopTestServlet = new StopTestServlet();
+ servletHolder.setServlet(stopTestServlet);
+ root.addServlet(servletHolder, "/test");
+ FilterHolder filterHolder = new FilterHolder();
+ StopTestFilter stopTestFilter = new StopTestFilter();
+ filterHolder.setFilter(stopTestFilter);
+ root.addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
+ _server.start();
+ _server.stop();
+
+ assertEquals(0, stopTestListener._initIndex); //listeners contextInitialized called first
+ assertEquals(1, stopTestFilter._initIndex); //filters init
+ assertEquals(2, stopTestServlet._initIndex); //servlets init
+
+ assertEquals(0, stopTestFilter._destroyIndex); //filters destroyed first
+ assertEquals(1, stopTestServlet._destroyIndex); //servlets destroyed next
+ assertEquals(2, stopTestListener._destroyIndex); //listener contextDestroyed last
+ }
+
@Test
public void testAddSessionListener() throws Exception
{
@@ -436,6 +537,41 @@ public class ServletContextHandlerTest
assertTrue((Boolean)root.getServletContext().getAttribute("MyContextListener.contextInitialized"));
}
+ @Test
+ public void testContextInitializationDestruction() throws Exception
+ {
+ Server server = new Server();
+ ContextHandlerCollection contexts = new ContextHandlerCollection();
+ server.setHandler(contexts);
+
+ ServletContextHandler root = new ServletContextHandler(contexts, "/");
+ class TestServletContextListener implements ServletContextListener
+ {
+ public int initialized = 0;
+ public int destroyed = 0;
+
+ @Override
+ public void contextInitialized(ServletContextEvent sce)
+ {
+ initialized++;
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent sce)
+ {
+ destroyed++;
+ }
+ }
+
+ TestServletContextListener listener = new TestServletContextListener();
+ root.addEventListener(listener);
+ server.start();
+ server.stop();
+ assertEquals(1, listener.initialized);
+ server.stop();
+ assertEquals(1, listener.destroyed);
+ }
+
@Test
public void testListenersFromContextListener() throws Exception
{
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletLifeCycleTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletLifeCycleTest.java
index f41c3bbbf93..4587572027f 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletLifeCycleTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletLifeCycleTest.java
@@ -60,8 +60,8 @@ public class ServletLifeCycleTest
context.getObjectFactory().addDecorator(new TestDecorator());
ServletHandler sh = context.getServletHandler();
- sh.addListener(new ListenerHolder(TestListener.class));
- context.addEventListener(context.getServletContext().createListener(TestListener2.class));
+ sh.addListener(new ListenerHolder(TestListener.class)); //added directly to ServletHandler
+ context.addEventListener(context.getServletContext().createListener(TestListener2.class));//create,decorate and add listener to context - no holder!
sh.addFilterWithMapping(TestFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
sh.addFilterWithMapping(new FilterHolder(context.getServletContext().createFilter(TestFilter2.class)), "/*", EnumSet.of(DispatcherType.REQUEST));
@@ -110,8 +110,6 @@ public class ServletLifeCycleTest
server.stop();
assertThat(events, Matchers.contains(
- "contextDestroyed class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestListener",
- "contextDestroyed class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestListener2",
"destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestFilter2",
"Destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestFilter2",
"destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestFilter",
@@ -122,6 +120,8 @@ public class ServletLifeCycleTest
"destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestServlet2",
"Destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestServlet",
"destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestServlet",
+ "contextDestroyed class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestListener",
+ "contextDestroyed class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestListener2",
"Destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestListener"
));
From 4b2842265ac80641f88447de9f23666925cf2b7b Mon Sep 17 00:00:00 2001
From: Jan Bartel
Date: Mon, 6 Apr 2020 11:30:33 +0200
Subject: [PATCH 041/101] Issue #4737 Ensure lifecycle callbacks happen for
run-as and non async servlets (#4744)
* Issue #4737 Ensure lifecycle callbacks happen for run-as and non async servlets
Signed-off-by: Jan Bartel
---
.../LifeCycleCallbackCollectionTest.java | 100 ++++++++++++++++++
.../eclipse/jetty/servlet/ServletHolder.java | 17 ++-
2 files changed, 116 insertions(+), 1 deletion(-)
diff --git a/jetty-plus/src/test/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollectionTest.java b/jetty-plus/src/test/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollectionTest.java
index a6d3466f445..9916b57ab09 100644
--- a/jetty-plus/src/test/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollectionTest.java
+++ b/jetty-plus/src/test/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollectionTest.java
@@ -20,15 +20,38 @@ package org.eclipse.jetty.plus.annotation;
import java.lang.reflect.Method;
+import javax.servlet.http.HttpServlet;
+
+import org.eclipse.jetty.plus.webapp.PlusDecorator;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.webapp.WebAppContext;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
public class LifeCycleCallbackCollectionTest
{
+ public static class TestServlet extends HttpServlet
+ {
+ public static int postConstructCount = 0;
+ public static int preDestroyCount = 0;
+
+ public void postconstruct()
+ {
+ ++postConstructCount;
+ }
+
+ public void predestroy()
+ {
+ ++preDestroyCount;
+ }
+ }
/**
* An unsupported lifecycle callback type
@@ -154,6 +177,83 @@ public class LifeCycleCallbackCollectionTest
//expected
}
}
+
+ @Test
+ public void testServletPostConstructPreDestroy() throws Exception
+ {
+ Server server = new Server();
+ WebAppContext context = new WebAppContext();
+ context.setResourceBase(MavenTestingUtils.getTargetTestingDir("predestroy-test").toURI().toURL().toString());
+ context.setContextPath("/");
+ server.setHandler(context);
+
+ //add a non-async servlet
+ ServletHolder notAsync = new ServletHolder();
+ notAsync.setHeldClass(TestServlet.class);
+ notAsync.setName("notAsync");
+ notAsync.setAsyncSupported(false);
+ notAsync.setInitOrder(1);
+ context.getServletHandler().addServletWithMapping(notAsync, "/notasync/*");
+
+ //add an async servlet
+ ServletHolder async = new ServletHolder();
+ async.setHeldClass(TestServlet.class);
+ async.setName("async");
+ async.setAsyncSupported(true);
+ async.setInitOrder(1);
+ context.getServletHandler().addServletWithMapping(async, "/async/*");
+
+ //add a run-as servlet
+ ServletHolder runas = new ServletHolder();
+ runas.setHeldClass(TestServlet.class);
+ runas.setName("runas");
+ runas.setRunAsRole("admin");
+ runas.setInitOrder(1);
+ context.getServletHandler().addServletWithMapping(runas, "/runas/*");
+
+ //add both run-as and non async servlet
+ ServletHolder both = new ServletHolder();
+ both.setHeldClass(TestServlet.class);
+ both.setName("both");
+ both.setRunAsRole("admin");
+ both.setAsyncSupported(false);
+ both.setInitOrder(1);
+ context.getServletHandler().addServletWithMapping(both, "/both/*");
+
+ //Make fake lifecycle callbacks for all servlets
+ LifeCycleCallbackCollection collection = new LifeCycleCallbackCollection();
+ context.setAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION, collection);
+ PostConstructCallback pcNotAsync = new PostConstructCallback(TestServlet.class, "postconstruct");
+ collection.add(pcNotAsync);
+ PreDestroyCallback pdNotAsync = new PreDestroyCallback(TestServlet.class, "predestroy");
+ collection.add(pdNotAsync);
+
+ PostConstructCallback pcAsync = new PostConstructCallback(TestServlet.class, "postconstruct");
+ collection.add(pcAsync);
+ PreDestroyCallback pdAsync = new PreDestroyCallback(TestServlet.class, "predestroy");
+ collection.add(pdAsync);
+
+ PostConstructCallback pcRunAs = new PostConstructCallback(TestServlet.class, "postconstruct");
+ collection.add(pcRunAs);
+ PreDestroyCallback pdRunAs = new PreDestroyCallback(TestServlet.class, "predestroy");
+ collection.add(pdRunAs);
+
+ PostConstructCallback pcBoth = new PostConstructCallback(TestServlet.class, "postconstruct");
+ collection.add(pcBoth);
+ PreDestroyCallback pdBoth = new PreDestroyCallback(TestServlet.class, "predestroy");
+ collection.add(pdBoth);
+
+ //ensure we invoke the lifecyclecallbacks
+ context.getObjectFactory().addDecorator(new PlusDecorator(context));
+
+ server.start();
+
+ assertEquals(4, TestServlet.postConstructCount);
+
+ server.stop();
+
+ assertEquals(4, TestServlet.preDestroyCount);
+ }
@Test
public void testAddForPreDestroy() throws Exception
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
index 20d31710ea6..28c4974ba6f 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
@@ -457,7 +457,14 @@ public class ServletHolder extends Holder implements UserIdentity.Scope
if (o == null)
return;
Servlet servlet = ((Servlet)o);
- getServletHandler().destroyServlet(servlet);
+ //need to use the unwrapped servlet because lifecycle callbacks such as
+ //postconstruct and predestroy are based off the classname and the wrapper
+ //classes are unknown outside the ServletHolder
+ Servlet unwrapped = servlet;
+ while (WrapperServlet.class.isAssignableFrom(unwrapped.getClass()))
+ unwrapped = ((WrapperServlet)unwrapped).getWrappedServlet();
+ getServletHandler().destroyServlet(unwrapped);
+ //destroy the wrapped servlet, in case there is special behaviour
servlet.destroy();
}
@@ -1304,6 +1311,14 @@ public class ServletHolder extends Holder implements UserIdentity.Scope
{
_servlet.destroy();
}
+
+ /**
+ * @return the original servlet
+ */
+ public Servlet getWrappedServlet()
+ {
+ return _servlet;
+ }
@Override
public String toString()
From 8eb4bb98a4c204ac3e8db038759257eb5668e21a Mon Sep 17 00:00:00 2001
From: Jan Bartel
Date: Mon, 6 Apr 2020 11:33:09 +0200
Subject: [PATCH 042/101] @RunAs not honoured (#4743)
* Issue #4739 Fix @RunAs
Signed-off-by: Jan Bartel
---
.../annotations/RunAsAnnotationHandler.java | 10 +---
.../annotations/TestRunAsAnnotation.java | 60 +++++++++++++++++++
.../eclipse/jetty/plus/annotation/RunAs.java | 2 +
.../plus/annotation/RunAsCollection.java | 2 +
.../jetty/plus/webapp/PlusDecorator.java | 6 --
.../plus/webapp/PlusDescriptorProcessor.java | 9 ---
.../eclipse/jetty/servlet/ServletHolder.java | 31 +++++-----
7 files changed, 81 insertions(+), 39 deletions(-)
create mode 100644 jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestRunAsAnnotation.java
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/RunAsAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/RunAsAnnotationHandler.java
index 923c7002932..2e4381ad5dc 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/RunAsAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/RunAsAnnotationHandler.java
@@ -21,7 +21,6 @@ package org.eclipse.jetty.annotations;
import javax.servlet.Servlet;
import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
-import org.eclipse.jetty.plus.annotation.RunAsCollection;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -64,14 +63,7 @@ public class RunAsAnnotationHandler extends AbstractIntrospectableAnnotationHand
if (d == null)
{
metaData.setOrigin(holder.getName() + ".servlet.run-as", runAs, clazz);
- org.eclipse.jetty.plus.annotation.RunAs ra = new org.eclipse.jetty.plus.annotation.RunAs(clazz.getName(), role);
- RunAsCollection raCollection = (RunAsCollection)_context.getAttribute(RunAsCollection.RUNAS_COLLECTION);
- if (raCollection == null)
- {
- raCollection = new RunAsCollection();
- _context.setAttribute(RunAsCollection.RUNAS_COLLECTION, raCollection);
- }
- raCollection.add(ra);
+ holder.setRunAsRole(role);
}
}
}
diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestRunAsAnnotation.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestRunAsAnnotation.java
new file mode 100644
index 00000000000..e79a4ffb286
--- /dev/null
+++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestRunAsAnnotation.java
@@ -0,0 +1,60 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.annotations;
+
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.webapp.WebDescriptor;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class TestRunAsAnnotation
+{
+ @Test
+ public void testRunAsAnnotation() throws Exception
+ {
+ WebAppContext wac = new WebAppContext();
+
+ //pre-add a servlet but not by descriptor
+ ServletHolder holder = new ServletHolder();
+ holder.setName("foo1");
+ holder.setHeldClass(ServletC.class);
+ holder.setInitOrder(1); //load on startup
+ wac.getServletHandler().addServletWithMapping(holder, "/foo/*");
+
+ //add another servlet of the same class, but as if by descriptor
+ ServletHolder holder2 = new ServletHolder();
+ holder2.setName("foo2");
+ holder2.setHeldClass(ServletC.class);
+ holder2.setInitOrder(1);
+ wac.getServletHandler().addServletWithMapping(holder2, "/foo2/*");
+ wac.getMetaData().setOrigin(holder2.getName() + ".servlet.run-as", new WebDescriptor(null));
+
+ AnnotationIntrospector parser = new AnnotationIntrospector();
+ RunAsAnnotationHandler handler = new RunAsAnnotationHandler(wac);
+ parser.registerHandler(handler);
+ parser.introspect(ServletC.class);
+
+ assertEquals("admin", holder.getRunAsRole());
+ assertEquals(null, holder2.getRunAsRole());
+
+
+ }
+}
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/RunAs.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/RunAs.java
index 09169337700..b5ccfb71738 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/RunAs.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/RunAs.java
@@ -26,7 +26,9 @@ import org.eclipse.jetty.servlet.ServletHolder;
* RunAs
*
* Represents a <run-as> element in web.xml, or a @RunAs annotation.
+ * @deprecated unused as of 9.4.28 due for removal in 10.0.0
*/
+@Deprecated
public class RunAs
{
private String _className;
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/RunAsCollection.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/RunAsCollection.java
index 86f7dd682ff..ad43dd080c3 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/RunAsCollection.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/RunAsCollection.java
@@ -27,7 +27,9 @@ import org.eclipse.jetty.util.log.Logger;
/**
* RunAsCollection
+ * @deprecated class unused as of 9.4.28 due for removal in 10.0.0
*/
+@Deprecated
public class RunAsCollection
{
private static final Logger LOG = Log.getLogger(RunAsCollection.class);
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDecorator.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDecorator.java
index be30c521292..b93d91764f6 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDecorator.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDecorator.java
@@ -20,7 +20,6 @@ package org.eclipse.jetty.plus.webapp;
import org.eclipse.jetty.plus.annotation.InjectionCollection;
import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection;
-import org.eclipse.jetty.plus.annotation.RunAsCollection;
import org.eclipse.jetty.util.Decorator;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -43,11 +42,6 @@ public class PlusDecorator implements Decorator
@Override
public Object decorate(Object o)
{
-
- RunAsCollection runAses = (RunAsCollection)_context.getAttribute(RunAsCollection.RUNAS_COLLECTION);
- if (runAses != null)
- runAses.setRunAs(o);
-
InjectionCollection injections = (InjectionCollection)_context.getAttribute(InjectionCollection.INJECTION_COLLECTION);
if (injections != null)
injections.inject(o);
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessor.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessor.java
index 410a61cba73..c434ade24b5 100644
--- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessor.java
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessor.java
@@ -20,7 +20,6 @@ package org.eclipse.jetty.plus.webapp;
import java.util.Iterator;
import java.util.Objects;
-
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
@@ -33,7 +32,6 @@ import org.eclipse.jetty.plus.annotation.LifeCycleCallback;
import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection;
import org.eclipse.jetty.plus.annotation.PostConstructCallback;
import org.eclipse.jetty.plus.annotation.PreDestroyCallback;
-import org.eclipse.jetty.plus.annotation.RunAsCollection;
import org.eclipse.jetty.plus.jndi.EnvEntry;
import org.eclipse.jetty.plus.jndi.Link;
import org.eclipse.jetty.plus.jndi.NamingEntry;
@@ -93,13 +91,6 @@ public class PlusDescriptorProcessor extends IterativeDescriptorProcessor
callbacks = new LifeCycleCallbackCollection();
context.setAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION, callbacks);
}
-
- RunAsCollection runAsCollection = (RunAsCollection)context.getAttribute(RunAsCollection.RUNAS_COLLECTION);
- if (runAsCollection == null)
- {
- runAsCollection = new RunAsCollection();
- context.setAttribute(RunAsCollection.RUNAS_COLLECTION, runAsCollection);
- }
}
/**
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
index 28c4974ba6f..8a39f77b83e 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
@@ -392,18 +392,6 @@ public class ServletHolder extends Holder implements UserIdentity.Scope
//check if we need to forcibly set load-on-startup
checkInitOnStartup();
- if (_runAsRole == null)
- {
- _identityService = null;
- _runAsToken = null;
- }
- else
- {
- _identityService = getServletHandler().getIdentityService();
- if (_identityService != null)
- _runAsToken = _identityService.newRunAsToken(_runAsRole);
- }
-
_config = new Config();
synchronized (this)
@@ -577,10 +565,23 @@ public class ServletHolder extends Holder implements UserIdentity.Scope
_servlet = newInstance();
if (_config == null)
_config = new Config();
+
+ //check run-as rolename and convert to token from IdentityService
+ if (_runAsRole == null)
+ {
+ _identityService = null;
+ _runAsToken = null;
+ }
+ else
+ {
+ _identityService = getServletHandler().getIdentityService();
+ if (_identityService != null)
+ {
- // Handle run as
- if (_identityService != null && _runAsToken != null)
- _servlet = new RunAsServlet(_servlet, _identityService, _runAsToken);
+ _runAsToken = _identityService.newRunAsToken(_runAsRole);
+ _servlet = new RunAsServlet(_servlet, _identityService, _runAsToken);
+ }
+ }
if (!isAsyncSupported())
_servlet = new NotAsyncServlet(_servlet);
From 3e2f27c197783c10ed00f477fe904623294f3f8c Mon Sep 17 00:00:00 2001
From: Jan Bartel
Date: Mon, 6 Apr 2020 11:35:57 +0200
Subject: [PATCH 043/101] Issue #4683 Fix jetty-slf4j-impl for osgi (#4684)
* Issue #4683 Fix jetty-slf4j-impl for osgi
Signed-off-by: Jan Bartel
---
jetty-osgi/jetty-osgi-boot-jsp/pom.xml | 72 ++++++++++++++++++-
jetty-osgi/jetty-osgi-boot/pom.xml | 2 +-
jetty-osgi/test-jetty-osgi/README.txt | 32 +++++++++
jetty-osgi/test-jetty-osgi/pom.xml | 37 ++++++----
.../main/resources/jetty-logging.properties | 1 -
.../TestJettyOSGiBootContextAsService.java | 8 +--
.../test/TestJettyOSGiBootHTTP2Conscrypt.java | 8 +--
.../osgi/test/TestJettyOSGiBootHTTP2JDK9.java | 8 +--
.../TestJettyOSGiBootWebAppAsService.java | 8 +--
.../TestJettyOSGiBootWithAnnotations.java | 11 ++-
.../test/TestJettyOSGiBootWithBundle.java | 9 +--
.../TestJettyOSGiBootWithJavaxWebSocket.java | 12 ++--
.../osgi/test/TestJettyOSGiBootWithJsp.java | 7 +-
.../test/TestJettyOSGiBootWithWebSocket.java | 8 +--
.../eclipse/jetty/osgi/test/TestOSGiUtil.java | 54 +++++++++++++-
.../test/resources/jetty-logging.properties | 1 +
.../src/test/resources/log4j.xml | 28 --------
jetty-slf4j-impl/pom.xml | 5 +-
18 files changed, 212 insertions(+), 99 deletions(-)
create mode 100644 jetty-osgi/test-jetty-osgi/README.txt
delete mode 100644 jetty-osgi/test-jetty-osgi/src/main/resources/jetty-logging.properties
create mode 100644 jetty-osgi/test-jetty-osgi/src/test/resources/jetty-logging.properties
delete mode 100644 jetty-osgi/test-jetty-osgi/src/test/resources/log4j.xml
diff --git a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
index b8ebe520cab..28a8ee7bb65 100644
--- a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
@@ -56,9 +56,77 @@
org.eclipse.jetty.osgi.boot!org.eclipse.jetty.osgi.boot.*
- org.eclipse.jdt.*;resolution:=optional, org.eclipse.jdt.core.compiler.*;resolution:=optional, com.sun.el;resolution:=optional, com.sun.el.lang;resolution:=optional, com.sun.el.parser;resolution:=optional, com.sun.el.util;resolution:=optional, javax.el;version="[3.0,3.1)", javax.servlet;version="[3.1,4.1)", javax.servlet.resources;version="[3.1,4.1)", javax.servlet.jsp.resources;version="[2.3,4.1)", javax.servlet.jsp;version="[2.3,2.4.1)", javax.servlet.jsp.el;version="[2.3,2.4.1)", javax.servlet.jsp.tagext;version="[2.3,2.4.1)", javax.servlet.jsp.jstl.core;version="1.2";resolution:=optional, javax.servlet.jsp.jstl.fmt;version="1.2";resolution:=optional, javax.servlet.jsp.jstl.sql;version="1.2";resolution:=optional, javax.servlet.jsp.jstl.tlv;version="1.2";resolution:=optional, org.apache.el;version="[8.0.23,10)";resolution:=optional, org.apache.el.lang;version="[8.0.23,10)";resolution:=optional, org.apache.el.stream;version="[8.0.23,10)";resolution:=optional, org.apache.el.util;version="[8.0.23,10)";resolution:=optional, org.apache.el.parser;version="[8.0.23,10)";resolution:=optional, org.apache.jasper;version="[8.0.23,10)";resolution:=optional, org.apache.jasper.compiler;version="[8.0.23,10)";resolution:=optional, org.apache.jasper.compiler.tagplugin;version="[8.0.23,10)";resolution:=optional, org.apache.jasper.runtime;version="[8.0.23,10)";resolution:=optional, org.apache.jasper.security;version="[8.0.23,10)";resolution:=optional, org.apache.jasper.servlet;version="[8.0.23,10)";resolution:=optional, org.apache.jasper.tagplugins.jstl;version="[8.0.23,10)";resolution:=optional, org.apache.jasper.util;version="[8.0.23,10)";resolution:=optional, org.apache.jasper.xmlparser;version="[8.0.23,10)";resolution:=optional, org.apache.taglibs.standard;version="1.2";resolution:=optional, org.apache.taglibs.standard.extra.spath;version="1.2";resolution:=optional, org.apache.taglibs.standard.functions;version="1.2";resolution:=optional, org.apache.taglibs.standard.lang.jstl;version="1.2";resolution:=optional, org.apache.taglibs.standard.lang.jstl.parser;version="1.2";resolution:=optional, org.apache.taglibs.standard.lang.jstl.test;version="1.2";resolution:=optional, org.apache.taglibs.standard.lang.jstl.test.beans;version="1.2";resolution:=optional, org.apache.taglibs.standard.lang.support;version="1.2";resolution:=optional, org.apache.taglibs.standard.resources;version="1.2";resolution:=optional, org.apache.taglibs.standard.tag.common.core;version="1.2";resolution:=optional, org.apache.taglibs.standard.tag.common.fmt;version="1.2";resolution:=optional, org.apache.taglibs.standard.tag.common.sql;version="1.2";resolution:=optional, org.apache.taglibs.standard.tag.common.xml;version="1.2";resolution:=optional, org.apache.taglibs.standard.tag.el.core;version="1.2";resolution:=optional, org.apache.taglibs.standard.tag.el.fmt;version="1.2";resolution:=optional, org.apache.taglibs.standard.tag.el.sql;version="1.2";resolution:=optional, org.apache.taglibs.standard.tag.el.xml;version="1.2";resolution:=optional, org.apache.taglibs.standard.tag.rt.core;version="1.2";resolution:=optional, org.apache.taglibs.standard.tag.rt.fmt;version="1.2";resolution:=optional, org.apache.taglibs.standard.tag.rt.sql;version="1.2";resolution:=optional, org.apache.taglibs.standard.tag.rt.xml;version="1.2";resolution:=optional, org.apache.taglibs.standard.tei;version="1.2";resolution:=optional, org.apache.taglibs.standard.tlv;version="1.2";resolution:=optional, org.apache.tomcat;version="[8.0.23,10)";resolution:=optional, org.eclipse.jetty.jsp;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))";resolution:=optional, org.osgi.*, org.xml.*;resolution:=optional, org.xml.sax.*;resolution:=optional, javax.xml.*;resolution:=optional, org.w3c.dom;resolution:=optional, org.w3c.dom.ls;resolution:=optional, javax.xml.parser;resolution:=optional
+
+ org.eclipse.jdt.*;resolution:=optional,
+ org.eclipse.jdt.core.compiler.*;resolution:=optional,
+ com.sun.el;resolution:=optional,
+ com.sun.el.lang;resolution:=optional,
+ com.sun.el.parser;resolution:=optional,
+ com.sun.el.util;resolution:=optional,
+ javax.el;version="[3.0,3.1)",
+ javax.servlet;version="[3.1,4.1)",
+ javax.servlet.resources;version="[3.1,4.1)",
+ javax.servlet.jsp.resources;version="[2.3,4.1)",
+ javax.servlet.jsp;version="[2.3,2.4.1)",
+ javax.servlet.jsp.el;version="[2.3,2.4.1)",
+ javax.servlet.jsp.tagext;version="[2.3,2.4.1)",
+ javax.servlet.jsp.jstl.core;version="1.2";resolution:=optional,
+ javax.servlet.jsp.jstl.fmt;version="1.2";resolution:=optional,
+ javax.servlet.jsp.jstl.sql;version="1.2";resolution:=optional,
+ javax.servlet.jsp.jstl.tlv;version="1.2";resolution:=optional,
+ org.apache.el;version="[8.0.23,10)";resolution:=optional,
+ org.apache.el.lang;version="[8.0.23,10)";resolution:=optional,
+ org.apache.el.stream;version="[8.0.23,10)";resolution:=optional,
+ org.apache.el.util;version="[8.0.23,10)";resolution:=optional,
+ org.apache.el.parser;version="[8.0.23,10)";resolution:=optional,
+ org.apache.jasper;version="[8.0.23,10)";resolution:=optional,
+ org.apache.jasper.compiler;version="[8.0.23,10)";resolution:=optional,
+ org.apache.jasper.compiler.tagplugin;version="[8.0.23,10)";resolution:=optional,
+ org.apache.jasper.runtime;version="[8.0.23,10)";resolution:=optional,
+ org.apache.jasper.security;version="[8.0.23,10)";resolution:=optional,
+ org.apache.jasper.servlet;version="[8.0.23,10)";resolution:=optional,
+ org.apache.jasper.tagplugins.jstl;version="[8.0.23,10)";resolution:=optional,
+ org.apache.jasper.util;version="[8.0.23,10)";resolution:=optional,
+ org.apache.jasper.xmlparser;version="[8.0.23,10)";resolution:=optional,
+ org.apache.taglibs.standard;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.extra.spath;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.functions;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.lang.jstl;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.lang.jstl.parser;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.lang.jstl.test;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.lang.jstl.test.beans;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.lang.support;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.resources;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.tag.common.core;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.tag.common.fmt;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.tag.common.sql;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.tag.common.xml;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.tag.el.core;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.tag.el.fmt;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.tag.el.sql;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.tag.el.xml;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.tag.rt.core;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.tag.rt.fmt;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.tag.rt.sql;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.tag.rt.xml;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.tei;version="1.2";resolution:=optional,
+ org.apache.taglibs.standard.tlv;version="1.2";resolution:=optional,
+ org.apache.tomcat;version="[8.0.23,10)";resolution:=optional,
+ org.eclipse.jetty.jsp;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))";resolution:=optional,
+ org.slf4j.*,
+ org.osgi.*,
+ org.xml.*;resolution:=optional,
+ org.xml.sax.*;resolution:=optional,
+ javax.xml.*;resolution:=optional,
+ org.w3c.dom;resolution:=optional,
+ org.w3c.dom.ls;resolution:=optional,
+ javax.xml.parser;resolution:=optional
- org.eclipse.jetty.jsp.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))",org.apache.jasper.*;version="8.0.23",org.apache.el.*;version="8.0.23"
+
+ org.eclipse.jetty.jsp.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))",
+ org.apache.jasper.*;version="8.0.23",
+ org.apache.el.*;version="8.0.23"
+
diff --git a/jetty-osgi/jetty-osgi-boot/pom.xml b/jetty-osgi/jetty-osgi-boot/pom.xml
index 443fe24c095..1bfdd01b97c 100644
--- a/jetty-osgi/jetty-osgi-boot/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot/pom.xml
@@ -71,7 +71,7 @@
org.eclipse.jetty.osgi.boot;singleton:=trueorg.eclipse.jetty.osgi.boot.JettyBootstrapActivatororg.eclipse.jetty.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))"
- javax.mail;version="1.4.0";resolution:=optional, javax.mail.event;version="1.4.0";resolution:=optional, javax.mail.internet;version="1.4.0";resolution:=optional, javax.mail.search;version="1.4.0";resolution:=optional, javax.mail.util;version="1.4.0";resolution:=optional, javax.servlet;version="[3.1,4.1)", javax.servlet.http;version="[3.1,4.1)", javax.transaction;version="1.1.0";resolution:=optional, javax.transaction.xa;version="1.1.0";resolution:=optional, org.objectweb.asm;version="5";resolution:=optional, org.osgi.framework, org.osgi.service.cm;version="1.2.0", org.osgi.service.packageadmin, org.osgi.service.startlevel;version="1.0.0", org.osgi.service.url;version="1.0.0", org.osgi.util.tracker;version="1.3.0", org.slf4j;resolution:=optional, org.slf4j.spi;resolution:=optional, org.slf4j.helpers;resolution:=optional, org.xml.sax, org.xml.sax.helpers, org.eclipse.jetty.annotations;resolution:=optional, *
+ javax.mail;version="1.4.0";resolution:=optional, javax.mail.event;version="1.4.0";resolution:=optional, javax.mail.internet;version="1.4.0";resolution:=optional, javax.mail.search;version="1.4.0";resolution:=optional, javax.mail.util;version="1.4.0";resolution:=optional, javax.servlet;version="[3.1,4.1)", javax.servlet.http;version="[3.1,4.1)", javax.transaction;version="1.1.0";resolution:=optional, javax.transaction.xa;version="1.1.0";resolution:=optional, org.objectweb.asm;version="5";resolution:=optional, org.osgi.framework, org.osgi.service.cm;version="1.2.0", org.osgi.service.packageadmin, org.osgi.service.startlevel;version="1.0.0", org.osgi.service.url;version="1.0.0", org.osgi.util.tracker;version="1.3.0", org.xml.sax, org.xml.sax.helpers, org.eclipse.jetty.annotations;resolution:=optional, *
osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)"
diff --git a/jetty-osgi/test-jetty-osgi/README.txt b/jetty-osgi/test-jetty-osgi/README.txt
new file mode 100644
index 00000000000..22f22dce121
--- /dev/null
+++ b/jetty-osgi/test-jetty-osgi/README.txt
@@ -0,0 +1,32 @@
+Unit Tests with OSGi
+--------------------
+
+The unit tests use PaxExam https://ops4j1.jira.com/wiki/spaces/PAXEXAM4/overview
+to fork a jvm to start an OSGi container (currently eclipse) and deploy the jetty
+jars as osgi bundles, along with the jetty-osgi infrastructure (like jetty-osgi-boot).
+
+To run all the tests:
+ mvn test
+
+To run a particular test:
+ mvn test -Dtest=[name of test]
+
+
+At the time of writing, PaxExam only works with junit-4, so you may not be
+able to invoke them easily from your IDE.
+
+Logging
+-------
+By default, very little log info comes out of the tests. If you wish to see more
+logging information, you can control this from the command line.
+
+There are 2 sources of logging information: 1) the pax environment and 2) jetty logs.
+
+To set the logging level for the pax environment use the following system property:
+
+ mvn -Dpax.exam.LEVEL=[log level]
+
+INFO, WARN and TRACE are known to work.
+
+To set the logging level for the jetty logs edit the src/test/resources/jetty-logging.properties
+to set the logging level you want and rerun your tests. The usual jetty logging levels apply.
diff --git a/jetty-osgi/test-jetty-osgi/pom.xml b/jetty-osgi/test-jetty-osgi/pom.xml
index d82c7e2dde9..01be0b6dfdb 100644
--- a/jetty-osgi/test-jetty-osgi/pom.xml
+++ b/jetty-osgi/test-jetty-osgi/pom.xml
@@ -72,18 +72,31 @@
org.ops4j.pax.tinybundlestinybundles
- 2.1.1
+ 3.0.0
+ test
+
+
+ biz.aQute.bnd
+ bnd
+
+ org.ops4j.pax.urlpax-url-wrap${pax.url.version}test
+
+
+ biz.aQute.bnd
+ bndlib
+
+ biz.aQute.bnd
- bndlib
- 2.4.0
+ biz.aQute.bndlib
+ 5.0.0org.osgi
@@ -105,6 +118,12 @@
+
+ org.eclipse.jetty
+ jetty-slf4j-impl
+ ${project.version}
+ test
+ org.eclipse.jetty.osgijetty-osgi-boot
@@ -409,18 +428,6 @@
jetty-test-helpertest
-
- org.slf4j
- slf4j-api
- ${slf4j.version}
- test
-
-
- org.slf4j
- slf4j-log4j12
- ${slf4j.version}
- test
- org.ow2.asmasm
diff --git a/jetty-osgi/test-jetty-osgi/src/main/resources/jetty-logging.properties b/jetty-osgi/test-jetty-osgi/src/main/resources/jetty-logging.properties
deleted file mode 100644
index c2be11c689a..00000000000
--- a/jetty-osgi/test-jetty-osgi/src/main/resources/jetty-logging.properties
+++ /dev/null
@@ -1 +0,0 @@
-org.eclipse.jetty.LEVEL=INFO
\ No newline at end of file
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java
index 3e34573a5bc..2e9f7393b82 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java
+++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java
@@ -51,8 +51,6 @@ import static org.ops4j.pax.exam.CoreOptions.systemProperty;
@RunWith(PaxExam.class)
public class TestJettyOSGiBootContextAsService
{
- private static final String LOG_LEVEL = "WARN";
-
@Inject
BundleContext bundleContext = null;
@@ -60,6 +58,9 @@ public class TestJettyOSGiBootContextAsService
public static Option[] configure()
{
ArrayList
*
The smaller bucket is defined as a fraction of the bigger bucket.
+ * rocking bamboo fountain,
+ * where the bamboo is the smaller bucket and the pool is the bigger bucket.
*
The algorithm works in this way.
*
The initial bigger bucket (BB) capacity is 100, and let's imagine the smaller
* bucket (SB) being 40% of the bigger bucket: 40.
@@ -50,6 +51,16 @@ import org.eclipse.jetty.util.annotation.ManagedObject;
* with delta=45.
*
The application consumes the remaining 15, so now SB=15, and no window
* control frame is emitted.
+ *
The {@code bufferRatio} controls how often the window control frame is
+ * emitted.
+ *
A {@code bufferRatio=0.0} means that a window control frame is emitted
+ * every time the application consumes a data frame. This may result in too many
+ * window control frames be emitted, but may allow the sender to avoid stalling.
+ *
A {@code bufferRatio=1.0} means that a window control frame is emitted
+ * only when the application has consumed a whole window. This minimizes the
+ * number of window control frames emitted, but may cause the sender to stall,
+ * waiting for the window control frame.
+ *
The default value is {@code bufferRatio=0.5}.
*/
@ManagedObject
public class BufferingFlowControlStrategy extends AbstractFlowControlStrategy
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java
index 097fecb5a59..6166aba962d 100644
--- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java
@@ -541,7 +541,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
synchronized (this)
{
HeadersFrame[] frameOut = new HeadersFrame[1];
- stream = newStream(frame, frameOut);
+ stream = newLocalStream(frame, frameOut);
stream.setListener(listener);
ControlEntry entry = new ControlEntry(frameOut[0], stream, new StreamPromiseCallback(promise, stream));
queued = flusher.append(entry);
@@ -567,7 +567,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
* allocated stream id, or null if not interested in the modified headers frame
* @return a new stream
*/
- public IStream newStream(HeadersFrame frameIn, HeadersFrame[] frameOut)
+ public IStream newLocalStream(HeadersFrame frameIn, HeadersFrame[] frameOut)
{
HeadersFrame frame = frameIn;
int streamId = frameIn.getStreamId();
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Session.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Session.java
index 5b5aa5c6ade..19f11d7b2ec 100644
--- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Session.java
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Session.java
@@ -21,6 +21,7 @@ package org.eclipse.jetty.http2.api;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.Map;
+import java.util.concurrent.CompletableFuture;
import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.GoAwayFrame;
@@ -55,6 +56,20 @@ import org.eclipse.jetty.util.Promise;
*/
public interface Session
{
+ /**
+ *
Sends the given HEADERS {@code frame} to create a new {@link Stream}.
+ *
+ * @param frame the HEADERS frame containing the HTTP headers
+ * @param listener the listener that gets notified of stream events
+ * @return a CompletableFuture that is notified of the stream creation
+ */
+ public default CompletableFuture newStream(HeadersFrame frame, Stream.Listener listener)
+ {
+ Promise.Completable result = new Promise.Completable<>();
+ newStream(frame, result, listener);
+ return result;
+ }
+
/**
*
Sends the given HEADERS {@code frame} to create a new {@link Stream}.
+ *
+ * @param frame the DATA frame to send
+ * @return the CompletableFuture that gets notified when the frame has been sent
+ */
+ public default CompletableFuture data(DataFrame frame)
+ {
+ Callback.Completable result = new Callback.Completable();
+ data(frame, result);
+ return result;
+ }
+
/**
*
Sends the given DATA {@code frame}.
*
@@ -174,7 +189,7 @@ public interface Stream
/**
*
Callback method invoked when a PUSH_PROMISE frame has been received.
*
- * @param stream the stream
+ * @param stream the pushed stream
* @param frame the PUSH_PROMISE frame received
* @return a Stream.Listener that will be notified of pushed stream events
*/
diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpConnectionOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpConnectionOverHTTP2.java
index 40e22015323..4f537b3fe57 100644
--- a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpConnectionOverHTTP2.java
+++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpConnectionOverHTTP2.java
@@ -108,7 +108,7 @@ public class HttpConnectionOverHTTP2 extends HttpConnection implements Sweeper.S
MetaData.Request metaData = new MetaData.Request(request.getMethod(), new HttpURI(request.getURI()), HttpVersion.HTTP_2, request.getHeaders());
// We do not support upgrade requests with content, so endStream=true.
HeadersFrame frame = new HeadersFrame(metaData, null, true);
- IStream stream = ((HTTP2Session)session).newStream(frame, null);
+ IStream stream = ((HTTP2Session)session).newLocalStream(frame, null);
stream.updateClose(frame.isEndStream(), CloseState.Event.AFTER_SEND);
HttpExchange exchange = request.getConversation().getExchanges().peekLast();
From ba2fe581112f683bc7d897a137c2fbb34d5a2cae Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Mon, 6 Apr 2020 13:31:34 +0200
Subject: [PATCH 048/101] Improvements to the Jetty client documentation, fixed
section ids and xrefs.
Signed-off-by: Simone Bordet
---
.../embedded-guide/client/client-io-arch.adoc | 10 +++----
.../embedded-guide/client/client.adoc | 10 +++----
.../client/http/client-http-api.adoc | 12 ++++----
.../http/client-http-authentication.adoc | 4 +--
.../http/client-http-configuration.adoc | 10 +++----
.../client/http/client-http-cookie.adoc | 2 +-
.../client/http/client-http-intro.adoc | 30 +++++++++----------
.../client/http/client-http-proxy.adoc | 8 ++---
.../client/http/client-http-transport.adoc | 20 ++++++-------
.../client/http/client-http.adoc | 2 +-
.../client/http2/client-http2.adoc | 30 +++++++++----------
11 files changed, 69 insertions(+), 69 deletions(-)
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/client-io-arch.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/client-io-arch.adoc
index f2afffd946e..83e59d0ed7a 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/client-io-arch.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/client-io-arch.adoc
@@ -16,7 +16,7 @@
// ========================================================================
//
-[[client-io-arch]]
+[[eg-client-io-arch]]
=== Client Libraries Architecture
The Jetty client libraries provide the basic components and APIs to implement
@@ -27,13 +27,13 @@ specific concepts (such as establishing a connection to a server).
There are conceptually two layers that compose the Jetty client libraries:
-. link:#client-io-arch-network[The network layer], that handles the low level
+. xref:eg-client-io-arch-network[The network layer], that handles the low level
I/O and deals with buffers, threads, etc.
-. link:#client-io-arch-protocol[The protocol layer], that handles the parsing
+. xref:eg-client-io-arch-protocol[The protocol layer], that handles the parsing
of bytes read from the network and the generation of bytes to write to the
network.
-[[client-io-arch-network]]
+[[eg-client-io-arch-network]]
==== Client Libraries Network Layer
The Jetty client libraries use the common I/O design described in
@@ -115,7 +115,7 @@ Please refer to the `ClientConnector`
link:{JDURL}/org/eclipse/jetty/io/ClientConnector.html[javadocs]
for the complete list of configurable parameters.
-[[client-io-arch-protocol]]
+[[eg-client-io-arch-protocol]]
==== Client Libraries Protocol Layer
The protocol layer builds on top of the network layer to generate the
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/client.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/client.adoc
index 013edfc2d8f..28a17421373 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/client.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/client.adoc
@@ -16,7 +16,7 @@
// ========================================================================
//
-[[client]]
+[[eg-client]]
== Client Libraries
The Eclipse Jetty Project provides also provides client-side libraries
@@ -34,13 +34,13 @@ and asynchronous APIs and come with a large number of configuration options.
These are the available client libraries:
-* link:#client-http[The HTTP Client Library]
-* link:#client-http2[The HTTP/2 Client Library]
-* link:#client-websocket[The WebSocket client library]
+* xref:eg-client-http[The HTTP Client Library]
+* xref:eg-client-http2[The HTTP/2 Client Library]
+* xref:eg-client-websocket[The WebSocket client library]
If you are interested in the low-level details of how the Eclipse Jetty
client libraries work, or are interested in writing a custom protocol,
-look at the link:#client-io-arch[Client I/O Architecture].
+look at the xref:eg-client-io-arch[Client I/O Architecture].
include::http/client-http.adoc[]
include::http2/client-http2.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-api.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-api.adoc
index b369bb33e91..e2aa147bb42 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-api.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-api.adoc
@@ -16,12 +16,12 @@
// ========================================================================
//
-[[client-http-api]]
+[[eg-client-http-api]]
=== HttpClient API Usage
`HttpClient` provides two types of APIs: a blocking API and a non-blocking API.
-[[client-http-blocking]]
+[[eg-client-http-blocking]]
==== HttpClient Blocking APIs
The simpler way to perform a HTTP request is the following:
@@ -81,7 +81,7 @@ include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=totalTim
In the example above, when the 5 seconds expire, the request is aborted and a `java.util.concurrent.TimeoutException` is thrown.
-[[client-http-non-blocking]]
+[[eg-client-http-non-blocking]]
==== HttpClient Non-Blocking APIs
So far we have shown how to use Jetty HTTP client in a blocking style - that is, the thread that issues the request blocks until the request/response conversation is complete.
@@ -139,10 +139,10 @@ This makes Jetty HTTP client suitable for HTTP load testing because, for example
Have a look at the link:{JDURL}/org/eclipse/jetty/client/api/Request.Listener.html[`Request.Listener`] class to know about request events, and to the link:{JDURL}/org/eclipse/jetty/client/api/Response.Listener.html[`Response.Listener`] class to know about response events.
-[[client-http-content]]
+[[eg-client-http-content]]
==== HttpClient Content Handling
-[[client-http-content-request]]
+[[eg-client-http-content-request]]
===== Request Content Handling
Jetty's `HttpClient` provides a number of utility classes off the shelf to handle request content.
@@ -190,7 +190,7 @@ which allows applications to write request content when it is available to the `
include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=outputStreamRequestContent]
----
-[[client-http-content-response]]
+[[eg-client-http-content-response]]
===== Response Content Handling
Jetty's `HttpClient` allows applications to handle response content in different ways.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-authentication.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-authentication.adoc
index 40a8addb58d..0f9a0134a52 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-authentication.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-authentication.adoc
@@ -16,7 +16,7 @@
// ========================================================================
//
-[[client-http-authentication]]
+[[eg-client-http-authentication]]
=== HttpClient Authentication Support
Jetty's `HttpClient` supports the `BASIC` and `DIGEST` authentication
@@ -97,5 +97,5 @@ in this way:
include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=requestPreemptedResult]
----
-See also the link:#client-http-proxy-authentication[proxy authentication section]
+See also the xref:eg-client-http-proxy-authentication[proxy authentication section]
for further information about how authentication works with HTTP proxies.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-configuration.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-configuration.adoc
index e9a6f0ab7b1..800d2c24adb 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-configuration.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-configuration.adoc
@@ -16,7 +16,7 @@
// ========================================================================
//
-[[client-http-configuration]]
+[[eg-client-http-configuration]]
=== HttpClient Configuration
`HttpClient` has a quite large number of configuration parameters.
@@ -26,17 +26,17 @@ for the complete list of configurable parameters.
The most common parameters are:
* `HttpClient.idleTimeout`: same as `ClientConnector.idleTimeout`
-described in link:#client-io-arch-network[this section].
+described in xref:eg-client-io-arch-network[this section].
* `HttpClient.connectBlocking`: same as `ClientConnector.connectBlocking`
-described in link:#client-io-arch-network[this section].
+described in xref:eg-client-io-arch-network[this section].
* `HttpClient.connectTimeout`: same as `ClientConnector.connectTimeout`
-described in link:#client-io-arch-network[this section].
+described in xref:eg-client-io-arch-network[this section].
* `HttpClient.maxConnectionsPerDestination`: the max number of TCP
connections that are opened for a particular destination (defaults to 64).
* `HttpClient.maxRequestsQueuedPerDestination`: the max number of requests
queued (defaults to 1024).
-[[client-http-configuration-tls]]
+[[eg-client-http-configuration-tls]]
==== HttpClient TLS Configuration
`HttpClient` supports HTTPS requests out-of-the-box like a browser does.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-cookie.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-cookie.adoc
index dc060ebf5cf..8c414de749f 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-cookie.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-cookie.adoc
@@ -16,7 +16,7 @@
// ========================================================================
//
-[[client-http-cookie]]
+[[eg-client-http-cookie]]
=== HttpClient Cookie Support
Jetty's `HttpClient` supports cookies out of the box.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc
index acd412130c8..ae1c4a364a3 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc
@@ -16,7 +16,7 @@
// ========================================================================
//
-[[client-http-intro]]
+[[eg-client-http-intro]]
=== HttpClient Introduction
The Jetty HTTP client module provides easy-to-use APIs and utility classes to perform HTTP (or HTTPS) requests.
@@ -27,7 +27,7 @@ It offers an asynchronous API that never blocks for I/O, making it very efficien
However, when all you need to do is to perform a `GET` request to a resource, Jetty's HTTP client offers also a synchronous API; a programming interface
where the thread that issued the request blocks until the request/response conversation is complete.
-Jetty's HTTP client supports link:#http-client-transport[different transports]: HTTP/1.1, FastCGI and HTTP/2.
+Jetty's HTTP client supports xref:#eg-client-http-transport[different transports]: HTTP/1.1, FastCGI and HTTP/2.
This means that the semantic of a HTTP request (that is, " `GET` me the resource `/index.html` ") can be carried over the network in different formats.
The most common and default format is HTTP/1.1.
That said, Jetty's HTTP client can carry the same request using the FastCGI format or the HTTP/2 format.
@@ -43,7 +43,7 @@ Out of the box features that you get with the Jetty HTTP client include:
* Authentication support - HTTP "Basic" and "Digest" authentications are supported, others are pluggable.
* Forward proxy support - HTTP proxying and SOCKS4 proxying.
-[[client-http-start]]
+[[eg-client-http-start]]
==== Starting HttpClient
The Jetty artifact that provides the main HTTP client implementation is `jetty-client`.
@@ -76,15 +76,15 @@ There are several reasons for having multiple `HttpClient` instances including,
* You want to specify different configuration parameters (for example, one instance is configured with a forward proxy while another is not).
* You want the two instances to behave like two different browsers and hence have different cookies, different authentication credentials, etc.
-* You want to use link:#http-client-transport[different transports].
+* You want to use link:#eg-client-http-transport[different transports].
Like browsers, HTTPS requests are supported out-of-the-box, as long as the server
provides a valid certificate.
In case the server does not provide a valid certificate (or in case it is self-signed)
you want to customize ``HttpClient``'s TLS configuration as described in
-link:#client-http-configuration-tls[this section].
+xref:eg-client-http-configuration-tls[this section].
-[[client-http-stop]]
+[[eg-client-http-stop]]
==== Stopping HttpClient
It is recommended that when your application stops, you also stop the `HttpClient` instance (or instances) that you are using.
@@ -96,20 +96,20 @@ include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=stop]
Stopping `HttpClient` makes sure that the memory it holds (for example, authentication credentials, cookies, etc.) is released, and that the thread pool and scheduler are properly stopped allowing all threads used by `HttpClient` to exit.
-[[client-http-arch]]
+[[eg-client-http-arch]]
==== HttpClient Architecture
A `HttpClient` instance can be thought as a browser instance, and it manages the
following components:
-* a `CookieStore` (see link:#client-http-cookie[this section]).
-* a `AuthenticationStore` (see link:#client-http-authentication[this section]).
-* a `ProxyConfiguration` (see link:#client-http-proxy[this section]).
+* a `CookieStore` (see xref:eg-client-http-cookie[this section]).
+* a `AuthenticationStore` (see xref:eg-client-http-authentication[this section]).
+* a `ProxyConfiguration` (see xref:eg-client-http-proxy[this section]).
* a set of _destinations_.
A _destination_ is the client-side component that represent an _origin_ on
a server, and manages a queue of requests for that origin, and a
-link:#client-http-connection-pool[pool of connections] to that origin.
+xref:eg-client-http-connection-pool[pool of connections] to that origin.
An _origin_ may be simply thought as the tuple `(scheme, host, port)` and it
is where the client connects to in order to communicate with the server.
@@ -145,7 +145,7 @@ connection pools.
Therefore an origin is identified by the tuple
`(scheme, host, port, tag, protocol)`.
-[[client-http-connection-pool]]
+[[eg-client-http-connection-pool]]
==== HttpClient Connection Pooling
A destination manages a `org.eclipse.jetty.client.ConnectionPool`, where
@@ -184,7 +184,7 @@ by setting a `ConnectionPool.Factory` on the `HttpClientTransport`:
include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=setConnectionPool]
----
-[[client-http-request-processing]]
+[[eg-client-http-request-processing]]
==== HttpClient Request Processing
[plantuml]
@@ -231,7 +231,7 @@ cycle is completed will reuse the same connection.
A second request with the same origin sent _concurrently_ with the first
request will cause the opening of a second connection.
The configuration parameter `HttpClient.maxConnectionsPerDestination`
-(see also the link:#client-http-configuration[configuration section]) controls
+(see also the xref:eg-client-http-configuration[configuration section]) controls
the max number of connections that can be opened for a destination.
NOTE: If opening connections to a given origin takes a long time, then
@@ -249,5 +249,5 @@ connections have maxed out their number of outstanding requests, more requests
sent to that destination will be queued.
When the request queue is full, the request will be failed.
The configuration parameter `HttpClient.maxRequestsQueuedPerDestination`
-(see also the link:#client-http-configuration[configuration section]) controls
+(see also the xref:eg-client-http-configuration[configuration section]) controls
the max number of requests that can be queued for a destination.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-proxy.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-proxy.adoc
index cea87ff73a1..a4b8fa9cc26 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-proxy.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-proxy.adoc
@@ -16,7 +16,7 @@
// ========================================================================
//
-[[client-http-proxy]]
+[[eg-client-http-proxy]]
=== HttpClient Proxy Support
Jetty's `HttpClient` can be configured to use proxies to connect to destinations.
@@ -43,11 +43,11 @@ encrypted HTTPS requests).
Proxying is supported for both HTTP/1.1 and HTTP/2.
-[[client-http-proxy-authentication]]
+[[eg-client-http-proxy-authentication]]
==== Proxy Authentication Support
Jetty's `HttpClient` supports proxy authentication in the same way it supports
-link:#client-http-authentication[server authentication].
+xref:eg-client-http-authentication[server authentication].
In the example below, the proxy requires `BASIC` authentication, but the server
requires `DIGEST` authentication, and therefore:
@@ -87,6 +87,6 @@ HttpClient -> Application : 200 OK
The application does not receive events related to the responses with code 407
and 401 since they are handled internally by `HttpClient`.
-Similarly to the link:#client-http-authentication[authentication section], the
+Similarly to the xref:eg-client-http-authentication[authentication section], the
proxy authentication result and the server authentication result can be
preempted to avoid, respectively, the 407 and 401 roundtrips.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-transport.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-transport.adoc
index 518483296b4..e5840c61390 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-transport.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-transport.adoc
@@ -16,7 +16,7 @@
// ========================================================================
//
-[[client-http-transport]]
+[[eg-client-http-transport]]
=== HttpClient Pluggable Transports
Jetty's `HttpClient` can be configured to use different transports to carry the
@@ -64,13 +64,13 @@ resource may be sent using one protocol (for example, HTTP/1.1), but the
response may arrive in a different protocol (for example, HTTP/2).
`HttpClient` supports 3 static transports, each speaking only one protocol:
-link:#client-http-transport-http11[HTTP/1.1],
-link:#client-http-transport-http2[HTTP/2] and
-link:#client-http-transport-fcgi[FastCGI],
+xref:eg-client-http-transport-http11[HTTP/1.1],
+xref:eg-client-http-transport-http2[HTTP/2] and
+xref:eg-client-http-transport-fcgi[FastCGI],
all of them with 2 variants: clear-text and TLS encrypted.
`HttpClient` also supports one
-link:#client-http-transport-dynamic[dynamic transport],
+xref:eg-client-http-transport-dynamic[dynamic transport],
that can speak different protocols and can select the right protocol by
negotiating it with the server or by explicit indication from applications.
@@ -78,7 +78,7 @@ Applications are typically not aware of the actual protocol being used.
This allows them to write their logic against a high-level API that hides the
details of the specific protocol being used over the network.
-[[client-http-transport-http11]]
+[[eg-client-http-transport-http11]]
==== HTTP/1.1 Transport
HTTP/1.1 is the default transport.
@@ -96,7 +96,7 @@ it in this way:
include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=http11Transport]
----
-[[client-http-transport-http2]]
+[[eg-client-http-transport-http2]]
==== HTTP/2 Transport
The HTTP/2 transport can be configured in this way:
@@ -108,12 +108,12 @@ include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=http2Tran
`HTTP2Client` is the lower-level client that provides an API based on HTTP/2
concepts such as _sessions_, _streams_ and _frames_ that are specific to HTTP/2.
-See link:#client-http2[the HTTP/2 client section] for more information.
+See xref:eg-client-http2[the HTTP/2 client section] for more information.
`HttpClientTransportOverHTTP2` uses `HTTP2Client` to format high-level semantic
HTTP requests (like "GET resource /index.html") into the HTTP/2 specific format.
-[[client-http-transport-fcgi]]
+[[eg-client-http-transport-fcgi]]
==== FastCGI Transport
The FastCGI transport can be configured in this way:
@@ -130,7 +130,7 @@ FastCGI server such as https://en.wikipedia.org/wiki/PHP#PHPFPM[PHP-FPM]
The FastCGI transport is primarily used by Jetty's link:#fastcgi[FastCGI support]
to serve PHP pages (WordPress for example).
-[[client-http-transport-dynamic]]
+[[eg-client-http-transport-dynamic]]
==== Dynamic Transport
The static transports work well if you know in advance the protocol you want
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http.adoc
index 53b5833b1cf..25821e403c1 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http.adoc
@@ -16,7 +16,7 @@
// ========================================================================
//
-[[client-http]]
+[[eg-client-http]]
=== HTTP Client
include::client-http-intro.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http2/client-http2.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http2/client-http2.adoc
index 4333955859e..546b61fa9fc 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http2/client-http2.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http2/client-http2.adoc
@@ -16,14 +16,14 @@
// ========================================================================
//
-[[client-http2]]
+[[eg-client-http2]]
=== HTTP/2 Client Library
In the vast majority of cases, client applications should use the generic,
-high-level, link:#client-http[HTTP client library] that also provides
+high-level, xref:eg-client-http[HTTP client library] that also provides
HTTP/2 support via the pluggable
-link:#client-http-transport-http2[HTTP/2 transport] or the
-link:#client-http-transport-dynamic[dynamic transport].
+xref:eg-client-http-transport-http2[HTTP/2 transport] or the
+xref:eg-client-http-transport-dynamic[dynamic transport].
The high-level HTTP library supports cookies, authentication, redirection,
connection pooling and a number of other features that are absent in the
@@ -33,7 +33,7 @@ The HTTP/2 client library has been designed for those applications that need
low-level access to HTTP/2 features such as _sessions_, _streams_ and
_frames_, and this is quite a rare use case.
-[[client-http2-intro]]
+[[eg-client-http2-intro]]
==== Introducing HTTP2Client
The Maven artifact coordinates for the HTTP/2 client library are the following:
@@ -80,7 +80,7 @@ A _stream_ has typically a very short life compared to the _session_: a
_stream_ only exists for the duration of the request/response cycle and then
disappears.
-[[client-http2-flow-control]]
+[[eg-client-http2-flow-control]]
===== HTTP/2 Flow Control
The HTTP/2 protocol is _flow controlled_ (see
@@ -132,12 +132,12 @@ have consumed data bytes as soon as possible, so that the implementation
`WINDOW_UPDATE` frame) with the information to enlarge the flow control
window, therefore reducing the possibility that sender stalls due to the flow
control windows being reduced to `0`.
-This is discussed in details in link:#client-http2-response[this section].
+This is discussed in details in xref:eg-client-http2-response[this section].
-[[client-http2-connect]]
+[[eg-client-http2-connect]]
==== Connecting to the Server
The first thing an application should do is to connect to the server and
@@ -160,7 +160,7 @@ IMPORTANT: Applications must know in advance whether they want to connect to a
clear-text or encrypted port, and pass the `SslContextFactory` parameter
accordingly to the `connect(...)` method.
-[[client-http2-configure]]
+[[eg-client-http2-configure]]
===== Configuring the Session
The `connect(...)` method takes a `Session.Listener` parameter.
@@ -185,7 +185,7 @@ Once a `Session` has been established, the communication with the server happens
by exchanging _frames_, as specified in the
link:https://tools.ietf.org/html/rfc7540#section-4[HTTP/2 specification].
-[[client-http2-request]]
+[[eg-client-http2-request]]
==== Sending a Request
Sending an HTTP request to the server, and receiving a response, creates a
@@ -206,7 +206,7 @@ include::../../{doc_code}/embedded/client/http2/HTTP2ClientDocs.java[tags=newStr
Note how `Session.newStream(...)` takes a `Stream.Listener` parameter.
This listener is notified of stream events originated by the server such as
receiving `HEADERS` or `DATA` frames that are part of the response, discussed
-in more details in the link:#client-http2-response[section below].
+in more details in the xref:eg-client-http2-response[section below].
Please refer to the `Stream.Listener`
link:{JDURL}/org/eclipse/jetty/http2/api/Stream.Listener.html[javadocs] for
the complete list of events.
@@ -224,7 +224,7 @@ IMPORTANT: When sending two `DATA` frames consecutively, the second call to
Use the `Callback` APIs or `CompletableFuture` APIs to ensure that the second
`Stream.data(...)` call is performed when the first completed successfully.
-[[client-http2-response]]
+[[eg-client-http2-response]]
==== Receiving a Response
Response events are delivered to the `Stream.Listener` passed to
@@ -278,7 +278,7 @@ to call `Stream.demand(...)`. If they don't, the implementation will not
deliver `DATA` frames and the application will stall threadlessly until an
idle timeout fires to close the stream or the session.
-[[client-http2-reset]]
+[[eg-client-http2-reset]]
==== Resetting a Request or Response
In HTTP/2, clients and servers have the ability to tell to the other peer that
@@ -293,7 +293,7 @@ The `HTTP2Client` APIs allow client applications to send and receive this
include::../../{doc_code}/embedded/client/http2/HTTP2ClientDocs.java[tags=reset]
----
-[[client-http2-push]]
+[[eg-client-http2-push]]
==== Receiving HTTP/2 Pushes
HTTP/2 servers have the ability to push resources related to a primary
@@ -303,7 +303,7 @@ frame that contains the request URI and headers that a client would use to
request explicitly that resource.
Client applications can be configured to tell the server to never push
-resources, see link:#client-http2-configure[this section].
+resources, see xref:eg-client-http2-configure[this section].
Client applications can listen to the push events, and act accordingly:
From f57a170e999e48f2b964a5e62cde703435e2a5a4 Mon Sep 17 00:00:00 2001
From: Jan Bartel
Date: Mon, 6 Apr 2020 14:30:38 +0200
Subject: [PATCH 049/101] Fix merge from 9.4 for #4737
---
.../jetty/plus/annotation/LifeCycleCallbackCollectionTest.java | 3 ---
1 file changed, 3 deletions(-)
diff --git a/jetty-plus/src/test/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollectionTest.java b/jetty-plus/src/test/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollectionTest.java
index b6bf8e47275..c93c8ffd920 100644
--- a/jetty-plus/src/test/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollectionTest.java
+++ b/jetty-plus/src/test/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollectionTest.java
@@ -243,9 +243,6 @@ public class LifeCycleCallbackCollectionTest
PreDestroyCallback pdBoth = new PreDestroyCallback(TestServlet.class, "predestroy");
collection.add(pdBoth);
- //ensure we invoke the lifecyclecallbacks
- context.getObjectFactory().addDecorator(new PlusDecorator(context));
-
server.start();
assertEquals(4, TestServlet.postConstructCount);
From be50caf5917e730ff48dfa439fc0203bef521feb Mon Sep 17 00:00:00 2001
From: olivier lamy
Date: Tue, 24 Mar 2020 17:41:54 +1000
Subject: [PATCH 050/101] Issue #4690 Allow DistributionTester to work with log
files
Signed-off-by: olivier lamy
---
.../distribution/DistributionTester.java | 101 ++++++++++++++++--
.../tests/distribution/DistributionTests.java | 47 ++++++++
.../src/test/resources/log4j2.xml | 39 +++++++
3 files changed, 181 insertions(+), 6 deletions(-)
create mode 100644 tests/test-distribution/src/test/resources/log4j2.xml
diff --git a/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/DistributionTester.java b/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/DistributionTester.java
index aae25684091..646be66fc7a 100644
--- a/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/DistributionTester.java
+++ b/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/DistributionTester.java
@@ -21,10 +21,13 @@ package org.eclipse.jetty.tests.distribution;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.io.RandomAccessFile;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.file.Files;
@@ -164,7 +167,7 @@ public class DistributionTester
pbCmd.directory(jettyBaseDir);
Process process = pbCmd.start();
- return new Run(process);
+ return new Run(process, config);
}
/**
@@ -370,6 +373,7 @@ public class DistributionTester
private String jettyVersion;
private String mavenLocalRepository = System.getProperty("user.home") + "/.m2/repository";
private Map mavenRemoteRepositories = new HashMap<>();
+ private Path logFile;
@Override
public String toString()
@@ -413,12 +417,17 @@ public class DistributionTester
private final Process process;
private final List consoleStreamers = new ArrayList<>();
private final Queue logs = new ConcurrentLinkedQueue<>();
+ private LogFileStreamer logFileStreamer;
- private Run(Process process)
+ private Run(Process process, DistributionTester.Config config)
{
this.process = process;
consoleStreamers.add(startPump("STDOUT", process.getInputStream()));
consoleStreamers.add(startPump("STDERR", process.getErrorStream()));
+ if (config.logFile != null)
+ {
+ logFileStreamer = new LogFileStreamer(config.logFile);
+ }
}
private ConsoleStreamer startPump(String mode, InputStream stream)
@@ -441,7 +450,7 @@ public class DistributionTester
{
boolean result = process.waitFor(time, unit);
if (result)
- stopConsoleStreamers();
+ stopStreamers();
return result;
}
@@ -462,7 +471,7 @@ public class DistributionTester
public void stop()
{
process.destroy();
- stopConsoleStreamers();
+ stopStreamers();
}
/**
@@ -471,12 +480,14 @@ public class DistributionTester
public void destroy()
{
process.destroyForcibly();
- stopConsoleStreamers();
+ stopStreamers();
}
- private void stopConsoleStreamers()
+ private void stopStreamers()
{
consoleStreamers.forEach(ConsoleStreamer::stop);
+ if (logFileStreamer != null)
+ logFileStreamer.stop();
}
@Override
@@ -507,6 +518,38 @@ public class DistributionTester
return false;
}
+ /**
+ * Awaits the logs file to contain the given text, for the given amount of time.
+ *
+ * @param logFile the log file to test
+ * @param txt the text that must be present in the console logs
+ * @param time the time to wait
+ * @param unit the unit of time
+ * @return true if the text was found, false if the timeout elapsed
+ * @throws InterruptedException if the wait is interrupted
+ */
+ public boolean awaitLogsFileFor(Path logFile, String txt, long time, TimeUnit unit) throws InterruptedException
+ {
+ LogFileStreamer logFileStreamer = new LogFileStreamer(logFile);
+ Thread thread = new Thread(logFileStreamer, "LogFileStreamer/" + logFile);
+ thread.start();
+ try
+ {
+ long end = System.nanoTime() + unit.toNanos(time);
+ while (System.nanoTime() < end)
+ {
+ boolean result = logs.stream().anyMatch(s -> s.contains(txt));
+ if (result) return true;
+ Thread.sleep(250);
+ }
+ return false;
+ }
+ finally
+ {
+ logFileStreamer.stop();
+ }
+ }
+
/**
* Simple streamer for the console output from a Process
*/
@@ -549,6 +592,52 @@ public class DistributionTester
}
}
+ private class LogFileStreamer implements Runnable
+ {
+ private RandomAccessFile inputFile;
+ private volatile boolean stop;
+ private final Path logFile;
+
+ public LogFileStreamer(Path logFile)
+ {
+ this.logFile = logFile;
+ }
+
+ @Override
+ public void run()
+ {
+ String currentLine;
+ long pos = 0;
+ while (!stop)
+ {
+ try
+ {
+ inputFile = new RandomAccessFile(logFile.toFile(), "r");
+ inputFile.seek(pos);
+ if ((currentLine = inputFile.readLine()) != null)
+ {
+ logs.add(currentLine);
+ }
+ pos = inputFile.getFilePointer();
+ }
+ catch (IOException e)
+ {
+ //ignore
+ }
+ finally
+ {
+ IO.close(inputFile);
+ }
+ }
+ }
+
+ public void stop()
+ {
+ stop = true;
+ IO.close(inputFile);
+ }
+ }
+
public Queue getLogs()
{
return logs;
diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java
index b1225195332..ee8911be9d0 100644
--- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java
+++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java
@@ -21,9 +21,11 @@ package org.eclipse.jetty.tests.distribution;
import java.io.BufferedWriter;
import java.io.File;
import java.nio.charset.StandardCharsets;
+import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.TimeUnit;
@@ -453,4 +455,49 @@ public class DistributionTests extends AbstractDistributionTest
}
}
}
+
+ @Test
+ public void testStartStopLog4j2Modules() throws Exception
+ {
+ Path jettyBase = Files.createTempDirectory("jetty_base");
+
+ String jettyVersion = System.getProperty("jettyVersion");
+ DistributionTester distribution = DistributionTester.Builder.newInstance() //
+ .jettyVersion(jettyVersion) //
+ .jettyBase(jettyBase) //
+ .mavenLocalRepository(System.getProperty("mavenRepoPath")) //
+ .build();
+
+ String[] args = {
+ "--create-startd",
+ "--approve-all-licenses",
+ "--add-to-start=http,logging-log4j2"
+ };
+
+ try (DistributionTester.Run run1 = distribution.start(args))
+ {
+ assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
+ assertEquals(0, run1.getExitValue());
+
+ Files.copy(Paths.get("src/test/resources/log4j2.xml"), //
+ Paths.get(jettyBase.toString(),"resources").resolve("log4j2.xml"), //
+ StandardCopyOption.REPLACE_EXISTING);
+
+ int port = distribution.freePort();
+ try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
+ {
+ assertTrue(run2.awaitLogsFileFor(
+ jettyBase.resolve("logs").resolve("jetty.log"), //
+ "Started Server@", 10, TimeUnit.SECONDS));
+
+ startHttpClient();
+ ContentResponse response = client.GET("http://localhost:" + port);
+ assertEquals(HttpStatus.NOT_FOUND_404, response.getStatus());
+
+ run2.stop();
+ assertTrue(run2.awaitFor(5, TimeUnit.SECONDS));
+ }
+ }
+ }
+
}
diff --git a/tests/test-distribution/src/test/resources/log4j2.xml b/tests/test-distribution/src/test/resources/log4j2.xml
new file mode 100644
index 00000000000..468c1d25f06
--- /dev/null
+++ b/tests/test-distribution/src/test/resources/log4j2.xml
@@ -0,0 +1,39 @@
+
+
+
+
+ ${sys:jetty.logging.dir:-logs}
+
+
+
+
+
+ %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+
+
+
+
+
+
+ %d [%t] %-5p %c %x - %m%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From a189d13fd548b2acb626c501148d5364a2a085a7 Mon Sep 17 00:00:00 2001
From: olivier lamy
Date: Tue, 24 Mar 2020 19:17:14 +1000
Subject: [PATCH 051/101] cleanup
Signed-off-by: olivier lamy
---
.../distribution/DistributionTester.java | 21 ++++++-------------
1 file changed, 6 insertions(+), 15 deletions(-)
diff --git a/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/DistributionTester.java b/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/DistributionTester.java
index 646be66fc7a..eb9a14df5d0 100644
--- a/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/DistributionTester.java
+++ b/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/DistributionTester.java
@@ -21,8 +21,6 @@ package org.eclipse.jetty.tests.distribution;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -167,7 +165,7 @@ public class DistributionTester
pbCmd.directory(jettyBaseDir);
Process process = pbCmd.start();
- return new Run(process, config);
+ return new Run(process);
}
/**
@@ -417,17 +415,12 @@ public class DistributionTester
private final Process process;
private final List consoleStreamers = new ArrayList<>();
private final Queue logs = new ConcurrentLinkedQueue<>();
- private LogFileStreamer logFileStreamer;
- private Run(Process process, DistributionTester.Config config)
+ private Run(Process process)
{
this.process = process;
consoleStreamers.add(startPump("STDOUT", process.getInputStream()));
consoleStreamers.add(startPump("STDERR", process.getErrorStream()));
- if (config.logFile != null)
- {
- logFileStreamer = new LogFileStreamer(config.logFile);
- }
}
private ConsoleStreamer startPump(String mode, InputStream stream)
@@ -450,7 +443,7 @@ public class DistributionTester
{
boolean result = process.waitFor(time, unit);
if (result)
- stopStreamers();
+ stopConsoleStreamers();
return result;
}
@@ -471,7 +464,7 @@ public class DistributionTester
public void stop()
{
process.destroy();
- stopStreamers();
+ stopConsoleStreamers();
}
/**
@@ -480,14 +473,12 @@ public class DistributionTester
public void destroy()
{
process.destroyForcibly();
- stopStreamers();
+ stopConsoleStreamers();
}
- private void stopStreamers()
+ private void stopConsoleStreamers()
{
consoleStreamers.forEach(ConsoleStreamer::stop);
- if (logFileStreamer != null)
- logFileStreamer.stop();
}
@Override
From 625dfd1a4d0b82b6174452b3c669e723388eebe5 Mon Sep 17 00:00:00 2001
From: Lachlan Roberts
Date: Tue, 7 Apr 2020 12:18:29 +1000
Subject: [PATCH 052/101] Issue #4747 - Fix WS path params to work within a
context path
Signed-off-by: Lachlan Roberts
---
.../client/internal/JavaxClientUpgradeRequest.java | 6 ++++++
.../JavaxWebSocketClientFrameHandlerFactory.java | 2 +-
.../common/JavaxWebSocketFrameHandlerFactory.java | 7 +++----
.../jetty/websocket/javax/common/UpgradeRequest.java | 11 ++++++++---
.../javax/common/UpgradeRequestAdapter.java | 12 ++++++++++--
.../websocket/javax/common/AbstractSessionTest.java | 2 +-
.../javax/common/DummyFrameHandlerFactory.java | 2 +-
.../server/internal/JavaxServerUpgradeRequest.java | 8 +++++++-
.../jetty/websocket/javax/tests/PathParamTest.java | 6 +++---
...WebSocketFrameHandlerOnMessageTextStreamTest.java | 3 ++-
.../websocket/servlet/ServletUpgradeRequest.java | 9 +++++++++
11 files changed, 51 insertions(+), 17 deletions(-)
diff --git a/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JavaxClientUpgradeRequest.java b/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JavaxClientUpgradeRequest.java
index b2fd4d5ae52..d2c8856db66 100644
--- a/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JavaxClientUpgradeRequest.java
+++ b/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JavaxClientUpgradeRequest.java
@@ -64,4 +64,10 @@ public class JavaxClientUpgradeRequest extends ClientUpgradeRequest implements U
{
return getURI();
}
+
+ @Override
+ public String getPathInContext()
+ {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JavaxWebSocketClientFrameHandlerFactory.java b/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JavaxWebSocketClientFrameHandlerFactory.java
index 39f3221b877..5a1b5252e0e 100644
--- a/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JavaxWebSocketClientFrameHandlerFactory.java
+++ b/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JavaxWebSocketClientFrameHandlerFactory.java
@@ -40,7 +40,7 @@ public class JavaxWebSocketClientFrameHandlerFactory extends JavaxWebSocketFrame
}
@Override
- public EndpointConfig newDefaultEndpointConfig(Class> endpointClass, String path)
+ public EndpointConfig newDefaultEndpointConfig(Class> endpointClass)
{
return new BasicClientEndpointConfig();
}
diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerFactory.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerFactory.java
index 5eeea251808..eb51e98d4fa 100644
--- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerFactory.java
+++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketFrameHandlerFactory.java
@@ -144,7 +144,7 @@ public abstract class JavaxWebSocketFrameHandlerFactory
public abstract JavaxWebSocketFrameHandlerMetadata getMetadata(Class> endpointClass, EndpointConfig endpointConfig);
- public abstract EndpointConfig newDefaultEndpointConfig(Class> endpointClass, String path);
+ public abstract EndpointConfig newDefaultEndpointConfig(Class> endpointClass);
public JavaxWebSocketFrameHandler newJavaxWebSocketFrameHandler(Object endpointInstance, UpgradeRequest upgradeRequest)
{
@@ -160,8 +160,7 @@ public abstract class JavaxWebSocketFrameHandlerFactory
else
{
endpoint = endpointInstance;
- String path = (upgradeRequest.getRequestURI() == null) ? null : upgradeRequest.getRequestURI().getPath();
- config = newDefaultEndpointConfig(endpoint.getClass(), path);
+ config = newDefaultEndpointConfig(endpoint.getClass());
}
JavaxWebSocketFrameHandlerMetadata metadata = getMetadata(endpoint.getClass(), config);
@@ -180,7 +179,7 @@ public abstract class JavaxWebSocketFrameHandlerFactory
if (templatePathSpec != null)
{
String[] namedVariables = templatePathSpec.getVariables();
- Map pathParams = templatePathSpec.getPathParams(upgradeRequest.getRequestURI().getRawPath());
+ Map pathParams = templatePathSpec.getPathParams(upgradeRequest.getPathInContext());
// Handle parameterized @PathParam entries
openHandle = bindTemplateVariables(openHandle, namedVariables, pathParams);
diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/UpgradeRequest.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/UpgradeRequest.java
index 90de5c9c492..5764910685a 100644
--- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/UpgradeRequest.java
+++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/UpgradeRequest.java
@@ -31,9 +31,14 @@ public interface UpgradeRequest
Principal getUserPrincipal();
/**
- * For obtaining {@link javax.websocket.server.PathParam} values from Request URI path
- *
- * @return the request URI
+ * @return the full URI of this request.
*/
URI getRequestURI();
+
+ /**
+ * For obtaining {@link javax.websocket.server.PathParam} values from the Request context path.
+ *
+ * @return the path in Context.
+ */
+ String getPathInContext();
}
diff --git a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/UpgradeRequestAdapter.java b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/UpgradeRequestAdapter.java
index 4ab2345d669..42e2f94e7fa 100644
--- a/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/UpgradeRequestAdapter.java
+++ b/jetty-websocket/websocket-javax-common/src/main/java/org/eclipse/jetty/websocket/javax/common/UpgradeRequestAdapter.java
@@ -24,16 +24,18 @@ import java.security.Principal;
public class UpgradeRequestAdapter implements UpgradeRequest
{
private final URI requestURI;
+ private final String pathInContext;
public UpgradeRequestAdapter()
{
/* anonymous, no requestURI, upgrade request */
- this(null);
+ this(null, null);
}
- public UpgradeRequestAdapter(URI uri)
+ public UpgradeRequestAdapter(URI uri, String pathInContext)
{
this.requestURI = uri;
+ this.pathInContext = pathInContext;
}
@Override
@@ -47,4 +49,10 @@ public class UpgradeRequestAdapter implements UpgradeRequest
{
return requestURI;
}
+
+ @Override
+ public String getPathInContext()
+ {
+ return pathInContext;
+ }
}
diff --git a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/AbstractSessionTest.java b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/AbstractSessionTest.java
index e425420c99b..c96927c7dc6 100644
--- a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/AbstractSessionTest.java
+++ b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/AbstractSessionTest.java
@@ -41,7 +41,7 @@ public abstract class AbstractSessionTest
JavaxWebSocketFrameHandler frameHandler = container.newFrameHandler(websocketPojo, upgradeRequest);
CoreSession coreSession = new CoreSession.Empty();
session = new JavaxWebSocketSession(container, coreSession, frameHandler, container.getFrameHandlerFactory()
- .newDefaultEndpointConfig(websocketPojo.getClass(), null));
+ .newDefaultEndpointConfig(websocketPojo.getClass()));
}
@AfterAll
diff --git a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/DummyFrameHandlerFactory.java b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/DummyFrameHandlerFactory.java
index d7468d6a2d0..12d00d0c67d 100644
--- a/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/DummyFrameHandlerFactory.java
+++ b/jetty-websocket/websocket-javax-common/src/test/java/org/eclipse/jetty/websocket/javax/common/DummyFrameHandlerFactory.java
@@ -33,7 +33,7 @@ public class DummyFrameHandlerFactory extends JavaxWebSocketFrameHandlerFactory
}
@Override
- public EndpointConfig newDefaultEndpointConfig(Class> endpointClass, String path)
+ public EndpointConfig newDefaultEndpointConfig(Class> endpointClass)
{
return ClientEndpointConfig.Builder.create().build();
}
diff --git a/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/JavaxServerUpgradeRequest.java b/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/JavaxServerUpgradeRequest.java
index 9b4d4d55939..69624723ec8 100644
--- a/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/JavaxServerUpgradeRequest.java
+++ b/jetty-websocket/websocket-javax-server/src/main/java/org/eclipse/jetty/websocket/javax/server/internal/JavaxServerUpgradeRequest.java
@@ -42,6 +42,12 @@ public class JavaxServerUpgradeRequest implements UpgradeRequest
@Override
public URI getRequestURI()
{
- return this.servletRequest.getRequestURI();
+ return servletRequest.getRequestURI();
+ }
+
+ @Override
+ public String getPathInContext()
+ {
+ return servletRequest.getPathInContext();
}
}
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/PathParamTest.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/PathParamTest.java
index 2b3e28eeff8..b67e44a462a 100644
--- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/PathParamTest.java
+++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/PathParamTest.java
@@ -53,7 +53,7 @@ public class PathParamTest
_server.addConnector(_connector);
_context = new ServletContextHandler(ServletContextHandler.SESSIONS);
- _context.setContextPath("/");
+ _context.setContextPath("/context");
_server.setHandler(_context);
JavaxWebSocketServletContainerInitializer.configure(_context, (context, container) ->
@@ -68,7 +68,7 @@ public class PathParamTest
_server.stop();
}
- @ServerEndpoint("/pathparam/echo/{name}")
+ @ServerEndpoint("/pathParam/echo/{name}")
public static class EchoParamSocket
{
private Session session;
@@ -92,7 +92,7 @@ public class PathParamTest
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
EventSocket clientEndpoint = new EventSocket();
- URI serverUri = URI.create("ws://localhost:" + _connector.getLocalPort() + "/pathparam/echo/myParam");
+ URI serverUri = URI.create("ws://localhost:" + _connector.getLocalPort() + "/context/pathParam/echo/myParam");
Session session = container.connectToServer(clientEndpoint, serverUri);
session.getBasicRemote().sendText("echo");
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/JavaxWebSocketFrameHandlerOnMessageTextStreamTest.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/JavaxWebSocketFrameHandlerOnMessageTextStreamTest.java
index 78117206672..3b1b6b5abe4 100644
--- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/JavaxWebSocketFrameHandlerOnMessageTextStreamTest.java
+++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/JavaxWebSocketFrameHandlerOnMessageTextStreamTest.java
@@ -46,7 +46,8 @@ public class JavaxWebSocketFrameHandlerOnMessageTextStreamTest extends AbstractJ
@SuppressWarnings("Duplicates")
private T performOnMessageInvocation(T socket, Consumer func) throws Exception
{
- UpgradeRequest request = new UpgradeRequestAdapter(URI.create("http://localhost:8080/msg/foo"));
+ URI uri = URI.create("http://localhost:8080/msg/foo");
+ UpgradeRequest request = new UpgradeRequestAdapter(uri, uri.getPath());
// Establish endpoint function
JavaxWebSocketFrameHandler frameHandler = container.newFrameHandler(socket, request);
diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeRequest.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeRequest.java
index ef81155a00b..f327a424a95 100644
--- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeRequest.java
+++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeRequest.java
@@ -38,6 +38,7 @@ import javax.servlet.http.HttpSession;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.websocket.core.ExtensionConfig;
import org.eclipse.jetty.websocket.core.WebSocketConstants;
import org.eclipse.jetty.websocket.core.server.Negotiation;
@@ -314,6 +315,14 @@ public class ServletUpgradeRequest
return requestURI;
}
+ /**
+ * @return the path within the context, combination of the ServletPath with the PathInfo.
+ */
+ public String getPathInContext()
+ {
+ return URIUtil.addPaths(request.getServletPath(), request.getPathInfo());
+ }
+
/**
* @param name Attribute name
* @return Attribute value or null
From 3d40e0e25d4710760fd071556e6e7912d5ad3ede Mon Sep 17 00:00:00 2001
From: Lachlan Roberts
Date: Tue, 7 Apr 2020 15:06:25 +1000
Subject: [PATCH 053/101] Issue #4747 - correctly copy headers in websocket
JsrUpgradeListener
Headers with the same name may not have been copied properly for
header values inspected and modified with a Configurator.
Signed-off-by: Lachlan Roberts
---
.../client/internal/JsrUpgradeListener.java | 26 +++++++------------
1 file changed, 10 insertions(+), 16 deletions(-)
diff --git a/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JsrUpgradeListener.java b/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JsrUpgradeListener.java
index 8ce0df47cdb..acc50088c95 100644
--- a/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JsrUpgradeListener.java
+++ b/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/internal/JsrUpgradeListener.java
@@ -19,10 +19,10 @@
package org.eclipse.jetty.websocket.javax.client.internal;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.stream.Stream;
import javax.websocket.ClientEndpointConfig.Configurator;
import javax.websocket.HandshakeResponse;
@@ -44,18 +44,15 @@ public class JsrUpgradeListener implements UpgradeListener
public void onHandshakeRequest(HttpRequest request)
{
if (configurator == null)
- {
return;
- }
HttpFields fields = request.getHeaders();
-
Map> originalHeaders = new HashMap<>();
- fields.forEach((field) ->
+ fields.forEach(field ->
{
- List values = new ArrayList<>();
- Stream.of(field.getValues()).forEach((val) -> values.add(val));
- originalHeaders.put(field.getName(), values);
+ originalHeaders.putIfAbsent(field.getName(), new ArrayList<>());
+ List values = originalHeaders.get(field.getName());
+ Collections.addAll(values, field.getValues());
});
// Give headers to configurator
@@ -63,26 +60,23 @@ public class JsrUpgradeListener implements UpgradeListener
// Reset headers on HttpRequest per configurator
fields.clear();
- originalHeaders.forEach((name, values) -> fields.put(name, values));
+ originalHeaders.forEach(fields::put);
}
@Override
public void onHandshakeResponse(HttpRequest request, HttpResponse response)
{
if (configurator == null)
- {
return;
- }
HandshakeResponse handshakeResponse = () ->
{
- HttpFields fields = response.getHeaders();
Map> ret = new HashMap<>();
- fields.forEach((field) ->
+ response.getHeaders().forEach(field ->
{
- List values = new ArrayList<>();
- Stream.of(field.getValues()).forEach((val) -> values.add(val));
- ret.put(field.getName(), values);
+ ret.putIfAbsent(field.getName(), new ArrayList<>());
+ List values = ret.get(field.getName());
+ Collections.addAll(values, field.getValues());
});
return ret;
};
From 4e3c0c8cd7c05fb50774d5abda18bcca0181a7bf Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Tue, 7 Apr 2020 12:04:24 +0200
Subject: [PATCH 054/101] Fixes #4751 - Refresh NetworkTraffic* classes.
Introduced NetworkTrafficSocketChannelEndPoint to replace
NetworkTrafficSelectChannelEndPoint, now deprecated.
Code and javadocs cleanup.
Moved the tests to jetty-client so that also the client is tested.
Signed-off-by: Simone Bordet
---
.../client/NetworkTrafficListenerTest.java | 526 ++++++++++++++++++
.../jetty/io/NetworkTrafficListener.java | 20 +-
.../NetworkTrafficSelectChannelEndPoint.java | 127 +----
.../NetworkTrafficSocketChannelEndPoint.java | 136 +++++
.../server/NetworkTrafficServerConnector.java | 8 +-
.../server/NetworkTrafficListenerTest.java | 484 ----------------
6 files changed, 687 insertions(+), 614 deletions(-)
create mode 100644 jetty-client/src/test/java/org/eclipse/jetty/client/NetworkTrafficListenerTest.java
create mode 100644 jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSocketChannelEndPoint.java
delete mode 100644 jetty-server/src/test/java/org/eclipse/jetty/server/NetworkTrafficListenerTest.java
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/NetworkTrafficListenerTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/NetworkTrafficListenerTest.java
new file mode 100644
index 00000000000..cc93df28d29
--- /dev/null
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/NetworkTrafficListenerTest.java
@@ -0,0 +1,526 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
+import org.eclipse.jetty.client.util.FormContentProvider;
+import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpHeaderValue;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.ManagedSelector;
+import org.eclipse.jetty.io.NetworkTrafficListener;
+import org.eclipse.jetty.io.NetworkTrafficSocketChannelEndPoint;
+import org.eclipse.jetty.io.SelectorManager;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.NetworkTrafficServerConnector;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Fields;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class NetworkTrafficListenerTest
+{
+ private static final String END_OF_CONTENT = "~";
+
+ private Server server;
+ private NetworkTrafficServerConnector connector;
+ private NetworkTrafficHttpClient client;
+
+ private void start(Handler handler) throws Exception
+ {
+ startServer(handler);
+ startClient();
+ }
+
+ private void startServer(Handler handler) throws Exception
+ {
+ server = new Server();
+ connector = new NetworkTrafficServerConnector(server);
+ connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendDateHeader(false);
+ connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendServerVersion(false);
+ server.addConnector(connector);
+ server.setHandler(handler);
+ server.start();
+ }
+
+ private void startClient() throws Exception
+ {
+ client = new NetworkTrafficHttpClient(new ArrayList<>());
+ client.start();
+ }
+
+ @AfterEach
+ public void dispose() throws Exception
+ {
+ if (client != null)
+ client.stop();
+ if (server != null)
+ server.stop();
+ }
+
+ @Test
+ public void testOpenedClosedAreInvoked() throws Exception
+ {
+ startServer(null);
+
+ CountDownLatch openedLatch = new CountDownLatch(1);
+ CountDownLatch closedLatch = new CountDownLatch(1);
+ connector.addNetworkTrafficListener(new NetworkTrafficListener()
+ {
+ public volatile Socket socket;
+
+ @Override
+ public void opened(Socket socket)
+ {
+ this.socket = socket;
+ openedLatch.countDown();
+ }
+
+ @Override
+ public void closed(Socket socket)
+ {
+ if (this.socket == socket)
+ closedLatch.countDown();
+ }
+ });
+ int port = connector.getLocalPort();
+
+ // Connect to the server
+ try (Socket ignored = new Socket("localhost", port))
+ {
+ assertTrue(openedLatch.await(10, TimeUnit.SECONDS));
+ }
+ assertTrue(closedLatch.await(10, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testTrafficWithNoResponseContentOnNonPersistentConnection() throws Exception
+ {
+ start(new AbstractHandler()
+ {
+ @Override
+ public void handle(String uri, Request request, HttpServletRequest servletRequest, HttpServletResponse servletResponse)
+ {
+ request.setHandled(true);
+ }
+ });
+
+ AtomicReference serverIncoming = new AtomicReference<>("");
+ CountDownLatch serverIncomingLatch = new CountDownLatch(1);
+ AtomicReference serverOutgoing = new AtomicReference<>("");
+ CountDownLatch serverOutgoingLatch = new CountDownLatch(1);
+ connector.addNetworkTrafficListener(new NetworkTrafficListener()
+ {
+ @Override
+ public void incoming(Socket socket, ByteBuffer bytes)
+ {
+ serverIncoming.set(serverIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ serverIncomingLatch.countDown();
+ }
+
+ @Override
+ public void outgoing(Socket socket, ByteBuffer bytes)
+ {
+ serverOutgoing.set(serverOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ serverOutgoingLatch.countDown();
+ }
+ });
+
+ AtomicReference clientIncoming = new AtomicReference<>("");
+ CountDownLatch clientIncomingLatch = new CountDownLatch(1);
+ AtomicReference clientOutgoing = new AtomicReference<>("");
+ CountDownLatch clientOutgoingLatch = new CountDownLatch(1);
+ client.listeners.add(new NetworkTrafficListener()
+ {
+ @Override
+ public void outgoing(Socket socket, ByteBuffer bytes)
+ {
+ clientOutgoing.set(clientOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ clientOutgoingLatch.countDown();
+ }
+
+ @Override
+ public void incoming(Socket socket, ByteBuffer bytes)
+ {
+ clientIncoming.set(clientIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ clientIncomingLatch.countDown();
+ }
+ });
+
+ ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+ .header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
+ .send();
+ assertEquals(HttpStatus.OK_200, response.getStatus());
+
+ assertTrue(clientOutgoingLatch.await(1, TimeUnit.SECONDS));
+ assertTrue(serverIncomingLatch.await(1, TimeUnit.SECONDS));
+ assertTrue(serverOutgoingLatch.await(1, TimeUnit.SECONDS));
+ assertTrue(clientIncomingLatch.await(1, TimeUnit.SECONDS));
+ assertEquals(clientOutgoing.get(), serverIncoming.get());
+ assertEquals(serverOutgoing.get(), clientIncoming.get());
+ }
+
+ @Test
+ public void testTrafficWithResponseContentOnPersistentConnection() throws Exception
+ {
+ String responseContent = "response_content" + END_OF_CONTENT;
+ start(new AbstractHandler()
+ {
+ @Override
+ public void handle(String uri, Request request, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException
+ {
+ request.setHandled(true);
+ ServletOutputStream output = servletResponse.getOutputStream();
+ output.write(responseContent.getBytes(StandardCharsets.UTF_8));
+ }
+ });
+
+ AtomicReference serverIncoming = new AtomicReference<>("");
+ CountDownLatch serverIncomingLatch = new CountDownLatch(1);
+ AtomicReference serverOutgoing = new AtomicReference<>("");
+ CountDownLatch serverOutgoingLatch = new CountDownLatch(1);
+ connector.addNetworkTrafficListener(new NetworkTrafficListener()
+ {
+ @Override
+ public void incoming(Socket socket, ByteBuffer bytes)
+ {
+ serverIncoming.set(serverIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ serverIncomingLatch.countDown();
+ }
+
+ @Override
+ public void outgoing(Socket socket, ByteBuffer bytes)
+ {
+ serverOutgoing.set(serverOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ serverOutgoingLatch.countDown();
+ }
+ });
+
+ AtomicReference clientIncoming = new AtomicReference<>("");
+ CountDownLatch clientIncomingLatch = new CountDownLatch(1);
+ AtomicReference clientOutgoing = new AtomicReference<>("");
+ CountDownLatch clientOutgoingLatch = new CountDownLatch(1);
+ client.listeners.add(new NetworkTrafficListener()
+ {
+ @Override
+ public void outgoing(Socket socket, ByteBuffer bytes)
+ {
+ clientOutgoing.set(clientOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ clientOutgoingLatch.countDown();
+ }
+
+ @Override
+ public void incoming(Socket socket, ByteBuffer bytes)
+ {
+ clientIncoming.set(clientIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ clientIncomingLatch.countDown();
+ }
+ });
+
+ ContentResponse response = client.newRequest("localhost", connector.getLocalPort()).send();
+ assertEquals(HttpStatus.OK_200, response.getStatus());
+ assertEquals(responseContent, response.getContentAsString());
+
+ assertTrue(clientOutgoingLatch.await(1, TimeUnit.SECONDS));
+ assertTrue(serverIncomingLatch.await(1, TimeUnit.SECONDS));
+ assertTrue(serverOutgoingLatch.await(1, TimeUnit.SECONDS));
+ assertTrue(clientIncomingLatch.await(1, TimeUnit.SECONDS));
+ assertEquals(clientOutgoing.get(), serverIncoming.get());
+ assertEquals(serverOutgoing.get(), clientIncoming.get());
+ }
+
+ @Test
+ public void testTrafficWithResponseContentChunkedOnPersistentConnection() throws Exception
+ {
+ String responseContent = "response_content";
+ String responseChunk1 = responseContent.substring(0, responseContent.length() / 2);
+ String responseChunk2 = responseContent.substring(responseContent.length() / 2);
+ start(new AbstractHandler()
+ {
+ @Override
+ public void handle(String uri, Request request, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException
+ {
+ request.setHandled(true);
+ ServletOutputStream output = servletResponse.getOutputStream();
+ output.write(responseChunk1.getBytes(StandardCharsets.UTF_8));
+ output.flush();
+ output.write(responseChunk2.getBytes(StandardCharsets.UTF_8));
+ output.flush();
+ }
+ });
+
+ AtomicReference serverIncoming = new AtomicReference<>("");
+ CountDownLatch serverIncomingLatch = new CountDownLatch(1);
+ AtomicReference serverOutgoing = new AtomicReference<>("");
+ CountDownLatch serverOutgoingLatch = new CountDownLatch(1);
+ connector.addNetworkTrafficListener(new NetworkTrafficListener()
+ {
+ @Override
+ public void incoming(Socket socket, ByteBuffer bytes)
+ {
+ serverIncoming.set(serverIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ serverIncomingLatch.countDown();
+ }
+
+ @Override
+ public void outgoing(Socket socket, ByteBuffer bytes)
+ {
+ serverOutgoing.set(serverOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ if (serverOutgoing.get().endsWith("\r\n0\r\n\r\n"))
+ serverOutgoingLatch.countDown();
+ }
+ });
+
+ AtomicReference clientIncoming = new AtomicReference<>("");
+ CountDownLatch clientIncomingLatch = new CountDownLatch(1);
+ AtomicReference clientOutgoing = new AtomicReference<>("");
+ CountDownLatch clientOutgoingLatch = new CountDownLatch(1);
+ client.listeners.add(new NetworkTrafficListener()
+ {
+ @Override
+ public void outgoing(Socket socket, ByteBuffer bytes)
+ {
+ clientOutgoing.set(clientOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ clientOutgoingLatch.countDown();
+ }
+
+ @Override
+ public void incoming(Socket socket, ByteBuffer bytes)
+ {
+ clientIncoming.set(clientIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ if (clientIncoming.get().endsWith("\r\n0\r\n\r\n"))
+ clientIncomingLatch.countDown();
+ }
+ });
+
+ ContentResponse response = client.newRequest("localhost", connector.getLocalPort()).send();
+ assertEquals(HttpStatus.OK_200, response.getStatus());
+
+ assertTrue(clientOutgoingLatch.await(1, TimeUnit.SECONDS));
+ assertTrue(serverIncomingLatch.await(1, TimeUnit.SECONDS));
+ assertTrue(serverOutgoingLatch.await(1, TimeUnit.SECONDS));
+ assertTrue(clientIncomingLatch.await(1, TimeUnit.SECONDS));
+ assertEquals(clientOutgoing.get(), serverIncoming.get());
+ assertEquals(serverOutgoing.get(), clientIncoming.get());
+ }
+
+ @Test
+ public void testTrafficWithRequestContentWithResponseRedirectOnPersistentConnection() throws Exception
+ {
+ String location = "/redirect";
+ start(new AbstractHandler()
+ {
+ @Override
+ public void handle(String uri, Request request, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException
+ {
+ request.setHandled(true);
+ servletResponse.sendRedirect(location);
+ }
+ });
+
+ AtomicReference serverIncoming = new AtomicReference<>("");
+ CountDownLatch serverIncomingLatch = new CountDownLatch(1);
+ AtomicReference serverOutgoing = new AtomicReference<>("");
+ CountDownLatch serverOutgoingLatch = new CountDownLatch(1);
+ connector.addNetworkTrafficListener(new NetworkTrafficListener()
+ {
+ @Override
+ public void incoming(Socket socket, ByteBuffer bytes)
+ {
+ serverIncoming.set(serverIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ serverIncomingLatch.countDown();
+ }
+
+ @Override
+ public void outgoing(Socket socket, ByteBuffer bytes)
+ {
+ serverOutgoing.set(serverOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ serverOutgoingLatch.countDown();
+ }
+ });
+
+ AtomicReference clientIncoming = new AtomicReference<>("");
+ CountDownLatch clientIncomingLatch = new CountDownLatch(1);
+ AtomicReference clientOutgoing = new AtomicReference<>("");
+ CountDownLatch clientOutgoingLatch = new CountDownLatch(1);
+ client.listeners.add(new NetworkTrafficListener()
+ {
+ @Override
+ public void outgoing(Socket socket, ByteBuffer bytes)
+ {
+ clientOutgoing.set(clientOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ clientOutgoingLatch.countDown();
+ }
+
+ @Override
+ public void incoming(Socket socket, ByteBuffer bytes)
+ {
+ clientIncoming.set(clientIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ clientIncomingLatch.countDown();
+ }
+ });
+
+ client.setFollowRedirects(false);
+ Fields fields = new Fields();
+ fields.put("a", "1");
+ fields.put("b", "2");
+ ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+ .content(new FormContentProvider(fields))
+ .send();
+ assertEquals(HttpStatus.FOUND_302, response.getStatus());
+
+ assertTrue(clientOutgoingLatch.await(1, TimeUnit.SECONDS));
+ assertTrue(serverIncomingLatch.await(1, TimeUnit.SECONDS));
+ assertTrue(serverOutgoingLatch.await(1, TimeUnit.SECONDS));
+ assertTrue(clientIncomingLatch.await(1, TimeUnit.SECONDS));
+ assertEquals(clientOutgoing.get(), serverIncoming.get());
+ assertEquals(serverOutgoing.get(), clientIncoming.get());
+ }
+
+ @Test
+ public void testTrafficWithBigRequestContentOnPersistentConnection() throws Exception
+ {
+ start(new AbstractHandler()
+ {
+ @Override
+ public void handle(String uri, Request request, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException
+ {
+ // Read and discard the request body to make the test more
+ // reliable, otherwise there is a race between request body
+ // upload and response download
+ InputStream input = servletRequest.getInputStream();
+ byte[] buffer = new byte[4096];
+ while (true)
+ {
+ int read = input.read(buffer);
+ if (read < 0)
+ break;
+ }
+ request.setHandled(true);
+ }
+ });
+
+ AtomicReference serverIncoming = new AtomicReference<>("");
+ AtomicReference serverOutgoing = new AtomicReference<>("");
+ CountDownLatch serverOutgoingLatch = new CountDownLatch(1);
+ connector.addNetworkTrafficListener(new NetworkTrafficListener()
+ {
+ @Override
+ public void incoming(Socket socket, ByteBuffer bytes)
+ {
+ serverIncoming.set(serverIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ }
+
+ @Override
+ public void outgoing(Socket socket, ByteBuffer bytes)
+ {
+ serverOutgoing.set(serverOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ serverOutgoingLatch.countDown();
+ }
+ });
+
+ AtomicReference clientIncoming = new AtomicReference<>("");
+ CountDownLatch clientIncomingLatch = new CountDownLatch(1);
+ AtomicReference clientOutgoing = new AtomicReference<>("");
+ client.listeners.add(new NetworkTrafficListener()
+ {
+ @Override
+ public void outgoing(Socket socket, ByteBuffer bytes)
+ {
+ clientOutgoing.set(clientOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ }
+
+ @Override
+ public void incoming(Socket socket, ByteBuffer bytes)
+ {
+ clientIncoming.set(clientIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
+ clientIncomingLatch.countDown();
+ }
+ });
+
+ // Generate a large request content.
+ String requestContent = "0123456789ABCDEF";
+ for (int i = 0; i < 16; ++i)
+ {
+ requestContent += requestContent;
+ }
+
+ ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+ .content(new StringContentProvider(requestContent))
+ .send();
+ assertEquals(HttpStatus.OK_200, response.getStatus());
+
+ assertTrue(serverOutgoingLatch.await(1, TimeUnit.SECONDS));
+ assertTrue(clientIncomingLatch.await(1, TimeUnit.SECONDS));
+ assertEquals(clientOutgoing.get(), serverIncoming.get());
+ assertTrue(clientOutgoing.get().length() > requestContent.length());
+ assertEquals(serverOutgoing.get(), clientIncoming.get());
+ }
+
+ private static class NetworkTrafficHttpClient extends HttpClient
+ {
+ private final List listeners;
+
+ private NetworkTrafficHttpClient(List listeners)
+ {
+ super(new HttpClientTransportOverHTTP()
+ {
+ @Override
+ protected SelectorManager newSelectorManager(HttpClient client)
+ {
+ return new AbstractConnectorHttpClientTransport.ClientSelectorManager(client, getSelectors())
+ {
+ @Override
+ protected EndPoint newEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey key)
+ {
+ return new NetworkTrafficSocketChannelEndPoint(channel, selector, key, getScheduler(), client.getIdleTimeout(), listeners);
+ }
+ };
+ }
+ }, null);
+ this.listeners = listeners;
+ }
+ }
+}
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficListener.java b/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficListener.java
index 92075bda28f..f2897ba2398 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficListener.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficListener.java
@@ -24,7 +24,7 @@ import java.nio.ByteBuffer;
/**
*
A listener for raw network traffic within Jetty.
*
{@link NetworkTrafficListener}s can be installed in a
- * org.eclipse.jetty.server.nio.NetworkTrafficSelectChannelConnector,
+ * {@code org.eclipse.jetty.server.NetworkTrafficServerConnector},
* and are notified of the following network traffic events:
*
*
Connection opened, when the server has accepted the connection from a remote client
@@ -45,7 +45,9 @@ public interface NetworkTrafficListener
*
* @param socket the socket associated with the remote client
*/
- void opened(Socket socket);
+ default void opened(Socket socket)
+ {
+ }
/**
*
Callback method invoked when bytes sent by a remote client arrived on the server.
@@ -53,7 +55,9 @@ public interface NetworkTrafficListener
* @param socket the socket associated with the remote client
* @param bytes the read-only buffer containing the incoming bytes
*/
- void incoming(Socket socket, ByteBuffer bytes);
+ default void incoming(Socket socket, ByteBuffer bytes)
+ {
+ }
/**
*
Callback method invoked when bytes are sent to a remote client from the server.
@@ -62,7 +66,9 @@ public interface NetworkTrafficListener
* @param socket the socket associated with the remote client
* @param bytes the read-only buffer containing the outgoing bytes
*/
- void outgoing(Socket socket, ByteBuffer bytes);
+ default void outgoing(Socket socket, ByteBuffer bytes)
+ {
+ }
/**
*
Callback method invoked when a connection to a remote client has been closed.
@@ -74,11 +80,15 @@ public interface NetworkTrafficListener
*
* @param socket the (closed) socket associated with the remote client
*/
- void closed(Socket socket);
+ default void closed(Socket socket)
+ {
+ }
/**
*
A commodity class that implements {@link NetworkTrafficListener} with empty methods.
A specialized version of {@link SocketChannelEndPoint} that supports {@link NetworkTrafficListener}s.
+ */
+public class NetworkTrafficSocketChannelEndPoint extends SocketChannelEndPoint
+{
+ private static final Logger LOG = Log.getLogger(NetworkTrafficSocketChannelEndPoint.class);
+
+ private final List listeners;
+
+ public NetworkTrafficSocketChannelEndPoint(SelectableChannel channel, ManagedSelector selectSet, SelectionKey key, Scheduler scheduler, long idleTimeout, List listeners)
+ {
+ super(channel, selectSet, key, scheduler);
+ setIdleTimeout(idleTimeout);
+ this.listeners = listeners;
+ }
+
+ @Override
+ public int fill(ByteBuffer buffer) throws IOException
+ {
+ int read = super.fill(buffer);
+ notifyIncoming(buffer, read);
+ return read;
+ }
+
+ @Override
+ public boolean flush(ByteBuffer... buffers) throws IOException
+ {
+ boolean flushed = true;
+ for (ByteBuffer b : buffers)
+ {
+ if (b.hasRemaining())
+ {
+ int position = b.position();
+ ByteBuffer view = b.slice();
+ flushed = super.flush(b);
+ int l = b.position() - position;
+ view.limit(view.position() + l);
+ notifyOutgoing(view);
+ if (!flushed)
+ break;
+ }
+ }
+ return flushed;
+ }
+
+ @Override
+ public void onOpen()
+ {
+ super.onOpen();
+ if (listeners != null && !listeners.isEmpty())
+ {
+ for (NetworkTrafficListener listener : listeners)
+ {
+ try
+ {
+ listener.opened(getSocket());
+ }
+ catch (Exception x)
+ {
+ LOG.warn(x);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onClose()
+ {
+ super.onClose();
+ if (listeners != null && !listeners.isEmpty())
+ {
+ for (NetworkTrafficListener listener : listeners)
+ {
+ try
+ {
+ listener.closed(getSocket());
+ }
+ catch (Exception x)
+ {
+ LOG.warn(x);
+ }
+ }
+ }
+ }
+
+ public void notifyIncoming(ByteBuffer buffer, int read)
+ {
+ if (listeners != null && !listeners.isEmpty() && read > 0)
+ {
+ for (NetworkTrafficListener listener : listeners)
+ {
+ try
+ {
+ ByteBuffer view = buffer.asReadOnlyBuffer();
+ listener.incoming(getSocket(), view);
+ }
+ catch (Exception x)
+ {
+ LOG.warn(x);
+ }
+ }
+ }
+ }
+
+ public void notifyOutgoing(ByteBuffer view)
+ {
+ if (listeners != null && !listeners.isEmpty() && view.hasRemaining())
+ {
+ Socket socket = getSocket();
+ for (NetworkTrafficListener listener : listeners)
+ {
+ try
+ {
+ listener.outgoing(socket, view);
+ }
+ catch (Exception x)
+ {
+ LOG.warn(x);
+ }
+ }
+ }
+ }
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java
index dab17e501b0..4c635be66ed 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java
@@ -18,7 +18,6 @@
package org.eclipse.jetty.server;
-import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.List;
@@ -29,7 +28,7 @@ import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ChannelEndPoint;
import org.eclipse.jetty.io.ManagedSelector;
import org.eclipse.jetty.io.NetworkTrafficListener;
-import org.eclipse.jetty.io.NetworkTrafficSelectChannelEndPoint;
+import org.eclipse.jetty.io.NetworkTrafficSocketChannelEndPoint;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.Scheduler;
@@ -84,9 +83,8 @@ public class NetworkTrafficServerConnector extends ServerConnector
}
@Override
- protected ChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException
+ protected ChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key)
{
- NetworkTrafficSelectChannelEndPoint endPoint = new NetworkTrafficSelectChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout(), listeners);
- return endPoint;
+ return new NetworkTrafficSocketChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout(), listeners);
}
}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/NetworkTrafficListenerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/NetworkTrafficListenerTest.java
deleted file mode 100644
index a64b7569466..00000000000
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/NetworkTrafficListenerTest.java
+++ /dev/null
@@ -1,484 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-// ------------------------------------------------------------------------
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.opensource.org/licenses/apache2.0.php
-//
-// You may elect to redistribute this code under either of these licenses.
-// ========================================================================
-//
-
-package org.eclipse.jetty.server;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.io.NetworkTrafficListener;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.util.BufferUtil;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-public class NetworkTrafficListenerTest
-{
- private static final byte END_OF_CONTENT = '~';
-
- private Server server;
- private NetworkTrafficServerConnector connector;
-
- public void initConnector(Handler handler) throws Exception
- {
- server = new Server();
-
- connector = new NetworkTrafficServerConnector(server);
- connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendDateHeader(false);
- connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendServerVersion(false);
- server.addConnector(connector);
- server.setHandler(handler);
- server.start();
- }
-
- @AfterEach
- public void destroyConnector() throws Exception
- {
- if (server != null)
- {
- server.stop();
- server.join();
- }
- }
-
- @Test
- public void testOpenedClosedAreInvoked() throws Exception
- {
- initConnector(null);
-
- final CountDownLatch openedLatch = new CountDownLatch(1);
- final CountDownLatch closedLatch = new CountDownLatch(1);
- connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
- {
- public volatile Socket socket;
-
- @Override
- public void opened(Socket socket)
- {
- this.socket = socket;
- openedLatch.countDown();
- }
-
- @Override
- public void closed(Socket socket)
- {
- if (this.socket == socket)
- closedLatch.countDown();
- }
- });
- int port = connector.getLocalPort();
-
- // Connect to the server
- Socket socket = new Socket("localhost", port);
- assertTrue(openedLatch.await(10, TimeUnit.SECONDS));
-
- socket.close();
- assertTrue(closedLatch.await(10, TimeUnit.SECONDS));
- }
-
- @Test
- public void testTrafficWithNoResponseContentOnNonPersistentConnection() throws Exception
- {
- initConnector(new AbstractHandler()
- {
- @Override
- public void handle(String uri, Request request, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException, ServletException
- {
- request.setHandled(true);
- }
- });
-
- final AtomicReference incomingData = new AtomicReference<>();
- final CountDownLatch incomingLatch = new CountDownLatch(1);
- final AtomicReference outgoingData = new AtomicReference<>("");
- final CountDownLatch outgoingLatch = new CountDownLatch(1);
- connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
- {
- @Override
- public void incoming(Socket socket, ByteBuffer bytes)
- {
- incomingData.set(BufferUtil.toString(bytes, StandardCharsets.UTF_8));
- incomingLatch.countDown();
- }
-
- @Override
- public void outgoing(Socket socket, ByteBuffer bytes)
- {
- outgoingData.set(outgoingData.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
- outgoingLatch.countDown();
- }
- });
- int port = connector.getLocalPort();
-
- String request =
- "GET / HTTP/1.1\r\n" +
- "Host: localhost:" + port + "\r\n" +
- "Connection: close\r\n" +
- "\r\n";
- String expectedResponse =
- "HTTP/1.1 200 OK\r\n" +
- "Connection: close\r\n" +
- "\r\n";
-
- Socket socket = new Socket("localhost", port);
- OutputStream output = socket.getOutputStream();
- output.write(request.getBytes(StandardCharsets.UTF_8));
- output.flush();
-
- assertTrue(incomingLatch.await(1, TimeUnit.SECONDS));
- assertEquals(request, incomingData.get());
-
- assertTrue(outgoingLatch.await(1, TimeUnit.SECONDS));
- assertEquals(expectedResponse, outgoingData.get());
-
- byte[] responseBytes = readResponse(socket);
- String response = new String(responseBytes, StandardCharsets.UTF_8);
- assertEquals(expectedResponse, response);
-
- socket.close();
- }
-
- @Test
- public void testTrafficWithResponseContentOnPersistentConnection() throws Exception
- {
- final String responseContent = "response_content";
- initConnector(new AbstractHandler()
- {
- @Override
- public void handle(String uri, Request request, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException, ServletException
- {
- request.setHandled(true);
- ServletOutputStream output = servletResponse.getOutputStream();
- output.write(responseContent.getBytes(StandardCharsets.UTF_8));
- output.write(END_OF_CONTENT);
- }
- });
-
- final AtomicReference incomingData = new AtomicReference<>();
- final CountDownLatch incomingLatch = new CountDownLatch(1);
- final AtomicReference outgoingData = new AtomicReference<>("");
- final CountDownLatch outgoingLatch = new CountDownLatch(2);
- connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
- {
- @Override
- public void incoming(Socket socket, ByteBuffer bytes)
- {
- incomingData.set(BufferUtil.toString(bytes, StandardCharsets.UTF_8));
- incomingLatch.countDown();
- }
-
- @Override
- public void outgoing(Socket socket, ByteBuffer bytes)
- {
- outgoingData.set(outgoingData.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
- outgoingLatch.countDown();
- }
- });
- int port = connector.getLocalPort();
-
- String request =
- "GET / HTTP/1.1\r\n" +
- "Host: localhost:" + port + "\r\n" +
- "\r\n";
- String expectedResponse =
- "HTTP/1.1 200 OK\r\n" +
- "Content-Length: " + (responseContent.length() + 1) + "\r\n" +
- "\r\n" +
- "" + responseContent + (char)END_OF_CONTENT;
-
- Socket socket = new Socket("localhost", port);
- OutputStream output = socket.getOutputStream();
- output.write(request.getBytes(StandardCharsets.UTF_8));
- output.flush();
-
- assertTrue(incomingLatch.await(1, TimeUnit.SECONDS));
- assertEquals(request, incomingData.get());
-
- assertTrue(outgoingLatch.await(1, TimeUnit.SECONDS));
- assertEquals(expectedResponse, outgoingData.get());
-
- byte[] responseBytes = readResponse(socket);
- String response = new String(responseBytes, StandardCharsets.UTF_8);
- assertEquals(expectedResponse, response);
-
- socket.close();
- }
-
- @Test
- public void testTrafficWithResponseContentChunkedOnPersistentConnection() throws Exception
- {
- final String responseContent = "response_content";
- final String responseChunk1 = "response_content".substring(0, responseContent.length() / 2);
- final String responseChunk2 = "response_content".substring(responseContent.length() / 2, responseContent.length());
- initConnector(new AbstractHandler()
- {
- @Override
- public void handle(String uri, Request request, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException, ServletException
- {
- request.setHandled(true);
- ServletOutputStream output = servletResponse.getOutputStream();
- output.write(responseChunk1.getBytes(StandardCharsets.UTF_8));
- output.flush();
- output.write(responseChunk2.getBytes(StandardCharsets.UTF_8));
- output.flush();
- }
- });
-
- final AtomicReference incomingData = new AtomicReference<>();
- final CountDownLatch incomingLatch = new CountDownLatch(1);
- final AtomicReference outgoingData = new AtomicReference<>("");
- final CountDownLatch outgoingLatch = new CountDownLatch(1);
- connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
- {
- @Override
- public void incoming(Socket socket, ByteBuffer bytes)
- {
- incomingData.set(BufferUtil.toString(bytes, StandardCharsets.UTF_8));
- incomingLatch.countDown();
- }
-
- @Override
- public void outgoing(Socket socket, ByteBuffer bytes)
- {
- outgoingData.set(outgoingData.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
- if (outgoingData.get().endsWith("\r\n0\r\n\r\n"))
- outgoingLatch.countDown();
- }
- });
- int port = connector.getLocalPort();
-
- String request =
- "GET / HTTP/1.1\r\n" +
- "Host: localhost:" + port + "\r\n" +
- "\r\n";
- String expectedResponse =
- "HTTP/1.1 200 OK\r\n" +
- "Transfer-Encoding: chunked\r\n" +
- "\r\n" +
- responseChunk1.length() + "\r\n" +
- responseChunk1 + "\r\n" +
- responseChunk2.length() + "\r\n" +
- responseChunk2 + "\r\n" +
- "0\r\n" +
- "\r\n";
-
- Socket socket = new Socket("localhost", port);
- OutputStream output = socket.getOutputStream();
- output.write(request.getBytes(StandardCharsets.UTF_8));
- output.flush();
-
- assertTrue(incomingLatch.await(1, TimeUnit.SECONDS));
- assertEquals(request, incomingData.get());
-
- assertTrue(outgoingLatch.await(1, TimeUnit.SECONDS));
- assertEquals(expectedResponse, outgoingData.get());
-
- byte[] responseBytes = readResponse(socket);
- String response = new String(responseBytes, StandardCharsets.UTF_8);
- assertEquals(expectedResponse, response);
-
- socket.close();
- }
-
- @Test
- public void testTrafficWithRequestContentWithResponseRedirectOnPersistentConnection() throws Exception
- {
- final String location = "/redirect";
- initConnector(new AbstractHandler()
- {
- @Override
- public void handle(String uri, Request request, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException, ServletException
- {
- request.setHandled(true);
- servletResponse.sendRedirect(location);
- }
- });
-
- final AtomicReference incomingData = new AtomicReference<>();
- final CountDownLatch incomingLatch = new CountDownLatch(1);
- final AtomicReference outgoingData = new AtomicReference<>("");
- final CountDownLatch outgoingLatch = new CountDownLatch(1);
- connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
- {
- @Override
- public void incoming(Socket socket, ByteBuffer bytes)
- {
- incomingData.set(BufferUtil.toString(bytes, StandardCharsets.UTF_8));
- incomingLatch.countDown();
- }
-
- @Override
- public void outgoing(Socket socket, ByteBuffer bytes)
- {
- outgoingData.set(outgoingData.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
- outgoingLatch.countDown();
- }
- });
- int port = connector.getLocalPort();
-
- String requestContent = "a=1&b=2";
- String request =
- "POST / HTTP/1.1\r\n" +
- "Host: localhost:" + port + "\r\n" +
- "Content-Type: application/x-www-form-urlencoded\r\n" +
- "Content-Length: " + requestContent.length() + "\r\n" +
- "\r\n" +
- requestContent;
- String expectedResponse =
- "HTTP/1.1 302 Found\r\n" +
- "Location: http://localhost:" + port + location + "\r\n" +
- "Content-Length: 0\r\n" +
- "\r\n";
-
- Socket socket = new Socket("localhost", port);
- OutputStream output = socket.getOutputStream();
- output.write(request.getBytes(StandardCharsets.UTF_8));
- output.flush();
-
- assertTrue(incomingLatch.await(1, TimeUnit.SECONDS));
- assertEquals(request, incomingData.get());
-
- assertTrue(outgoingLatch.await(1, TimeUnit.SECONDS));
- assertEquals(expectedResponse, outgoingData.get());
-
- byte[] responseBytes = readResponse(socket);
- String response = new String(responseBytes, StandardCharsets.UTF_8);
- assertEquals(expectedResponse, response);
-
- socket.close();
- }
-
- @Test
- public void testTrafficWithBigRequestContentOnPersistentConnection() throws Exception
- {
- initConnector(new AbstractHandler()
- {
- @Override
- public void handle(String uri, Request request, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException, ServletException
- {
- // Read and discard the request body to make the test more
- // reliable, otherwise there is a race between request body
- // upload and response download
- InputStream input = servletRequest.getInputStream();
- byte[] buffer = new byte[4096];
- while (true)
- {
- int read = input.read(buffer);
- if (read < 0)
- break;
- }
- request.setHandled(true);
- }
- });
-
- final AtomicReference incomingData = new AtomicReference<>("");
- final AtomicReference outgoingData = new AtomicReference<>("");
- final CountDownLatch outgoingLatch = new CountDownLatch(1);
- connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
- {
- @Override
- public void incoming(Socket socket, ByteBuffer bytes)
- {
- incomingData.set(incomingData.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
- }
-
- @Override
- public void outgoing(Socket socket, ByteBuffer bytes)
- {
- outgoingData.set(outgoingData.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
- outgoingLatch.countDown();
- }
- });
- int port = connector.getLocalPort();
-
- // Generate 32 KiB of request content
- String requestContent = "0123456789ABCDEF";
- for (int i = 0; i < 11; ++i)
- {
- requestContent += requestContent;
- }
- String request =
- "POST / HTTP/1.1\r\n" +
- "Host: localhost:" + port + "\r\n" +
- "Content-Type: text/plain\r\n" +
- "Content-Length: " + requestContent.length() + "\r\n" +
- "\r\n" +
- requestContent;
- String expectedResponse =
- "HTTP/1.1 200 OK\r\n" +
- "Content-Length: 0\r\n" +
- "\r\n";
-
- Socket socket = new Socket("localhost", port);
- OutputStream output = socket.getOutputStream();
- output.write(request.getBytes(StandardCharsets.UTF_8));
- output.flush();
-
- assertTrue(outgoingLatch.await(1, TimeUnit.SECONDS));
- assertEquals(expectedResponse, outgoingData.get());
-
- byte[] responseBytes = readResponse(socket);
- String response = new String(responseBytes, StandardCharsets.UTF_8);
- assertEquals(expectedResponse, response);
-
- assertEquals(request, incomingData.get());
-
- socket.close();
- }
-
- private byte[] readResponse(Socket socket) throws IOException
- {
- socket.setSoTimeout(5000);
- InputStream input = socket.getInputStream();
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- int read;
- while ((read = input.read()) >= 0)
- {
- baos.write(read);
-
- // Handle non-chunked end of response
- if (read == END_OF_CONTENT)
- break;
-
- // Handle chunked end of response
- String response = baos.toString("UTF-8");
- if (response.endsWith("\r\n0\r\n\r\n"))
- break;
-
- // Handle non-content responses
- if (response.contains("Content-Length: 0") && response.endsWith("\r\n\r\n"))
- break;
- }
- return baos.toByteArray();
- }
-}
From 7ef05860ee88e6621bed2002f008de656e62927d Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Tue, 7 Apr 2020 12:19:49 +0200
Subject: [PATCH 055/101] Fixes #4751 - Refresh NetworkTraffic* classes.
Added missing license header.
Signed-off-by: Simone Bordet
---
.../NetworkTrafficSocketChannelEndPoint.java | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSocketChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSocketChannelEndPoint.java
index f8d74e0acfc..5d8d63f5806 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSocketChannelEndPoint.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSocketChannelEndPoint.java
@@ -1,3 +1,21 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
package org.eclipse.jetty.io;
import java.io.IOException;
From 4e69b483446f6ce6214c2f0d4ab001b0fd204aff Mon Sep 17 00:00:00 2001
From: Lachlan Roberts
Date: Wed, 8 Apr 2020 15:31:42 +1000
Subject: [PATCH 056/101] Issue #4747 - filter out synthetic annotated methods
Signed-off-by: Lachlan Roberts
---
.../javax/tests/SyntheticOnMessageTest.java | 112 ++++++++++++++++++
.../jetty/websocket/util/ReflectUtils.java | 21 ++--
2 files changed, 119 insertions(+), 14 deletions(-)
create mode 100644 jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/SyntheticOnMessageTest.java
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/SyntheticOnMessageTest.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/SyntheticOnMessageTest.java
new file mode 100644
index 00000000000..8130905ee42
--- /dev/null
+++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/SyntheticOnMessageTest.java
@@ -0,0 +1,112 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.javax.tests;
+
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.websocket.CloseReason;
+import javax.websocket.ContainerProvider;
+import javax.websocket.OnMessage;
+import javax.websocket.Session;
+import javax.websocket.WebSocketContainer;
+import javax.websocket.server.ServerEndpoint;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class SyntheticOnMessageTest
+{
+ private Server server;
+ private URI serverUri;
+ private ServerConnector connector;
+ private WebSocketContainer client;
+
+ @BeforeEach
+ public void before() throws Exception
+ {
+ server = new Server();
+ connector = new ServerConnector(server);
+ server.addConnector(connector);
+
+ ServletContextHandler contextHandler = new ServletContextHandler();
+ contextHandler.setContextPath("/");
+ JavaxWebSocketServletContainerInitializer.configure(contextHandler, (context, container) ->
+ container.addEndpoint(ServerSocket.class));
+ server.setHandler(contextHandler);
+ server.start();
+ serverUri = URI.create("ws://localhost:" + connector.getLocalPort());
+ client = ContainerProvider.getWebSocketContainer();
+ }
+
+ @AfterEach
+ public void after() throws Exception
+ {
+ LifeCycle.stop(client);
+ server.stop();
+ }
+
+ public static class AnnotatedEndpoint
+ {
+ public void onMessage(T message)
+ {
+ }
+ }
+
+ @ServerEndpoint("/")
+ public static class ServerSocket extends AnnotatedEndpoint
+ {
+ @OnMessage
+ public void onMessage(String message)
+ {
+ }
+ }
+
+ @Test
+ public void syntheticOnMessageTest() throws Exception
+ {
+ // ServerSocket has two annotated onMessage methods, one is a synthetic bridge method generated
+ // by the compiler and shouldn't be used.
+ List annotatedOnMessages = Stream.of(ServerSocket.class.getDeclaredMethods())
+ .filter(method -> method.getAnnotation(OnMessage.class) != null)
+ .collect(Collectors.toList());
+ assertThat(annotatedOnMessages.size(), is(2));
+
+ // We should correctly filter out all synthetic methods so we should not get an InvalidSignatureException.
+ EventSocket clientSocket = new EventSocket();
+ Session session = client.connectToServer(clientSocket, serverUri);
+ assertTrue(clientSocket.openLatch.await(5, TimeUnit.SECONDS));
+ session.close();
+ assertTrue(clientSocket.closeLatch.await(5, TimeUnit.SECONDS));
+ assertThat(clientSocket.closeReason.getCloseCode(), is(CloseReason.CloseCodes.NORMAL_CLOSURE));
+ }
+}
diff --git a/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/ReflectUtils.java b/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/ReflectUtils.java
index c038b45ead2..bb1a33e6e99 100644
--- a/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/ReflectUtils.java
+++ b/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/ReflectUtils.java
@@ -29,6 +29,7 @@ import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
+import java.util.stream.Stream;
public class ReflectUtils
{
@@ -220,27 +221,19 @@ public class ReflectUtils
public static Method[] findAnnotatedMethods(Class> pojo, Class extends Annotation> anno)
{
- List methods = null;
Class> clazz = pojo;
-
+ List methods = new ArrayList<>();
while ((clazz != null) && Object.class.isAssignableFrom(clazz))
{
- for (Method method : clazz.getDeclaredMethods())
- {
- if (method.getAnnotation(anno) != null)
- {
- if (methods == null)
- methods = new ArrayList<>();
- methods.add(method);
- }
- }
+ Stream.of(clazz.getDeclaredMethods())
+ .filter(method -> !method.isSynthetic() && (method.getAnnotation(anno) != null))
+ .forEach(methods::add);
clazz = clazz.getSuperclass();
}
- if (methods == null)
+ if (methods.isEmpty())
return null;
- int len = methods.size();
- return methods.toArray(new Method[len]);
+ return methods.toArray(new Method[0]);
}
/**
From 45f822ed328a93e0457c27308bed12290a827780 Mon Sep 17 00:00:00 2001
From: Lachlan Roberts
Date: Wed, 8 Apr 2020 17:08:52 +1000
Subject: [PATCH 057/101] Issue #4747 - server is allowed to not select a
websocket subprotocol
Signed-off-by: Lachlan Roberts
---
.../core/client/ClientUpgradeRequest.java | 2 --
.../server/internal/AbstractHandshaker.java | 7 +----
.../core/WebSocketNegotiationTest.java | 27 +++++++++++++------
3 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/client/ClientUpgradeRequest.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/client/ClientUpgradeRequest.java
index c37918952fe..3583989e71d 100644
--- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/client/ClientUpgradeRequest.java
+++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/client/ClientUpgradeRequest.java
@@ -409,8 +409,6 @@ public abstract class ClientUpgradeRequest extends HttpRequest implements Respon
// Verify the negotiated subprotocol
List offeredSubProtocols = getSubProtocols();
- if (negotiatedSubProtocol == null && !offeredSubProtocols.isEmpty())
- throw new WebSocketException("Upgrade failed: no subprotocol selected from offered subprotocols ");
if (negotiatedSubProtocol != null && !offeredSubProtocols.contains(negotiatedSubProtocol))
throw new WebSocketException("Upgrade failed: subprotocol [" + negotiatedSubProtocol + "] not found in offered subprotocols " + offeredSubProtocols);
diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java
index afd98b662d8..1a0d84c1c5b 100644
--- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java
+++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java
@@ -96,7 +96,7 @@ public abstract class AbstractHandshaker implements Handshaker
return false;
}
- // Validate negotiated protocol
+ // Validate negotiated protocol.
String protocol = negotiation.getSubprotocol();
List offeredProtocols = negotiation.getOfferedSubprotocols();
if (protocol != null)
@@ -104,11 +104,6 @@ public abstract class AbstractHandshaker implements Handshaker
if (!offeredProtocols.contains(protocol))
throw new WebSocketException("not upgraded: selected a protocol not present in offered protocols");
}
- else
- {
- if (!offeredProtocols.isEmpty())
- throw new WebSocketException("not upgraded: no protocol selected from offered protocols");
- }
// validate negotiated extensions
for (ExtensionConfig config : negotiation.getNegotiatedExtensions())
diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/WebSocketNegotiationTest.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/WebSocketNegotiationTest.java
index 5f9e5482426..237f8012625 100644
--- a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/WebSocketNegotiationTest.java
+++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/WebSocketNegotiationTest.java
@@ -317,17 +317,28 @@ public class WebSocketNegotiationTest extends WebSocketTester
public void testNoSubProtocolSelected() throws Exception
{
TestFrameHandler clientHandler = new TestFrameHandler();
-
ClientUpgradeRequest upgradeRequest = ClientUpgradeRequest.from(client, server.getUri(), clientHandler);
upgradeRequest.setSubProtocols("testNoSubProtocolSelected");
-
- try (StacklessLogging stacklessLogging = new StacklessLogging(HttpChannel.class))
+ CompletableFuture headers = new CompletableFuture<>();
+ upgradeRequest.addListener(new UpgradeListener()
{
- CompletableFuture connect = client.connect(upgradeRequest);
- Throwable t = assertThrows(ExecutionException.class, () -> connect.get(5, TimeUnit.SECONDS));
- assertThat(t.getMessage(), containsString("Failed to upgrade to websocket:"));
- assertThat(t.getMessage(), containsString("500 Server Error"));
- }
+ @Override
+ public void onHandshakeResponse(HttpRequest request, HttpResponse response)
+ {
+ headers.complete(response.getHeaders());
+ }
+ });
+
+ CoreSession session = client.connect(upgradeRequest).get(5, TimeUnit.SECONDS);
+ session.close(Callback.NOOP);
+ assertTrue(clientHandler.closed.await(5, TimeUnit.SECONDS));
+ assertThat(clientHandler.closeStatus.getCode(), is(CloseStatus.NO_CODE));
+
+ // RFC6455: If the server does not agree to any of the client's requested subprotocols, the only acceptable
+ // value is null. It MUST NOT send back a |Sec-WebSocket-Protocol| header field in its response.
+ HttpFields httpFields = headers.get();
+ assertThat(httpFields.get(HttpHeader.UPGRADE), is("WebSocket"));
+ assertNull(httpFields.get(HttpHeader.SEC_WEBSOCKET_SUBPROTOCOL));
}
@Test
From efe2dc1e90706f3d6ffc5711969f85c65e970ffe Mon Sep 17 00:00:00 2001
From: Jan Bartel
Date: Wed, 8 Apr 2020 11:16:20 +0200
Subject: [PATCH 058/101] Issue #4752 Call HttpSessionListener.sessionCreated
in add order. (#4753)
Signed-off-by: Jan Bartel
---
.../jetty/server/session/SessionHandler.java | 10 +--
.../server/session/SessionHandlerTest.java | 66 ++++++++++++++++++-
2 files changed, 70 insertions(+), 6 deletions(-)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java
index a3d624e5163..98c91bd0694 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java
@@ -279,7 +279,8 @@ public class SessionHandler extends ScopedHandler
}
/**
- * Call the session lifecycle listeners
+ * Call the session lifecycle listeners in
+ * the reverse order they were added.
*
* @param session the session on which to call the lifecycle listeners
*/
@@ -310,7 +311,8 @@ public class SessionHandler extends ScopedHandler
}
/**
- * Call the session lifecycle listeners
+ * Call the session lifecycle listeners in the order
+ * they were added.
*
* @param session the session on which to call the lifecycle listeners
*/
@@ -322,9 +324,9 @@ public class SessionHandler extends ScopedHandler
if (_sessionListeners != null)
{
HttpSessionEvent event = new HttpSessionEvent(session);
- for (int i = _sessionListeners.size() - 1; i >= 0; i--)
+ for (HttpSessionListener l : _sessionListeners)
{
- _sessionListeners.get(i).sessionCreated(event);
+ l.sessionCreated(event);
}
}
}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionHandlerTest.java
index 8f5201ab539..5be4b2d508a 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionHandlerTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionHandlerTest.java
@@ -22,9 +22,14 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import javax.servlet.SessionTrackingMode;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+import org.eclipse.jetty.server.Server;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class SessionHandlerTest
@@ -35,7 +40,64 @@ public class SessionHandlerTest
SessionHandler sessionHandler = new SessionHandler();
sessionHandler.setSessionTrackingModes(new HashSet<>(Arrays.asList(SessionTrackingMode.COOKIE, SessionTrackingMode.URL)));
sessionHandler.setSessionTrackingModes(Collections.singleton(SessionTrackingMode.SSL));
- assertThrows(IllegalArgumentException.class,() ->
- sessionHandler.setSessionTrackingModes(new HashSet<>(Arrays.asList(SessionTrackingMode.SSL, SessionTrackingMode.URL))));
+ assertThrows(IllegalArgumentException.class, () -> sessionHandler.setSessionTrackingModes(new HashSet<>(Arrays.asList(SessionTrackingMode.SSL, SessionTrackingMode.URL))));
+ }
+
+ @Test
+ public void testSessionListenerOrdering()
+ throws Exception
+ {
+ final StringBuffer result = new StringBuffer();
+
+ class Listener1 implements HttpSessionListener
+ {
+
+ @Override
+ public void sessionCreated(HttpSessionEvent se)
+ {
+ result.append("Listener1 create;");
+ }
+
+ @Override
+ public void sessionDestroyed(HttpSessionEvent se)
+ {
+ result.append("Listener1 destroy;");
+ }
+ }
+
+ class Listener2 implements HttpSessionListener
+ {
+
+ @Override
+ public void sessionCreated(HttpSessionEvent se)
+ {
+ result.append("Listener2 create;");
+ }
+
+ @Override
+ public void sessionDestroyed(HttpSessionEvent se)
+ {
+ result.append("Listener2 destroy;");
+ }
+
+ }
+
+ Server server = new Server();
+ SessionHandler sessionHandler = new SessionHandler();
+ try
+ {
+ sessionHandler.addEventListener(new Listener1());
+ sessionHandler.addEventListener(new Listener2());
+ sessionHandler.setServer(server);
+ sessionHandler.start();
+ Session session = new Session(sessionHandler, new SessionData("aa", "_", "0.0", 0, 0, 0, 0));
+ sessionHandler.callSessionCreatedListeners(session);
+ sessionHandler.callSessionDestroyedListeners(session);
+ assertEquals("Listener1 create;Listener2 create;Listener2 destroy;Listener1 destroy;", result.toString());
+ }
+ finally
+ {
+ sessionHandler.stop();
+ }
}
}
From 3981ec1342381ad75bc01886ece735f4144dbe50 Mon Sep 17 00:00:00 2001
From: Jan Bartel
Date: Wed, 8 Apr 2020 12:27:43 +0200
Subject: [PATCH 059/101] Issue #4758 Fix doc for
DefaultServlet.welcomeServlets
Signed-off-by: Jan Bartel
---
.../asciidoc/distribution-guide/extras/default-servlet.adoc | 2 +-
.../src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java | 2 +-
jetty-webapp/src/main/config/etc/webdefault.xml | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/jetty-documentation/src/main/asciidoc/distribution-guide/extras/default-servlet.adoc b/jetty-documentation/src/main/asciidoc/distribution-guide/extras/default-servlet.adoc
index b9ba8c5b7ee..856a19ca2d5 100644
--- a/jetty-documentation/src/main/asciidoc/distribution-guide/extras/default-servlet.adoc
+++ b/jetty-documentation/src/main/asciidoc/distribution-guide/extras/default-servlet.adoc
@@ -49,7 +49,7 @@ If `true`, welcome files are redirected rather that forwarded.
welcomeServlets::
If `true`, attempt to dispatch to welcome files that are servlets, but only after no matching static
resources could be found. If `false`, then a welcome file must exist on disk. If `exact`, then exact
-servlet matches are supported without an existing file. Default is `true`. This must be `false` if you want directory listings,
+servlet matches are supported without an existing file. Default is `false`. This must be `false` if you want directory listings,
but have index.jsp in your welcome file list.
precompressed::
If set to a comma separated list of encoding types (that may be listed in a requests Accept-Encoding header) to file extension mappings to look for and serve.
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java
index 000f84874d6..8db55db6ec0 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java
@@ -67,7 +67,7 @@ import org.slf4j.LoggerFactory;
* resources could be found. If false, then a welcome
* file must exist on disk. If "exact", then exact
* servlet matches are supported without an existing file.
- * Default is true.
+ * Default is false.
*
* This must be false if you want directory listings,
* but have index.jsp in your welcome file list.
diff --git a/jetty-webapp/src/main/config/etc/webdefault.xml b/jetty-webapp/src/main/config/etc/webdefault.xml
index 39a9efc4381..a52ecc2152b 100644
--- a/jetty-webapp/src/main/config/etc/webdefault.xml
+++ b/jetty-webapp/src/main/config/etc/webdefault.xml
@@ -82,7 +82,7 @@
* resources could be found. If false, then a welcome
* file must exist on disk. If "exact", then exact
* servlet matches are supported without an existing file.
- * Default is true.
+ * Default is false.
*
* This must be false if you want directory listings,
* but have index.jsp in your welcome file list.
From a640701d78f622c27aaccd98121901dc017e9d55 Mon Sep 17 00:00:00 2001
From: Michael Mayer
Date: Wed, 8 Apr 2020 15:36:52 +0200
Subject: [PATCH 060/101] Improve keystore exception message when keystore is
not valid (#4759)
Signed-off-by: Michael Mayer
---
.../java/org/eclipse/jetty/util/security/CertificateUtils.java | 2 +-
.../java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/security/CertificateUtils.java b/jetty-util/src/main/java/org/eclipse/jetty/util/security/CertificateUtils.java
index 49b55ec99da..d39d00ff2f6 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/security/CertificateUtils.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/security/CertificateUtils.java
@@ -47,7 +47,7 @@ public class CertificateUtils
}
if (!store.exists())
- throw new IllegalStateException("no valid keystore");
+ throw new IllegalStateException(store.getName() + " is not a valid keystore");
try (InputStream inStream = store.getInputStream())
{
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java
index 15d8228f427..f6270c1eace 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java
@@ -217,7 +217,7 @@ public class SslContextFactoryTest
cf.setTrustStorePath("/foo");
cf.start();
});
- assertThat(x.getMessage(), containsString("no valid keystore"));
+ assertThat(x.getMessage(), equalTo("/foo is not a valid keystore"));
}
}
From 9079fa63b3b4c852e9b62af9804c9a9daa4998e1 Mon Sep 17 00:00:00 2001
From: Michael Mayer
Date: Wed, 8 Apr 2020 15:36:52 +0200
Subject: [PATCH 061/101] Improve keystore exception message when keystore is
not valid (#4759)
Signed-off-by: Michael Mayer
---
.../java/org/eclipse/jetty/util/security/CertificateUtils.java | 2 +-
.../java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/security/CertificateUtils.java b/jetty-util/src/main/java/org/eclipse/jetty/util/security/CertificateUtils.java
index e5261cc3787..7a91878c80d 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/security/CertificateUtils.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/security/CertificateUtils.java
@@ -47,7 +47,7 @@ public class CertificateUtils
}
if (!store.exists())
- throw new IllegalStateException("no valid keystore");
+ throw new IllegalStateException(store.getName() + " is not a valid keystore");
try (InputStream inStream = store.getInputStream())
{
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java
index d676dcbb77e..edce0f218be 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java
@@ -244,7 +244,7 @@ public class SslContextFactoryTest
cf.setTrustStorePath("/foo");
cf.start();
});
- assertThat(x.getMessage(), containsString("no valid keystore"));
+ assertThat(x.getMessage(), equalTo("/foo is not a valid keystore"));
}
}
From bfc9738f8685f14a1868a2741dc0b9957ca8df63 Mon Sep 17 00:00:00 2001
From: Jan Bartel
Date: Wed, 8 Apr 2020 17:28:14 +0200
Subject: [PATCH 062/101] Issue #4760 Response.setLocale should override
previous (#4761)
Signed-off-by: Jan Bartel
---
.../eclipse/jetty/http/encoding.properties | 4 +-
.../org/eclipse/jetty/server/Response.java | 2 +-
.../eclipse/jetty/server/ResponseTest.java | 62 +++++++++++++++++++
3 files changed, 65 insertions(+), 3 deletions(-)
diff --git a/jetty-http/src/main/resources/org/eclipse/jetty/http/encoding.properties b/jetty-http/src/main/resources/org/eclipse/jetty/http/encoding.properties
index 3f2d2dd358e..9fbebc6191a 100644
--- a/jetty-http/src/main/resources/org/eclipse/jetty/http/encoding.properties
+++ b/jetty-http/src/main/resources/org/eclipse/jetty/http/encoding.properties
@@ -1,6 +1,6 @@
# Mapping of mime type to inferred or assumed charset
-# inferred charsets are used for encoding/decoding and explicitly set in associated metadata
-# assumed charsets are used for encoding/decoding, but are not set in associated metadata
+# inferred charsets are used for encoding/decoding and explicitly set in Content-Type
+# assumed charsets are used for encoding/decoding, but are not set in Content-Type
# In this file, assumed charsets are indicated with a leading '-'
text/html=utf-8
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
index 4a8eea246a9..c4441d9ff80 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
@@ -107,7 +107,7 @@ public class Response implements HttpServletResponse
NOT_SET, INFERRED, SET_LOCALE, SET_CONTENT_TYPE, SET_CHARACTER_ENCODING
}
- private static final EnumSet __localeOverride = EnumSet.of(EncodingFrom.NOT_SET, EncodingFrom.INFERRED);
+ private static final EnumSet __localeOverride = EnumSet.of(EncodingFrom.NOT_SET, EncodingFrom.INFERRED, EncodingFrom.SET_LOCALE);
private static final EnumSet __explicitCharset = EnumSet.of(EncodingFrom.SET_LOCALE, EncodingFrom.SET_CHARACTER_ENCODING);
public Response(HttpChannel channel, HttpOutput out)
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
index 273e28ad360..cb79e3f3923 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
@@ -53,6 +53,7 @@ import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.io.AbstractEndPoint;
import org.eclipse.jetty.io.ByteArrayEndPoint;
import org.eclipse.jetty.server.handler.AbstractHandler;
@@ -473,6 +474,67 @@ public class ResponseTest
response.getWriter();
assertThat("iso-8859-1", Matchers.equalTo(response.getCharacterEncoding()));
}
+
+ @Test
+ public void testLocaleAndContentTypeEncoding() throws Exception
+ {
+ _server.stop();
+ MimeTypes.getInferredEncodings().put("text/html", "iso-8859-1");
+ ContextHandler handler = new ContextHandler();
+ handler.addLocaleEncoding("ja", "euc-jp");
+ handler.addLocaleEncoding("zh_CN", "gb18030");
+ _server.setHandler(handler);
+ handler.setHandler(new DumpHandler());
+ _server.start();
+
+ Response response = getResponse();
+ response.getHttpChannel().getRequest().setContext(handler.getServletContext());
+
+ response.setContentType("text/html");
+ assertEquals("iso-8859-1", response.getCharacterEncoding());
+
+ // setLocale should change character encoding based on
+ // locale-encoding-mapping-list
+ response.setLocale(Locale.JAPAN);
+ assertEquals("euc-jp", response.getCharacterEncoding());
+
+ // setLocale should change character encoding based on
+ // locale-encoding-mapping-list
+ response.setLocale(Locale.CHINA);
+ assertEquals("gb18030", response.getCharacterEncoding());
+
+ // setContentType here doesn't define character encoding
+ response.setContentType("text/html");
+ assertEquals("gb18030", response.getCharacterEncoding());
+
+ // setCharacterEncoding should still be able to change encoding
+ response.setCharacterEncoding("utf-8");
+ assertEquals("utf-8", response.getCharacterEncoding());
+
+ // setLocale should not override explicit character encoding request
+ response.setLocale(Locale.JAPAN);
+ assertEquals("utf-8", response.getCharacterEncoding());
+
+ // setContentType should still be able to change encoding
+ response.setContentType("text/html;charset=gb18030");
+ assertEquals("gb18030", response.getCharacterEncoding());
+
+ // setCharacterEncoding should still be able to change encoding
+ response.setCharacterEncoding("utf-8");
+ assertEquals("utf-8", response.getCharacterEncoding());
+
+ // getWriter should freeze the character encoding
+ PrintWriter pw = response.getWriter();
+ assertEquals("utf-8", response.getCharacterEncoding());
+
+ // setCharacterEncoding should no longer be able to change the encoding
+ response.setCharacterEncoding("iso-8859-1");
+ assertEquals("utf-8", response.getCharacterEncoding());
+
+ // setLocale should not override explicit character encoding request
+ response.setLocale(Locale.JAPAN);
+ assertEquals("utf-8", response.getCharacterEncoding());
+ }
@Test
public void testContentTypeCharacterEncoding() throws Exception
From ab228fde9e55e9164c738d7fa121f8ac5acd51c9 Mon Sep 17 00:00:00 2001
From: Joakim Erdfelt
Date: Wed, 8 Apr 2020 12:33:56 -0500
Subject: [PATCH 063/101] Updating to version 9.4.28.v20200408
---
VERSION.txt | 40 +++++-
aggregates/jetty-all-compact3/pom.xml | 2 +-
aggregates/jetty-all/pom.xml | 2 +-
apache-jsp/pom.xml | 2 +-
apache-jstl/pom.xml | 2 +-
build-resources/pom.xml | 2 +-
examples/async-rest/async-rest-jar/pom.xml | 2 +-
examples/async-rest/async-rest-webapp/pom.xml | 2 +-
examples/async-rest/pom.xml | 2 +-
examples/embedded/pom.xml | 2 +-
examples/pom.xml | 2 +-
jetty-alpn/jetty-alpn-client/pom.xml | 2 +-
.../jetty-alpn-conscrypt-client/pom.xml | 2 +-
.../jetty-alpn-conscrypt-server/pom.xml | 2 +-
jetty-alpn/jetty-alpn-java-client/pom.xml | 2 +-
jetty-alpn/jetty-alpn-java-server/pom.xml | 2 +-
jetty-alpn/jetty-alpn-openjdk8-client/pom.xml | 2 +-
jetty-alpn/jetty-alpn-openjdk8-server/pom.xml | 2 +-
jetty-alpn/jetty-alpn-server/pom.xml | 2 +-
jetty-alpn/pom.xml | 2 +-
jetty-annotations/pom.xml | 2 +-
jetty-ant/pom.xml | 2 +-
jetty-bom/pom.xml | 134 +++++++++---------
jetty-cdi/pom.xml | 2 +-
jetty-client/pom.xml | 2 +-
jetty-continuation/pom.xml | 2 +-
jetty-deploy/pom.xml | 2 +-
jetty-distribution/pom.xml | 2 +-
jetty-documentation/pom.xml | 2 +-
jetty-fcgi/fcgi-client/pom.xml | 2 +-
jetty-fcgi/fcgi-server/pom.xml | 2 +-
jetty-fcgi/pom.xml | 2 +-
.../jetty-gcloud-session-manager/pom.xml | 2 +-
jetty-gcloud/pom.xml | 2 +-
jetty-hazelcast/pom.xml | 2 +-
jetty-home/pom.xml | 2 +-
jetty-http-spi/pom.xml | 2 +-
jetty-http/pom.xml | 2 +-
jetty-http2/http2-alpn-tests/pom.xml | 2 +-
jetty-http2/http2-client/pom.xml | 2 +-
jetty-http2/http2-common/pom.xml | 2 +-
jetty-http2/http2-hpack/pom.xml | 2 +-
.../http2-http-client-transport/pom.xml | 2 +-
jetty-http2/http2-server/pom.xml | 2 +-
jetty-http2/pom.xml | 2 +-
jetty-infinispan/infinispan-common/pom.xml | 2 +-
.../infinispan-embedded-query/pom.xml | 2 +-
jetty-infinispan/infinispan-embedded/pom.xml | 2 +-
.../infinispan-remote-query/pom.xml | 2 +-
jetty-infinispan/infinispan-remote/pom.xml | 2 +-
jetty-infinispan/pom.xml | 2 +-
jetty-io/pom.xml | 2 +-
jetty-jaas/pom.xml | 2 +-
jetty-jaspi/pom.xml | 2 +-
jetty-jmh/pom.xml | 2 +-
jetty-jmx/pom.xml | 2 +-
jetty-jndi/pom.xml | 2 +-
jetty-jspc-maven-plugin/pom.xml | 2 +-
jetty-maven-plugin/pom.xml | 2 +-
.../jetty-memcached-sessions/pom.xml | 2 +-
jetty-memcached/pom.xml | 2 +-
jetty-nosql/pom.xml | 2 +-
jetty-openid/pom.xml | 2 +-
jetty-osgi/jetty-osgi-alpn/pom.xml | 2 +-
jetty-osgi/jetty-osgi-boot-jsp/pom.xml | 2 +-
jetty-osgi/jetty-osgi-boot-warurl/pom.xml | 2 +-
jetty-osgi/jetty-osgi-boot/pom.xml | 2 +-
jetty-osgi/jetty-osgi-httpservice/pom.xml | 2 +-
jetty-osgi/pom.xml | 2 +-
jetty-osgi/test-jetty-osgi-context/pom.xml | 2 +-
jetty-osgi/test-jetty-osgi-fragment/pom.xml | 2 +-
jetty-osgi/test-jetty-osgi-server/pom.xml | 2 +-
jetty-osgi/test-jetty-osgi-webapp/pom.xml | 2 +-
jetty-osgi/test-jetty-osgi/pom.xml | 2 +-
jetty-plus/pom.xml | 2 +-
jetty-proxy/pom.xml | 2 +-
jetty-quickstart/pom.xml | 2 +-
jetty-rewrite/pom.xml | 2 +-
jetty-runner/pom.xml | 2 +-
jetty-security/pom.xml | 2 +-
jetty-server/pom.xml | 2 +-
jetty-servlet/pom.xml | 2 +-
jetty-servlets/pom.xml | 2 +-
jetty-spring/pom.xml | 2 +-
jetty-start/pom.xml | 2 +-
jetty-unixsocket/pom.xml | 2 +-
jetty-util-ajax/pom.xml | 2 +-
jetty-util/pom.xml | 2 +-
jetty-webapp/pom.xml | 2 +-
.../javax-websocket-client-impl/pom.xml | 2 +-
.../javax-websocket-server-impl/pom.xml | 2 +-
jetty-websocket/jetty-websocket-tests/pom.xml | 2 +-
jetty-websocket/pom.xml | 2 +-
jetty-websocket/websocket-api/pom.xml | 2 +-
jetty-websocket/websocket-client/pom.xml | 2 +-
jetty-websocket/websocket-common/pom.xml | 2 +-
jetty-websocket/websocket-server/pom.xml | 2 +-
jetty-websocket/websocket-servlet/pom.xml | 2 +-
jetty-xml/pom.xml | 2 +-
pom.xml | 2 +-
tests/pom.xml | 2 +-
tests/test-continuation/pom.xml | 2 +-
tests/test-distribution/pom.xml | 2 +-
tests/test-http-client-transport/pom.xml | 2 +-
tests/test-integration/pom.xml | 2 +-
tests/test-jmx/jmx-webapp-it/pom.xml | 2 +-
tests/test-jmx/jmx-webapp/pom.xml | 2 +-
tests/test-jmx/pom.xml | 2 +-
tests/test-loginservice/pom.xml | 2 +-
tests/test-quickstart/pom.xml | 2 +-
tests/test-sessions/pom.xml | 2 +-
.../test-sessions/test-file-sessions/pom.xml | 2 +-
.../test-gcloud-sessions/pom.xml | 2 +-
.../test-hazelcast-sessions/pom.xml | 2 +-
.../test-infinispan-sessions/pom.xml | 2 +-
.../test-sessions/test-jdbc-sessions/pom.xml | 2 +-
.../test-memcached-sessions/pom.xml | 2 +-
.../test-mongodb-sessions/pom.xml | 2 +-
.../test-sessions-common/pom.xml | 2 +-
tests/test-webapps/pom.xml | 2 +-
.../test-cdi-common-webapp/pom.xml | 2 +-
tests/test-webapps/test-felix-webapp/pom.xml | 2 +-
tests/test-webapps/test-http2-webapp/pom.xml | 2 +-
tests/test-webapps/test-jaas-webapp/pom.xml | 2 +-
tests/test-webapps/test-jetty-webapp/pom.xml | 2 +-
tests/test-webapps/test-jndi-webapp/pom.xml | 2 +-
.../test-webapps/test-mock-resources/pom.xml | 2 +-
.../test-webapps/test-owb-cdi-webapp/pom.xml | 2 +-
tests/test-webapps/test-proxy-webapp/pom.xml | 2 +-
tests/test-webapps/test-servlet-spec/pom.xml | 2 +-
.../test-container-initializer/pom.xml | 2 +-
.../test-spec-webapp/pom.xml | 2 +-
.../test-web-fragment/pom.xml | 2 +-
tests/test-webapps/test-simple-webapp/pom.xml | 2 +-
.../test-webapps/test-webapp-rfc2616/pom.xml | 2 +-
.../test-webapps/test-weld-cdi-webapp/pom.xml | 2 +-
136 files changed, 240 insertions(+), 202 deletions(-)
diff --git a/VERSION.txt b/VERSION.txt
index ccae5569bb9..a18b2cc03c5 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1,4 +1,42 @@
-jetty-9.4.28-SNAPSHOT
+jetty-9.4.28.v20200408 - 08 April 2020
+ + 847 Setting async timeout on WebSocketClient does not seem to timeout writes
+ + 2896 Wrong Certificate Selected When Using Multiple Virtual Host Names in
+ Conscrypt
+ + 4443 Track backport of ALPN APIs to Java 8
+ + 4529 ErrorHandler showing servlet info, can not be disabled unless
+ overriding most of its functionality
+ + 4542 servlet context root mapping incorrect
+ + 4619 Inconsistent library versions notice.
+ + 4620 Using console-capture with StdErrLog results in empty log file
+ + 4621 jetty-jaspi in jetty-all uber aggregate artifact requires
+ javax.security.auth.message.AuthException which cannot be included
+ + 4628 Add support for conditional module dependencies in jetty-start
+ + 4631 Startup XmlConfiguration WARN on Arg threadpool
+ + 4638 maxFormContentSize fix in Issue #3856 broke JenkinsCI/Winstone
+ + 4644 no injection of env-entry if env-entry-value is whitespace only or
+ missing
+ + 4645 Empty "X-Forwarded-Port" header results in NumberFormatException
+ + 4647 Hazelcast remote.xml configuration file do not configure hazelcast
+ remote addresses
+ + 4650 Do not use ServiceLoader every time a WebSocketSession is created
+ + 4654 Hazelcast configurationLocation is not configurable via mod files
+ + 4662 Jetty 9.4.x calls ServletContextListener.contextDestroyed() too early
+ + 4671 CustomRequestLog throws NullPointerException when no request cookie is
+ present
+ + 4673 Short reads break form-data multipart parsing
+ + 4676 ALPN support for Java 15
+ + 4682 "UnreadableSessionDataException Unreadable session ..." after upgrading
+ to 9.4.27
+ + 4693 Version 9.4.25 breaks binary compatibility by renaming
+ Response.closeOutput()
+ + 4699 ServletContainerInitializer.onStartUp is not called with maven jar
+ packaging using Jetty Maven Plugin
+ + 4711 Reset trailers on recycled response
+ + 4714 Low setMaxConcurrentStreams causes "1/unexpected_data_frame" errors
+ + 4735 Get env variables in PHP scripts served through FastCGIProxyServlet
+ + 4737 PreDestroy not called for non-async and run-as servlets
+ + 4739 @RunAs not honoured on servlets
+ + 4751 Refresh NetworkTraffic* classes
jetty-9.4.27.v20200227 - 27 February 2020
+ 3247 Generate jetty-maven-plugin website
diff --git a/aggregates/jetty-all-compact3/pom.xml b/aggregates/jetty-all-compact3/pom.xml
index aaeda8838b1..1eabf07ded7 100644
--- a/aggregates/jetty-all-compact3/pom.xml
+++ b/aggregates/jetty-all-compact3/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408../../pom.xml4.0.0
diff --git a/aggregates/jetty-all/pom.xml b/aggregates/jetty-all/pom.xml
index 7bf293ff067..b0a64b4e124 100644
--- a/aggregates/jetty-all/pom.xml
+++ b/aggregates/jetty-all/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408../../pom.xml4.0.0
diff --git a/apache-jsp/pom.xml b/apache-jsp/pom.xml
index 9a7ea5ed3ff..3cfcb4f2b95 100644
--- a/apache-jsp/pom.xml
+++ b/apache-jsp/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0apache-jsp
diff --git a/apache-jstl/pom.xml b/apache-jstl/pom.xml
index eeff5bc5e5a..60209ea0f8e 100644
--- a/apache-jstl/pom.xml
+++ b/apache-jstl/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0apache-jstl
diff --git a/build-resources/pom.xml b/build-resources/pom.xml
index fc25f203f41..23b8184c142 100644
--- a/build-resources/pom.xml
+++ b/build-resources/pom.xml
@@ -2,7 +2,7 @@
4.0.0org.eclipse.jettybuild-resources
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408jarJetty :: Build Resources
diff --git a/examples/async-rest/async-rest-jar/pom.xml b/examples/async-rest/async-rest-jar/pom.xml
index 10e592c3080..944c5cb2a8b 100644
--- a/examples/async-rest/async-rest-jar/pom.xml
+++ b/examples/async-rest/async-rest-jar/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyexample-async-rest
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/examples/async-rest/async-rest-webapp/pom.xml b/examples/async-rest/async-rest-webapp/pom.xml
index 2bd6113930e..9d8a5820245 100644
--- a/examples/async-rest/async-rest-webapp/pom.xml
+++ b/examples/async-rest/async-rest-webapp/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyexample-async-rest
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/examples/async-rest/pom.xml b/examples/async-rest/pom.xml
index 14b9fc444c6..dad7888ce4e 100644
--- a/examples/async-rest/pom.xml
+++ b/examples/async-rest/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.examplesexamples-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/examples/embedded/pom.xml b/examples/embedded/pom.xml
index 25fa3bf60c9..393ec89f1b1 100644
--- a/examples/embedded/pom.xml
+++ b/examples/embedded/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.examplesexamples-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408../pom.xml4.0.0
diff --git a/examples/pom.xml b/examples/pom.xml
index e0c9fbba50b..fab43bf94f1 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-alpn/jetty-alpn-client/pom.xml b/jetty-alpn/jetty-alpn-client/pom.xml
index 2b56cc739c0..fc655e73f86 100644
--- a/jetty-alpn/jetty-alpn-client/pom.xml
+++ b/jetty-alpn/jetty-alpn-client/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-alpn-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-alpn-client
diff --git a/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml b/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml
index 915d9177dfc..173d2bbc3e2 100644
--- a/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml
+++ b/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jettyjetty-alpn-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml b/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml
index 1e1d9d24680..e5d8910158f 100644
--- a/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml
+++ b/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jettyjetty-alpn-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-alpn/jetty-alpn-java-client/pom.xml b/jetty-alpn/jetty-alpn-java-client/pom.xml
index cd38c4a9069..af355a8f4f5 100644
--- a/jetty-alpn/jetty-alpn-java-client/pom.xml
+++ b/jetty-alpn/jetty-alpn-java-client/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jettyjetty-alpn-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-alpn/jetty-alpn-java-server/pom.xml b/jetty-alpn/jetty-alpn-java-server/pom.xml
index 5582dccfa56..2406fde0c73 100644
--- a/jetty-alpn/jetty-alpn-java-server/pom.xml
+++ b/jetty-alpn/jetty-alpn-java-server/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jettyjetty-alpn-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-alpn/jetty-alpn-openjdk8-client/pom.xml b/jetty-alpn/jetty-alpn-openjdk8-client/pom.xml
index a76767a2f0a..d43d013b807 100644
--- a/jetty-alpn/jetty-alpn-openjdk8-client/pom.xml
+++ b/jetty-alpn/jetty-alpn-openjdk8-client/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jettyjetty-alpn-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-alpn/jetty-alpn-openjdk8-server/pom.xml b/jetty-alpn/jetty-alpn-openjdk8-server/pom.xml
index fa8fc721763..406b540a343 100644
--- a/jetty-alpn/jetty-alpn-openjdk8-server/pom.xml
+++ b/jetty-alpn/jetty-alpn-openjdk8-server/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jettyjetty-alpn-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-alpn/jetty-alpn-server/pom.xml b/jetty-alpn/jetty-alpn-server/pom.xml
index 78ce0638f5a..172f0247238 100644
--- a/jetty-alpn/jetty-alpn-server/pom.xml
+++ b/jetty-alpn/jetty-alpn-server/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-alpn-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-alpn-server
diff --git a/jetty-alpn/pom.xml b/jetty-alpn/pom.xml
index ea355904386..a0fd982dce6 100644
--- a/jetty-alpn/pom.xml
+++ b/jetty-alpn/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-alpn-parent
diff --git a/jetty-annotations/pom.xml b/jetty-annotations/pom.xml
index d282ba92614..1fff4ec4d88 100644
--- a/jetty-annotations/pom.xml
+++ b/jetty-annotations/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-annotations
diff --git a/jetty-ant/pom.xml b/jetty-ant/pom.xml
index b6cbf070fd2..f67827cf3b3 100644
--- a/jetty-ant/pom.xml
+++ b/jetty-ant/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-ant
diff --git a/jetty-bom/pom.xml b/jetty-bom/pom.xml
index 88c71a8ee6f..fb246fb98b0 100644
--- a/jetty-bom/pom.xml
+++ b/jetty-bom/pom.xml
@@ -9,7 +9,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408
@@ -53,336 +53,336 @@
org.eclipse.jettyapache-jsp
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyapache-jstl
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-alpn-client
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-alpn-java-client
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-alpn-java-server
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-alpn-openjdk8-client
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-alpn-openjdk8-server
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-alpn-conscrypt-client
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-alpn-conscrypt-server
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-alpn-server
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-annotations
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-ant
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-client
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-continuation
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-deploy
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-distribution
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408ziporg.eclipse.jettyjetty-distribution
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408tar.gzorg.eclipse.jetty.fcgifcgi-client
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.fcgifcgi-server
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.gcloudjetty-gcloud-session-manager
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-home
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408ziporg.eclipse.jettyjetty-home
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408tar.gzorg.eclipse.jettyjetty-http
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.http2http2-client
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.http2http2-common
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.http2http2-hpack
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.http2http2-http-client-transport
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.http2http2-server
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-http-spi
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyinfinispan-common
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyinfinispan-remote-query
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyinfinispan-embedded-query
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-hazelcast
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-io
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-jaas
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-jaspi
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-jmx
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-jndi
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.memcachedjetty-memcached-sessions
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-nosql
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.osgijetty-osgi-boot
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.osgijetty-osgi-boot-jsp
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.osgijetty-osgi-boot-warurl
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.osgijetty-httpservice
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-plus
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-proxy
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-quickstart
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-rewrite
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-security
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-openid
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-server
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-servlet
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-servlets
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-spring
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-unixsocket
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-util
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-util-ajax
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-webapp
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.websocketjavax-websocket-client-impl
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.websocketjavax-websocket-server-impl
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.websocketwebsocket-api
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.websocketwebsocket-client
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.websocketwebsocket-common
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.websocketwebsocket-server
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jetty.websocketwebsocket-servlet
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408org.eclipse.jettyjetty-xml
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408
diff --git a/jetty-cdi/pom.xml b/jetty-cdi/pom.xml
index 709ee31fbe7..22b8255f18b 100644
--- a/jetty-cdi/pom.xml
+++ b/jetty-cdi/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0org.eclipse.jetty
diff --git a/jetty-client/pom.xml b/jetty-client/pom.xml
index 9c1e35bf6db..cdd49444081 100644
--- a/jetty-client/pom.xml
+++ b/jetty-client/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-continuation/pom.xml b/jetty-continuation/pom.xml
index 9b3e9f76b70..b8f0f187835 100644
--- a/jetty-continuation/pom.xml
+++ b/jetty-continuation/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-continuation
diff --git a/jetty-deploy/pom.xml b/jetty-deploy/pom.xml
index ff231159a36..417d7d953dc 100644
--- a/jetty-deploy/pom.xml
+++ b/jetty-deploy/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-deploy
diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml
index d8bc99f5c1d..3f003be87bd 100644
--- a/jetty-distribution/pom.xml
+++ b/jetty-distribution/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-distribution
diff --git a/jetty-documentation/pom.xml b/jetty-documentation/pom.xml
index ab0c8281a00..119e62f4662 100644
--- a/jetty-documentation/pom.xml
+++ b/jetty-documentation/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408jetty-documentationJetty :: Documentation
diff --git a/jetty-fcgi/fcgi-client/pom.xml b/jetty-fcgi/fcgi-client/pom.xml
index c49c236bd1c..06756f19b0a 100644
--- a/jetty-fcgi/fcgi-client/pom.xml
+++ b/jetty-fcgi/fcgi-client/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.fcgifcgi-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-fcgi/fcgi-server/pom.xml b/jetty-fcgi/fcgi-server/pom.xml
index e7df5968c92..221312a6899 100644
--- a/jetty-fcgi/fcgi-server/pom.xml
+++ b/jetty-fcgi/fcgi-server/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.fcgifcgi-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-fcgi/pom.xml b/jetty-fcgi/pom.xml
index c3d93c657ac..e2f1b186d8d 100644
--- a/jetty-fcgi/pom.xml
+++ b/jetty-fcgi/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-gcloud/jetty-gcloud-session-manager/pom.xml b/jetty-gcloud/jetty-gcloud-session-manager/pom.xml
index 965b2b3de39..464abb4ebf5 100644
--- a/jetty-gcloud/jetty-gcloud-session-manager/pom.xml
+++ b/jetty-gcloud/jetty-gcloud-session-manager/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.gcloudgcloud-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-gcloud/pom.xml b/jetty-gcloud/pom.xml
index e982ff2e620..273544d929d 100644
--- a/jetty-gcloud/pom.xml
+++ b/jetty-gcloud/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-hazelcast/pom.xml b/jetty-hazelcast/pom.xml
index f32f3a9bec2..a5ae15d32df 100644
--- a/jetty-hazelcast/pom.xml
+++ b/jetty-hazelcast/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-home/pom.xml b/jetty-home/pom.xml
index 7cc9e6cf16b..1932babcfd2 100644
--- a/jetty-home/pom.xml
+++ b/jetty-home/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-home
diff --git a/jetty-http-spi/pom.xml b/jetty-http-spi/pom.xml
index ec89e2470da..ca132b03a41 100644
--- a/jetty-http-spi/pom.xml
+++ b/jetty-http-spi/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-http-spi
diff --git a/jetty-http/pom.xml b/jetty-http/pom.xml
index c3f0c4b9e90..b6edfd2bece 100644
--- a/jetty-http/pom.xml
+++ b/jetty-http/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-http
diff --git a/jetty-http2/http2-alpn-tests/pom.xml b/jetty-http2/http2-alpn-tests/pom.xml
index 0eb37365467..62491f58b28 100644
--- a/jetty-http2/http2-alpn-tests/pom.xml
+++ b/jetty-http2/http2-alpn-tests/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.http2http2-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-http2/http2-client/pom.xml b/jetty-http2/http2-client/pom.xml
index afdbb0205b4..55c6e48265b 100644
--- a/jetty-http2/http2-client/pom.xml
+++ b/jetty-http2/http2-client/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.http2http2-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-http2/http2-common/pom.xml b/jetty-http2/http2-common/pom.xml
index 9d704efefd1..195767af9bd 100644
--- a/jetty-http2/http2-common/pom.xml
+++ b/jetty-http2/http2-common/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.http2http2-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-http2/http2-hpack/pom.xml b/jetty-http2/http2-hpack/pom.xml
index d03adb5253c..159b8296c63 100644
--- a/jetty-http2/http2-hpack/pom.xml
+++ b/jetty-http2/http2-hpack/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.http2http2-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-http2/http2-http-client-transport/pom.xml b/jetty-http2/http2-http-client-transport/pom.xml
index 6381d1269ce..efc7ff381f0 100644
--- a/jetty-http2/http2-http-client-transport/pom.xml
+++ b/jetty-http2/http2-http-client-transport/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.http2http2-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-http2/http2-server/pom.xml b/jetty-http2/http2-server/pom.xml
index c788e2fa413..a882ab53477 100644
--- a/jetty-http2/http2-server/pom.xml
+++ b/jetty-http2/http2-server/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.http2http2-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-http2/pom.xml b/jetty-http2/pom.xml
index 4139a840928..b0bed74236d 100644
--- a/jetty-http2/pom.xml
+++ b/jetty-http2/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-infinispan/infinispan-common/pom.xml b/jetty-infinispan/infinispan-common/pom.xml
index 1c5d45c0ece..8179109783f 100644
--- a/jetty-infinispan/infinispan-common/pom.xml
+++ b/jetty-infinispan/infinispan-common/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyinfinispan-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0infinispan-common
diff --git a/jetty-infinispan/infinispan-embedded-query/pom.xml b/jetty-infinispan/infinispan-embedded-query/pom.xml
index 4540ddf1e96..5a2247ca7d2 100644
--- a/jetty-infinispan/infinispan-embedded-query/pom.xml
+++ b/jetty-infinispan/infinispan-embedded-query/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyinfinispan-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0infinispan-embedded-query
diff --git a/jetty-infinispan/infinispan-embedded/pom.xml b/jetty-infinispan/infinispan-embedded/pom.xml
index 2e4384157c1..29e41e4c692 100644
--- a/jetty-infinispan/infinispan-embedded/pom.xml
+++ b/jetty-infinispan/infinispan-embedded/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyinfinispan-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0infinispan-embedded
diff --git a/jetty-infinispan/infinispan-remote-query/pom.xml b/jetty-infinispan/infinispan-remote-query/pom.xml
index 210e7fe0ccc..d49d6822640 100644
--- a/jetty-infinispan/infinispan-remote-query/pom.xml
+++ b/jetty-infinispan/infinispan-remote-query/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyinfinispan-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0infinispan-remote-query
diff --git a/jetty-infinispan/infinispan-remote/pom.xml b/jetty-infinispan/infinispan-remote/pom.xml
index e0d6487d748..c21b228bf94 100644
--- a/jetty-infinispan/infinispan-remote/pom.xml
+++ b/jetty-infinispan/infinispan-remote/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyinfinispan-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0infinispan-remote
diff --git a/jetty-infinispan/pom.xml b/jetty-infinispan/pom.xml
index 0c3f15357fe..026bb5d9d57 100644
--- a/jetty-infinispan/pom.xml
+++ b/jetty-infinispan/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-io/pom.xml b/jetty-io/pom.xml
index 8e8a813ed8f..605a8aaa02e 100644
--- a/jetty-io/pom.xml
+++ b/jetty-io/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-io
diff --git a/jetty-jaas/pom.xml b/jetty-jaas/pom.xml
index 5909961fae1..174444c0eb9 100644
--- a/jetty-jaas/pom.xml
+++ b/jetty-jaas/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-jaas
diff --git a/jetty-jaspi/pom.xml b/jetty-jaspi/pom.xml
index e3e29a8a3a8..d07dd54c914 100644
--- a/jetty-jaspi/pom.xml
+++ b/jetty-jaspi/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-jmh/pom.xml b/jetty-jmh/pom.xml
index c777ddbf14e..f6febdf1185 100644
--- a/jetty-jmh/pom.xml
+++ b/jetty-jmh/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-jmx/pom.xml b/jetty-jmx/pom.xml
index 608708778ee..d1f4fb8fe72 100644
--- a/jetty-jmx/pom.xml
+++ b/jetty-jmx/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-jmx
diff --git a/jetty-jndi/pom.xml b/jetty-jndi/pom.xml
index cda6724747a..4a362722962 100644
--- a/jetty-jndi/pom.xml
+++ b/jetty-jndi/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-jndi
diff --git a/jetty-jspc-maven-plugin/pom.xml b/jetty-jspc-maven-plugin/pom.xml
index 568252f1b52..e6a033ad8fb 100644
--- a/jetty-jspc-maven-plugin/pom.xml
+++ b/jetty-jspc-maven-plugin/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-jspc-maven-plugin
diff --git a/jetty-maven-plugin/pom.xml b/jetty-maven-plugin/pom.xml
index 4342c4b1e7f..623c6db72b1 100644
--- a/jetty-maven-plugin/pom.xml
+++ b/jetty-maven-plugin/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-maven-plugin
diff --git a/jetty-memcached/jetty-memcached-sessions/pom.xml b/jetty-memcached/jetty-memcached-sessions/pom.xml
index 872aa42e681..3e3fb5b4af0 100644
--- a/jetty-memcached/jetty-memcached-sessions/pom.xml
+++ b/jetty-memcached/jetty-memcached-sessions/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.memcachedmemcached-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-memcached/pom.xml b/jetty-memcached/pom.xml
index 4fd544ac7b8..ef1ea741272 100644
--- a/jetty-memcached/pom.xml
+++ b/jetty-memcached/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-nosql/pom.xml b/jetty-nosql/pom.xml
index 6bc6479a319..9f5baa72093 100644
--- a/jetty-nosql/pom.xml
+++ b/jetty-nosql/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-nosql
diff --git a/jetty-openid/pom.xml b/jetty-openid/pom.xml
index 2b6dc89741a..62e4120046d 100644
--- a/jetty-openid/pom.xml
+++ b/jetty-openid/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-osgi/jetty-osgi-alpn/pom.xml b/jetty-osgi/jetty-osgi-alpn/pom.xml
index 7045631aae3..963027b94ec 100644
--- a/jetty-osgi/jetty-osgi-alpn/pom.xml
+++ b/jetty-osgi/jetty-osgi-alpn/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-osgi-alpn
diff --git a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
index 2c8f933f206..d1958d9f06d 100644
--- a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-osgi-boot-jsp
diff --git a/jetty-osgi/jetty-osgi-boot-warurl/pom.xml b/jetty-osgi/jetty-osgi-boot-warurl/pom.xml
index 1850bb462df..336ed587621 100644
--- a/jetty-osgi/jetty-osgi-boot-warurl/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot-warurl/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408../pom.xml4.0.0
diff --git a/jetty-osgi/jetty-osgi-boot/pom.xml b/jetty-osgi/jetty-osgi-boot/pom.xml
index 985f89c780b..9fef576bfb6 100644
--- a/jetty-osgi/jetty-osgi-boot/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-osgi-boot
diff --git a/jetty-osgi/jetty-osgi-httpservice/pom.xml b/jetty-osgi/jetty-osgi-httpservice/pom.xml
index 492fc79154e..15847b7db59 100644
--- a/jetty-osgi/jetty-osgi-httpservice/pom.xml
+++ b/jetty-osgi/jetty-osgi-httpservice/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-httpservice
diff --git a/jetty-osgi/pom.xml b/jetty-osgi/pom.xml
index f1d974b0873..cd3e4ae41f8 100644
--- a/jetty-osgi/pom.xml
+++ b/jetty-osgi/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-osgi/test-jetty-osgi-context/pom.xml b/jetty-osgi/test-jetty-osgi-context/pom.xml
index 9424cefe540..63496271666 100644
--- a/jetty-osgi/test-jetty-osgi-context/pom.xml
+++ b/jetty-osgi/test-jetty-osgi-context/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0test-jetty-osgi-context
diff --git a/jetty-osgi/test-jetty-osgi-fragment/pom.xml b/jetty-osgi/test-jetty-osgi-fragment/pom.xml
index 08231073e44..16ce2bb7979 100644
--- a/jetty-osgi/test-jetty-osgi-fragment/pom.xml
+++ b/jetty-osgi/test-jetty-osgi-fragment/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408../pom.xml4.0.0
diff --git a/jetty-osgi/test-jetty-osgi-server/pom.xml b/jetty-osgi/test-jetty-osgi-server/pom.xml
index cc75d28878d..9f8d8b6c455 100644
--- a/jetty-osgi/test-jetty-osgi-server/pom.xml
+++ b/jetty-osgi/test-jetty-osgi-server/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0test-jetty-osgi-server
diff --git a/jetty-osgi/test-jetty-osgi-webapp/pom.xml b/jetty-osgi/test-jetty-osgi-webapp/pom.xml
index 1d7f3386da3..685531fe2ca 100644
--- a/jetty-osgi/test-jetty-osgi-webapp/pom.xml
+++ b/jetty-osgi/test-jetty-osgi-webapp/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408../pom.xml4.0.0
diff --git a/jetty-osgi/test-jetty-osgi/pom.xml b/jetty-osgi/test-jetty-osgi/pom.xml
index 600946d2241..a2bb0c47e2b 100644
--- a/jetty-osgi/test-jetty-osgi/pom.xml
+++ b/jetty-osgi/test-jetty-osgi/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408../pom.xml4.0.0
diff --git a/jetty-plus/pom.xml b/jetty-plus/pom.xml
index 25245b0ff34..83895ad6219 100644
--- a/jetty-plus/pom.xml
+++ b/jetty-plus/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-plus
diff --git a/jetty-proxy/pom.xml b/jetty-proxy/pom.xml
index 5a652934487..3bb5e9aed93 100644
--- a/jetty-proxy/pom.xml
+++ b/jetty-proxy/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-proxy
diff --git a/jetty-quickstart/pom.xml b/jetty-quickstart/pom.xml
index 8fd5acee465..ef81c7d9f50 100644
--- a/jetty-quickstart/pom.xml
+++ b/jetty-quickstart/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0org.eclipse.jetty
diff --git a/jetty-rewrite/pom.xml b/jetty-rewrite/pom.xml
index 2f0a67774bc..e48c3db8c42 100644
--- a/jetty-rewrite/pom.xml
+++ b/jetty-rewrite/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-rewrite
diff --git a/jetty-runner/pom.xml b/jetty-runner/pom.xml
index 383d42c8c9a..1e218b4ed60 100644
--- a/jetty-runner/pom.xml
+++ b/jetty-runner/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-runner
diff --git a/jetty-security/pom.xml b/jetty-security/pom.xml
index fe03a87ab3f..b5a7ec859b6 100644
--- a/jetty-security/pom.xml
+++ b/jetty-security/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-security
diff --git a/jetty-server/pom.xml b/jetty-server/pom.xml
index e33b32679d5..e1b8513a5be 100644
--- a/jetty-server/pom.xml
+++ b/jetty-server/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-server
diff --git a/jetty-servlet/pom.xml b/jetty-servlet/pom.xml
index a29402ed6fa..ba3d42d4139 100644
--- a/jetty-servlet/pom.xml
+++ b/jetty-servlet/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-servlet
diff --git a/jetty-servlets/pom.xml b/jetty-servlets/pom.xml
index 0f44cad59ea..0cd87fe7d60 100644
--- a/jetty-servlets/pom.xml
+++ b/jetty-servlets/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-servlets
diff --git a/jetty-spring/pom.xml b/jetty-spring/pom.xml
index 8f962377568..8a73d6ab249 100644
--- a/jetty-spring/pom.xml
+++ b/jetty-spring/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-spring
diff --git a/jetty-start/pom.xml b/jetty-start/pom.xml
index 768b7b02cff..21c8aa5f615 100644
--- a/jetty-start/pom.xml
+++ b/jetty-start/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-start
diff --git a/jetty-unixsocket/pom.xml b/jetty-unixsocket/pom.xml
index 90391db6849..fe6dfe32879 100644
--- a/jetty-unixsocket/pom.xml
+++ b/jetty-unixsocket/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-unixsocket
diff --git a/jetty-util-ajax/pom.xml b/jetty-util-ajax/pom.xml
index bc40146a46a..50009f5ea60 100644
--- a/jetty-util-ajax/pom.xml
+++ b/jetty-util-ajax/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-util-ajax
diff --git a/jetty-util/pom.xml b/jetty-util/pom.xml
index 00837fc5a1b..455f784102e 100644
--- a/jetty-util/pom.xml
+++ b/jetty-util/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-util
diff --git a/jetty-webapp/pom.xml b/jetty-webapp/pom.xml
index 039c9dffad6..194abb28d41 100644
--- a/jetty-webapp/pom.xml
+++ b/jetty-webapp/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-webapp
diff --git a/jetty-websocket/javax-websocket-client-impl/pom.xml b/jetty-websocket/javax-websocket-client-impl/pom.xml
index fbdff4fcbe2..693d47beabf 100644
--- a/jetty-websocket/javax-websocket-client-impl/pom.xml
+++ b/jetty-websocket/javax-websocket-client-impl/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.websocketwebsocket-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-websocket/javax-websocket-server-impl/pom.xml b/jetty-websocket/javax-websocket-server-impl/pom.xml
index 7ab40c2b1d3..0e0c0a0b318 100644
--- a/jetty-websocket/javax-websocket-server-impl/pom.xml
+++ b/jetty-websocket/javax-websocket-server-impl/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.websocketwebsocket-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-websocket/jetty-websocket-tests/pom.xml b/jetty-websocket/jetty-websocket-tests/pom.xml
index 6e6598bb51b..aecb624ee35 100644
--- a/jetty-websocket/jetty-websocket-tests/pom.xml
+++ b/jetty-websocket/jetty-websocket-tests/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.websocketwebsocket-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-websocket/pom.xml b/jetty-websocket/pom.xml
index c29ebedf926..9c0f8af898f 100644
--- a/jetty-websocket/pom.xml
+++ b/jetty-websocket/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-websocket/websocket-api/pom.xml b/jetty-websocket/websocket-api/pom.xml
index 9eec5d56611..929ecae0165 100644
--- a/jetty-websocket/websocket-api/pom.xml
+++ b/jetty-websocket/websocket-api/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.websocketwebsocket-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-websocket/websocket-client/pom.xml b/jetty-websocket/websocket-client/pom.xml
index 19042689ad8..ca624c66825 100644
--- a/jetty-websocket/websocket-client/pom.xml
+++ b/jetty-websocket/websocket-client/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.websocketwebsocket-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-websocket/websocket-common/pom.xml b/jetty-websocket/websocket-common/pom.xml
index f97d68617fe..f0ed7a19eb0 100644
--- a/jetty-websocket/websocket-common/pom.xml
+++ b/jetty-websocket/websocket-common/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.websocketwebsocket-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-websocket/websocket-server/pom.xml b/jetty-websocket/websocket-server/pom.xml
index afd8c2f53ec..4ea0c98a90f 100644
--- a/jetty-websocket/websocket-server/pom.xml
+++ b/jetty-websocket/websocket-server/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.websocketwebsocket-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-websocket/websocket-servlet/pom.xml b/jetty-websocket/websocket-servlet/pom.xml
index 875cd4f7820..fe62e9468a6 100644
--- a/jetty-websocket/websocket-servlet/pom.xml
+++ b/jetty-websocket/websocket-servlet/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.websocketwebsocket-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/jetty-xml/pom.xml b/jetty-xml/pom.xml
index 4af2979745b..f27aefed9a0 100644
--- a/jetty-xml/pom.xml
+++ b/jetty-xml/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jetty-xml
diff --git a/pom.xml b/pom.xml
index f148d8d433a..c73480e9fc1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408Jetty :: ProjectThe Eclipse Jetty Projectpom
diff --git a/tests/pom.xml b/tests/pom.xml
index 502c070c810..860d9ad1ca8 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jettyjetty-project
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408../pom.xmlorg.eclipse.jetty.tests
diff --git a/tests/test-continuation/pom.xml b/tests/test-continuation/pom.xml
index 500de02b4b5..6df822f6b45 100644
--- a/tests/test-continuation/pom.xml
+++ b/tests/test-continuation/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststests-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408../pom.xml4.0.0
diff --git a/tests/test-distribution/pom.xml b/tests/test-distribution/pom.xml
index a63680c64ca..38d2189ce3b 100644
--- a/tests/test-distribution/pom.xml
+++ b/tests/test-distribution/pom.xml
@@ -2,7 +2,7 @@
tests-parentorg.eclipse.jetty.tests
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/tests/test-http-client-transport/pom.xml b/tests/test-http-client-transport/pom.xml
index f647f245df5..838d2e6a259 100644
--- a/tests/test-http-client-transport/pom.xml
+++ b/tests/test-http-client-transport/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststests-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/tests/test-integration/pom.xml b/tests/test-integration/pom.xml
index 881f5145965..c5788875a98 100644
--- a/tests/test-integration/pom.xml
+++ b/tests/test-integration/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststests-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0test-integration
diff --git a/tests/test-jmx/jmx-webapp-it/pom.xml b/tests/test-jmx/jmx-webapp-it/pom.xml
index ec9a807727c..8a780fcfdf8 100644
--- a/tests/test-jmx/jmx-webapp-it/pom.xml
+++ b/tests/test-jmx/jmx-webapp-it/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-jmx-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0jmx-webapp-it
diff --git a/tests/test-jmx/jmx-webapp/pom.xml b/tests/test-jmx/jmx-webapp/pom.xml
index b09aaf898ac..0f18e74ab0c 100644
--- a/tests/test-jmx/jmx-webapp/pom.xml
+++ b/tests/test-jmx/jmx-webapp/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-jmx-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408jmx-webappwar
diff --git a/tests/test-jmx/pom.xml b/tests/test-jmx/pom.xml
index f5a352f2e95..5d9f49b83b4 100644
--- a/tests/test-jmx/pom.xml
+++ b/tests/test-jmx/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststests-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0test-jmx-parent
diff --git a/tests/test-loginservice/pom.xml b/tests/test-loginservice/pom.xml
index ee1c08be5e7..4bec180d21f 100644
--- a/tests/test-loginservice/pom.xml
+++ b/tests/test-loginservice/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststests-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408test-loginserviceJetty Tests :: Login Service
diff --git a/tests/test-quickstart/pom.xml b/tests/test-quickstart/pom.xml
index 182f33e4636..f84a342be8f 100644
--- a/tests/test-quickstart/pom.xml
+++ b/tests/test-quickstart/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststests-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408../pom.xml4.0.0
diff --git a/tests/test-sessions/pom.xml b/tests/test-sessions/pom.xml
index f5de90f2753..e4ee636a4e6 100644
--- a/tests/test-sessions/pom.xml
+++ b/tests/test-sessions/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststests-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408test-sessions-parentJetty Tests :: Sessions :: Parent
diff --git a/tests/test-sessions/test-file-sessions/pom.xml b/tests/test-sessions/test-file-sessions/pom.xml
index 9fbcbf548c4..7d1311177b9 100644
--- a/tests/test-sessions/test-file-sessions/pom.xml
+++ b/tests/test-sessions/test-file-sessions/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-sessions-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408test-file-sessionsJetty Tests :: Sessions :: File
diff --git a/tests/test-sessions/test-gcloud-sessions/pom.xml b/tests/test-sessions/test-gcloud-sessions/pom.xml
index 197ef52d32c..7fc9f9bb6b9 100644
--- a/tests/test-sessions/test-gcloud-sessions/pom.xml
+++ b/tests/test-sessions/test-gcloud-sessions/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-sessions-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408test-gcloud-sessionsJetty Tests :: Sessions :: GCloud
diff --git a/tests/test-sessions/test-hazelcast-sessions/pom.xml b/tests/test-sessions/test-hazelcast-sessions/pom.xml
index 831aee39f57..94f97a1a6d1 100644
--- a/tests/test-sessions/test-hazelcast-sessions/pom.xml
+++ b/tests/test-sessions/test-hazelcast-sessions/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-sessions-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408test-hazelcast-sessionsJetty Tests :: Sessions :: Hazelcast
diff --git a/tests/test-sessions/test-infinispan-sessions/pom.xml b/tests/test-sessions/test-infinispan-sessions/pom.xml
index 13fb1351891..92a354df743 100644
--- a/tests/test-sessions/test-infinispan-sessions/pom.xml
+++ b/tests/test-sessions/test-infinispan-sessions/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-sessions-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408test-infinispan-sessionsJetty Tests :: Sessions :: Infinispan
diff --git a/tests/test-sessions/test-jdbc-sessions/pom.xml b/tests/test-sessions/test-jdbc-sessions/pom.xml
index 0ed9795be05..63acd900c15 100644
--- a/tests/test-sessions/test-jdbc-sessions/pom.xml
+++ b/tests/test-sessions/test-jdbc-sessions/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-sessions-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408test-jdbc-sessionsJetty Tests :: Sessions :: JDBC
diff --git a/tests/test-sessions/test-memcached-sessions/pom.xml b/tests/test-sessions/test-memcached-sessions/pom.xml
index 1c875e092ab..b5520a67486 100644
--- a/tests/test-sessions/test-memcached-sessions/pom.xml
+++ b/tests/test-sessions/test-memcached-sessions/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-sessions-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408test-memcached-sessionsJetty Tests :: Sessions :: Memcached
diff --git a/tests/test-sessions/test-mongodb-sessions/pom.xml b/tests/test-sessions/test-mongodb-sessions/pom.xml
index 5e424945fd5..266241d83af 100644
--- a/tests/test-sessions/test-mongodb-sessions/pom.xml
+++ b/tests/test-sessions/test-mongodb-sessions/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-sessions-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408test-mongodb-sessionsJetty Tests :: Sessions :: Mongo
diff --git a/tests/test-sessions/test-sessions-common/pom.xml b/tests/test-sessions/test-sessions-common/pom.xml
index 6f5b268398a..faa26d2a9fd 100644
--- a/tests/test-sessions/test-sessions-common/pom.xml
+++ b/tests/test-sessions/test-sessions-common/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-sessions-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408test-sessions-commonJetty Tests :: Sessions :: Common
diff --git a/tests/test-webapps/pom.xml b/tests/test-webapps/pom.xml
index 641c70f20bb..c63f0966b47 100644
--- a/tests/test-webapps/pom.xml
+++ b/tests/test-webapps/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststests-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408../pom.xmltest-webapps-parent
diff --git a/tests/test-webapps/test-cdi-common-webapp/pom.xml b/tests/test-webapps/test-cdi-common-webapp/pom.xml
index a0f0f030950..d72012db769 100644
--- a/tests/test-webapps/test-cdi-common-webapp/pom.xml
+++ b/tests/test-webapps/test-cdi-common-webapp/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/tests/test-webapps/test-felix-webapp/pom.xml b/tests/test-webapps/test-felix-webapp/pom.xml
index 4d7f2faba6f..85bfc7220de 100644
--- a/tests/test-webapps/test-felix-webapp/pom.xml
+++ b/tests/test-webapps/test-felix-webapp/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/tests/test-webapps/test-http2-webapp/pom.xml b/tests/test-webapps/test-http2-webapp/pom.xml
index e20377fb867..544682c765a 100644
--- a/tests/test-webapps/test-http2-webapp/pom.xml
+++ b/tests/test-webapps/test-http2-webapp/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/tests/test-webapps/test-jaas-webapp/pom.xml b/tests/test-webapps/test-jaas-webapp/pom.xml
index dc56b91cab1..7a3cff0b6bf 100644
--- a/tests/test-webapps/test-jaas-webapp/pom.xml
+++ b/tests/test-webapps/test-jaas-webapp/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408test-jaas-webappJetty Tests :: WebApp :: JAAS
diff --git a/tests/test-webapps/test-jetty-webapp/pom.xml b/tests/test-webapps/test-jetty-webapp/pom.xml
index 342851e7726..07d1e669709 100644
--- a/tests/test-webapps/test-jetty-webapp/pom.xml
+++ b/tests/test-webapps/test-jetty-webapp/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408../pom.xml4.0.0
diff --git a/tests/test-webapps/test-jndi-webapp/pom.xml b/tests/test-webapps/test-jndi-webapp/pom.xml
index 4cf66fc72c0..1e3cd33b08f 100644
--- a/tests/test-webapps/test-jndi-webapp/pom.xml
+++ b/tests/test-webapps/test-jndi-webapp/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408test-jndi-webappJetty Tests :: WebApp :: JNDI
diff --git a/tests/test-webapps/test-mock-resources/pom.xml b/tests/test-webapps/test-mock-resources/pom.xml
index 27ba0bc06e8..7b1b46c85b2 100644
--- a/tests/test-webapps/test-mock-resources/pom.xml
+++ b/tests/test-webapps/test-mock-resources/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408Jetty Tests :: WebApp :: Mock Resourcestest-mock-resources
diff --git a/tests/test-webapps/test-owb-cdi-webapp/pom.xml b/tests/test-webapps/test-owb-cdi-webapp/pom.xml
index 323eca72cf5..6157ecd0f4f 100644
--- a/tests/test-webapps/test-owb-cdi-webapp/pom.xml
+++ b/tests/test-webapps/test-owb-cdi-webapp/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
diff --git a/tests/test-webapps/test-proxy-webapp/pom.xml b/tests/test-webapps/test-proxy-webapp/pom.xml
index abe8613dcfb..5fcd1c21c30 100644
--- a/tests/test-webapps/test-proxy-webapp/pom.xml
+++ b/tests/test-webapps/test-proxy-webapp/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408../pom.xml4.0.0
diff --git a/tests/test-webapps/test-servlet-spec/pom.xml b/tests/test-webapps/test-servlet-spec/pom.xml
index 111d09f08c2..26512ed5941 100644
--- a/tests/test-webapps/test-servlet-spec/pom.xml
+++ b/tests/test-webapps/test-servlet-spec/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408test-servlet-spec-parentJetty Tests :: Spec Test WebApp :: Parent
diff --git a/tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml b/tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml
index c2c0ec69ea1..7b9b8cbc1e5 100644
--- a/tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml
+++ b/tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-servlet-spec-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408test-container-initializerjar
diff --git a/tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml b/tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml
index 39f7cdf38b7..5b401b80488 100644
--- a/tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml
+++ b/tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-servlet-spec-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408Jetty Tests :: Webapps :: Spec Webapptest-spec-webapp
diff --git a/tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml b/tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml
index 8d9e1abf36b..3e38da60b57 100644
--- a/tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml
+++ b/tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-servlet-spec-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408Jetty Tests :: WebApp :: Servlet Spec :: Fragment Jar
diff --git a/tests/test-webapps/test-simple-webapp/pom.xml b/tests/test-webapps/test-simple-webapp/pom.xml
index f4c08cbec0d..f815056dcae 100644
--- a/tests/test-webapps/test-simple-webapp/pom.xml
+++ b/tests/test-webapps/test-simple-webapp/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408test-simple-webapp
diff --git a/tests/test-webapps/test-webapp-rfc2616/pom.xml b/tests/test-webapps/test-webapp-rfc2616/pom.xml
index 6267f01dbbf..571534ccedc 100644
--- a/tests/test-webapps/test-webapp-rfc2616/pom.xml
+++ b/tests/test-webapps/test-webapp-rfc2616/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v20200408test-webapp-rfc2616Jetty Tests :: WebApp :: RFC2616
diff --git a/tests/test-webapps/test-weld-cdi-webapp/pom.xml b/tests/test-webapps/test-weld-cdi-webapp/pom.xml
index 444615d9251..c31107d0259 100644
--- a/tests/test-webapps/test-weld-cdi-webapp/pom.xml
+++ b/tests/test-webapps/test-weld-cdi-webapp/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28-SNAPSHOT
+ 9.4.28.v202004084.0.0
From 5699b9ff3d3ff94cd16385e20def30cbff2f2512 Mon Sep 17 00:00:00 2001
From: Joakim Erdfelt
Date: Wed, 8 Apr 2020 13:15:04 -0500
Subject: [PATCH 064/101] Updating to version 9.4.29-SNAPSHOT
---
VERSION.txt | 2 +
aggregates/jetty-all-compact3/pom.xml | 2 +-
aggregates/jetty-all/pom.xml | 2 +-
apache-jsp/pom.xml | 2 +-
apache-jstl/pom.xml | 2 +-
build-resources/pom.xml | 2 +-
examples/async-rest/async-rest-jar/pom.xml | 2 +-
examples/async-rest/async-rest-webapp/pom.xml | 2 +-
examples/async-rest/pom.xml | 2 +-
examples/embedded/pom.xml | 2 +-
examples/pom.xml | 2 +-
jetty-alpn/jetty-alpn-client/pom.xml | 2 +-
.../jetty-alpn-conscrypt-client/pom.xml | 2 +-
.../jetty-alpn-conscrypt-server/pom.xml | 2 +-
jetty-alpn/jetty-alpn-java-client/pom.xml | 2 +-
jetty-alpn/jetty-alpn-java-server/pom.xml | 2 +-
jetty-alpn/jetty-alpn-openjdk8-client/pom.xml | 2 +-
jetty-alpn/jetty-alpn-openjdk8-server/pom.xml | 2 +-
jetty-alpn/jetty-alpn-server/pom.xml | 2 +-
jetty-alpn/pom.xml | 2 +-
jetty-annotations/pom.xml | 2 +-
jetty-ant/pom.xml | 2 +-
jetty-bom/pom.xml | 134 +++++++++---------
jetty-cdi/pom.xml | 2 +-
jetty-client/pom.xml | 2 +-
jetty-continuation/pom.xml | 2 +-
jetty-deploy/pom.xml | 2 +-
jetty-distribution/pom.xml | 2 +-
jetty-documentation/pom.xml | 2 +-
jetty-fcgi/fcgi-client/pom.xml | 2 +-
jetty-fcgi/fcgi-server/pom.xml | 2 +-
jetty-fcgi/pom.xml | 2 +-
.../jetty-gcloud-session-manager/pom.xml | 2 +-
jetty-gcloud/pom.xml | 2 +-
jetty-hazelcast/pom.xml | 2 +-
jetty-home/pom.xml | 2 +-
jetty-http-spi/pom.xml | 2 +-
jetty-http/pom.xml | 2 +-
jetty-http2/http2-alpn-tests/pom.xml | 2 +-
jetty-http2/http2-client/pom.xml | 2 +-
jetty-http2/http2-common/pom.xml | 2 +-
jetty-http2/http2-hpack/pom.xml | 2 +-
.../http2-http-client-transport/pom.xml | 2 +-
jetty-http2/http2-server/pom.xml | 2 +-
jetty-http2/pom.xml | 2 +-
jetty-infinispan/infinispan-common/pom.xml | 2 +-
.../infinispan-embedded-query/pom.xml | 2 +-
jetty-infinispan/infinispan-embedded/pom.xml | 2 +-
.../infinispan-remote-query/pom.xml | 2 +-
jetty-infinispan/infinispan-remote/pom.xml | 2 +-
jetty-infinispan/pom.xml | 2 +-
jetty-io/pom.xml | 2 +-
jetty-jaas/pom.xml | 2 +-
jetty-jaspi/pom.xml | 2 +-
jetty-jmh/pom.xml | 2 +-
jetty-jmx/pom.xml | 2 +-
jetty-jndi/pom.xml | 2 +-
jetty-jspc-maven-plugin/pom.xml | 2 +-
jetty-maven-plugin/pom.xml | 2 +-
.../jetty-memcached-sessions/pom.xml | 2 +-
jetty-memcached/pom.xml | 2 +-
jetty-nosql/pom.xml | 2 +-
jetty-openid/pom.xml | 2 +-
jetty-osgi/jetty-osgi-alpn/pom.xml | 2 +-
jetty-osgi/jetty-osgi-boot-jsp/pom.xml | 2 +-
jetty-osgi/jetty-osgi-boot-warurl/pom.xml | 2 +-
jetty-osgi/jetty-osgi-boot/pom.xml | 2 +-
jetty-osgi/jetty-osgi-httpservice/pom.xml | 2 +-
jetty-osgi/pom.xml | 2 +-
jetty-osgi/test-jetty-osgi-context/pom.xml | 2 +-
jetty-osgi/test-jetty-osgi-fragment/pom.xml | 2 +-
jetty-osgi/test-jetty-osgi-server/pom.xml | 2 +-
jetty-osgi/test-jetty-osgi-webapp/pom.xml | 2 +-
jetty-osgi/test-jetty-osgi/pom.xml | 2 +-
jetty-plus/pom.xml | 2 +-
jetty-proxy/pom.xml | 2 +-
jetty-quickstart/pom.xml | 2 +-
jetty-rewrite/pom.xml | 2 +-
jetty-runner/pom.xml | 2 +-
jetty-security/pom.xml | 2 +-
jetty-server/pom.xml | 2 +-
jetty-servlet/pom.xml | 2 +-
jetty-servlets/pom.xml | 2 +-
jetty-spring/pom.xml | 2 +-
jetty-start/pom.xml | 2 +-
jetty-unixsocket/pom.xml | 2 +-
jetty-util-ajax/pom.xml | 2 +-
jetty-util/pom.xml | 2 +-
jetty-webapp/pom.xml | 2 +-
.../javax-websocket-client-impl/pom.xml | 2 +-
.../javax-websocket-server-impl/pom.xml | 2 +-
jetty-websocket/jetty-websocket-tests/pom.xml | 2 +-
jetty-websocket/pom.xml | 2 +-
jetty-websocket/websocket-api/pom.xml | 2 +-
jetty-websocket/websocket-client/pom.xml | 2 +-
jetty-websocket/websocket-common/pom.xml | 2 +-
jetty-websocket/websocket-server/pom.xml | 2 +-
jetty-websocket/websocket-servlet/pom.xml | 2 +-
jetty-xml/pom.xml | 2 +-
pom.xml | 2 +-
tests/pom.xml | 2 +-
tests/test-continuation/pom.xml | 2 +-
tests/test-distribution/pom.xml | 2 +-
tests/test-http-client-transport/pom.xml | 2 +-
tests/test-integration/pom.xml | 2 +-
tests/test-jmx/jmx-webapp-it/pom.xml | 2 +-
tests/test-jmx/jmx-webapp/pom.xml | 2 +-
tests/test-jmx/pom.xml | 2 +-
tests/test-loginservice/pom.xml | 2 +-
tests/test-quickstart/pom.xml | 2 +-
tests/test-sessions/pom.xml | 2 +-
.../test-sessions/test-file-sessions/pom.xml | 2 +-
.../test-gcloud-sessions/pom.xml | 2 +-
.../test-hazelcast-sessions/pom.xml | 2 +-
.../test-infinispan-sessions/pom.xml | 2 +-
.../test-sessions/test-jdbc-sessions/pom.xml | 2 +-
.../test-memcached-sessions/pom.xml | 2 +-
.../test-mongodb-sessions/pom.xml | 2 +-
.../test-sessions-common/pom.xml | 2 +-
tests/test-webapps/pom.xml | 2 +-
.../test-cdi-common-webapp/pom.xml | 2 +-
tests/test-webapps/test-felix-webapp/pom.xml | 2 +-
tests/test-webapps/test-http2-webapp/pom.xml | 2 +-
tests/test-webapps/test-jaas-webapp/pom.xml | 2 +-
tests/test-webapps/test-jetty-webapp/pom.xml | 2 +-
tests/test-webapps/test-jndi-webapp/pom.xml | 2 +-
.../test-webapps/test-mock-resources/pom.xml | 2 +-
.../test-webapps/test-owb-cdi-webapp/pom.xml | 2 +-
tests/test-webapps/test-proxy-webapp/pom.xml | 2 +-
tests/test-webapps/test-servlet-spec/pom.xml | 2 +-
.../test-container-initializer/pom.xml | 2 +-
.../test-spec-webapp/pom.xml | 2 +-
.../test-web-fragment/pom.xml | 2 +-
tests/test-webapps/test-simple-webapp/pom.xml | 2 +-
.../test-webapps/test-webapp-rfc2616/pom.xml | 2 +-
.../test-webapps/test-weld-cdi-webapp/pom.xml | 2 +-
136 files changed, 203 insertions(+), 201 deletions(-)
diff --git a/VERSION.txt b/VERSION.txt
index a18b2cc03c5..c5f5ce782ea 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1,3 +1,5 @@
+jetty-9.4.29-SNAPSHOT
+
jetty-9.4.28.v20200408 - 08 April 2020
+ 847 Setting async timeout on WebSocketClient does not seem to timeout writes
+ 2896 Wrong Certificate Selected When Using Multiple Virtual Host Names in
diff --git a/aggregates/jetty-all-compact3/pom.xml b/aggregates/jetty-all-compact3/pom.xml
index 1eabf07ded7..9aa8de61f7d 100644
--- a/aggregates/jetty-all-compact3/pom.xml
+++ b/aggregates/jetty-all-compact3/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT../../pom.xml4.0.0
diff --git a/aggregates/jetty-all/pom.xml b/aggregates/jetty-all/pom.xml
index b0a64b4e124..c7ba43155b5 100644
--- a/aggregates/jetty-all/pom.xml
+++ b/aggregates/jetty-all/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT../../pom.xml4.0.0
diff --git a/apache-jsp/pom.xml b/apache-jsp/pom.xml
index 3cfcb4f2b95..d65d9fbcbcc 100644
--- a/apache-jsp/pom.xml
+++ b/apache-jsp/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0apache-jsp
diff --git a/apache-jstl/pom.xml b/apache-jstl/pom.xml
index 60209ea0f8e..7ddb0b852a2 100644
--- a/apache-jstl/pom.xml
+++ b/apache-jstl/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0apache-jstl
diff --git a/build-resources/pom.xml b/build-resources/pom.xml
index 23b8184c142..2c9845d2625 100644
--- a/build-resources/pom.xml
+++ b/build-resources/pom.xml
@@ -2,7 +2,7 @@
4.0.0org.eclipse.jettybuild-resources
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTjarJetty :: Build Resources
diff --git a/examples/async-rest/async-rest-jar/pom.xml b/examples/async-rest/async-rest-jar/pom.xml
index 944c5cb2a8b..85d69939487 100644
--- a/examples/async-rest/async-rest-jar/pom.xml
+++ b/examples/async-rest/async-rest-jar/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyexample-async-rest
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/examples/async-rest/async-rest-webapp/pom.xml b/examples/async-rest/async-rest-webapp/pom.xml
index 9d8a5820245..19486ffe653 100644
--- a/examples/async-rest/async-rest-webapp/pom.xml
+++ b/examples/async-rest/async-rest-webapp/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyexample-async-rest
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/examples/async-rest/pom.xml b/examples/async-rest/pom.xml
index dad7888ce4e..799db801363 100644
--- a/examples/async-rest/pom.xml
+++ b/examples/async-rest/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.examplesexamples-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/examples/embedded/pom.xml b/examples/embedded/pom.xml
index 393ec89f1b1..ef0b4eba461 100644
--- a/examples/embedded/pom.xml
+++ b/examples/embedded/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.examplesexamples-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT../pom.xml4.0.0
diff --git a/examples/pom.xml b/examples/pom.xml
index fab43bf94f1..284ef573fee 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-alpn/jetty-alpn-client/pom.xml b/jetty-alpn/jetty-alpn-client/pom.xml
index fc655e73f86..8886bd3d421 100644
--- a/jetty-alpn/jetty-alpn-client/pom.xml
+++ b/jetty-alpn/jetty-alpn-client/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-alpn-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-alpn-client
diff --git a/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml b/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml
index 173d2bbc3e2..a6f273200ae 100644
--- a/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml
+++ b/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jettyjetty-alpn-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml b/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml
index e5d8910158f..585cfcec66d 100644
--- a/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml
+++ b/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jettyjetty-alpn-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-alpn/jetty-alpn-java-client/pom.xml b/jetty-alpn/jetty-alpn-java-client/pom.xml
index af355a8f4f5..17a626e7e11 100644
--- a/jetty-alpn/jetty-alpn-java-client/pom.xml
+++ b/jetty-alpn/jetty-alpn-java-client/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jettyjetty-alpn-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-alpn/jetty-alpn-java-server/pom.xml b/jetty-alpn/jetty-alpn-java-server/pom.xml
index 2406fde0c73..af2f2fbe46e 100644
--- a/jetty-alpn/jetty-alpn-java-server/pom.xml
+++ b/jetty-alpn/jetty-alpn-java-server/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jettyjetty-alpn-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-alpn/jetty-alpn-openjdk8-client/pom.xml b/jetty-alpn/jetty-alpn-openjdk8-client/pom.xml
index d43d013b807..e7b25f6c49f 100644
--- a/jetty-alpn/jetty-alpn-openjdk8-client/pom.xml
+++ b/jetty-alpn/jetty-alpn-openjdk8-client/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jettyjetty-alpn-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-alpn/jetty-alpn-openjdk8-server/pom.xml b/jetty-alpn/jetty-alpn-openjdk8-server/pom.xml
index 406b540a343..4a646c4078a 100644
--- a/jetty-alpn/jetty-alpn-openjdk8-server/pom.xml
+++ b/jetty-alpn/jetty-alpn-openjdk8-server/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jettyjetty-alpn-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-alpn/jetty-alpn-server/pom.xml b/jetty-alpn/jetty-alpn-server/pom.xml
index 172f0247238..9d79ac35845 100644
--- a/jetty-alpn/jetty-alpn-server/pom.xml
+++ b/jetty-alpn/jetty-alpn-server/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-alpn-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-alpn-server
diff --git a/jetty-alpn/pom.xml b/jetty-alpn/pom.xml
index a0fd982dce6..5744bbbbf71 100644
--- a/jetty-alpn/pom.xml
+++ b/jetty-alpn/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-alpn-parent
diff --git a/jetty-annotations/pom.xml b/jetty-annotations/pom.xml
index 1fff4ec4d88..fe43d65faeb 100644
--- a/jetty-annotations/pom.xml
+++ b/jetty-annotations/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-annotations
diff --git a/jetty-ant/pom.xml b/jetty-ant/pom.xml
index f67827cf3b3..5bb1b54ea29 100644
--- a/jetty-ant/pom.xml
+++ b/jetty-ant/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-ant
diff --git a/jetty-bom/pom.xml b/jetty-bom/pom.xml
index fb246fb98b0..25fb9d37eb3 100644
--- a/jetty-bom/pom.xml
+++ b/jetty-bom/pom.xml
@@ -9,7 +9,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT
@@ -53,336 +53,336 @@
org.eclipse.jettyapache-jsp
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyapache-jstl
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-alpn-client
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-alpn-java-client
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-alpn-java-server
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-alpn-openjdk8-client
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-alpn-openjdk8-server
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-alpn-conscrypt-client
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-alpn-conscrypt-server
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-alpn-server
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-annotations
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-ant
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-client
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-continuation
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-deploy
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-distribution
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTziporg.eclipse.jettyjetty-distribution
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtar.gzorg.eclipse.jetty.fcgifcgi-client
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.fcgifcgi-server
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.gcloudjetty-gcloud-session-manager
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-home
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTziporg.eclipse.jettyjetty-home
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtar.gzorg.eclipse.jettyjetty-http
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.http2http2-client
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.http2http2-common
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.http2http2-hpack
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.http2http2-http-client-transport
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.http2http2-server
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-http-spi
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyinfinispan-common
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyinfinispan-remote-query
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyinfinispan-embedded-query
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-hazelcast
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-io
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-jaas
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-jaspi
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-jmx
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-jndi
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.memcachedjetty-memcached-sessions
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-nosql
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.osgijetty-osgi-boot
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.osgijetty-osgi-boot-jsp
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.osgijetty-osgi-boot-warurl
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.osgijetty-httpservice
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-plus
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-proxy
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-quickstart
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-rewrite
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-security
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-openid
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-server
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-servlet
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-servlets
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-spring
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-unixsocket
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-util
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-util-ajax
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-webapp
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.websocketjavax-websocket-client-impl
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.websocketjavax-websocket-server-impl
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.websocketwebsocket-api
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.websocketwebsocket-client
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.websocketwebsocket-common
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.websocketwebsocket-server
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jetty.websocketwebsocket-servlet
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTorg.eclipse.jettyjetty-xml
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT
diff --git a/jetty-cdi/pom.xml b/jetty-cdi/pom.xml
index 22b8255f18b..192eeba6002 100644
--- a/jetty-cdi/pom.xml
+++ b/jetty-cdi/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0org.eclipse.jetty
diff --git a/jetty-client/pom.xml b/jetty-client/pom.xml
index cdd49444081..76409b9bfa7 100644
--- a/jetty-client/pom.xml
+++ b/jetty-client/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-continuation/pom.xml b/jetty-continuation/pom.xml
index b8f0f187835..0c1e7a6540d 100644
--- a/jetty-continuation/pom.xml
+++ b/jetty-continuation/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-continuation
diff --git a/jetty-deploy/pom.xml b/jetty-deploy/pom.xml
index 417d7d953dc..aac2955999a 100644
--- a/jetty-deploy/pom.xml
+++ b/jetty-deploy/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-deploy
diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml
index 3f003be87bd..1e60b7b3190 100644
--- a/jetty-distribution/pom.xml
+++ b/jetty-distribution/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-distribution
diff --git a/jetty-documentation/pom.xml b/jetty-documentation/pom.xml
index 119e62f4662..62da69a54b1 100644
--- a/jetty-documentation/pom.xml
+++ b/jetty-documentation/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTjetty-documentationJetty :: Documentation
diff --git a/jetty-fcgi/fcgi-client/pom.xml b/jetty-fcgi/fcgi-client/pom.xml
index 06756f19b0a..8519df4d6f2 100644
--- a/jetty-fcgi/fcgi-client/pom.xml
+++ b/jetty-fcgi/fcgi-client/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.fcgifcgi-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-fcgi/fcgi-server/pom.xml b/jetty-fcgi/fcgi-server/pom.xml
index 221312a6899..72775eaae08 100644
--- a/jetty-fcgi/fcgi-server/pom.xml
+++ b/jetty-fcgi/fcgi-server/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.fcgifcgi-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-fcgi/pom.xml b/jetty-fcgi/pom.xml
index e2f1b186d8d..93483624a72 100644
--- a/jetty-fcgi/pom.xml
+++ b/jetty-fcgi/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-gcloud/jetty-gcloud-session-manager/pom.xml b/jetty-gcloud/jetty-gcloud-session-manager/pom.xml
index 464abb4ebf5..07bc061d319 100644
--- a/jetty-gcloud/jetty-gcloud-session-manager/pom.xml
+++ b/jetty-gcloud/jetty-gcloud-session-manager/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.gcloudgcloud-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-gcloud/pom.xml b/jetty-gcloud/pom.xml
index 273544d929d..db7045d9a4a 100644
--- a/jetty-gcloud/pom.xml
+++ b/jetty-gcloud/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-hazelcast/pom.xml b/jetty-hazelcast/pom.xml
index a5ae15d32df..c9b01e3d9cd 100644
--- a/jetty-hazelcast/pom.xml
+++ b/jetty-hazelcast/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-home/pom.xml b/jetty-home/pom.xml
index 1932babcfd2..e97c7dbf02f 100644
--- a/jetty-home/pom.xml
+++ b/jetty-home/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-home
diff --git a/jetty-http-spi/pom.xml b/jetty-http-spi/pom.xml
index ca132b03a41..bc4f4b040bd 100644
--- a/jetty-http-spi/pom.xml
+++ b/jetty-http-spi/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-http-spi
diff --git a/jetty-http/pom.xml b/jetty-http/pom.xml
index b6edfd2bece..d5c97ec4f16 100644
--- a/jetty-http/pom.xml
+++ b/jetty-http/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-http
diff --git a/jetty-http2/http2-alpn-tests/pom.xml b/jetty-http2/http2-alpn-tests/pom.xml
index 62491f58b28..d397a5e22d1 100644
--- a/jetty-http2/http2-alpn-tests/pom.xml
+++ b/jetty-http2/http2-alpn-tests/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.http2http2-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-http2/http2-client/pom.xml b/jetty-http2/http2-client/pom.xml
index 55c6e48265b..b57b00b5a24 100644
--- a/jetty-http2/http2-client/pom.xml
+++ b/jetty-http2/http2-client/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.http2http2-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-http2/http2-common/pom.xml b/jetty-http2/http2-common/pom.xml
index 195767af9bd..a8e58c545b7 100644
--- a/jetty-http2/http2-common/pom.xml
+++ b/jetty-http2/http2-common/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.http2http2-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-http2/http2-hpack/pom.xml b/jetty-http2/http2-hpack/pom.xml
index 159b8296c63..add16c2afd1 100644
--- a/jetty-http2/http2-hpack/pom.xml
+++ b/jetty-http2/http2-hpack/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.http2http2-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-http2/http2-http-client-transport/pom.xml b/jetty-http2/http2-http-client-transport/pom.xml
index efc7ff381f0..3f8490b9ab8 100644
--- a/jetty-http2/http2-http-client-transport/pom.xml
+++ b/jetty-http2/http2-http-client-transport/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.http2http2-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-http2/http2-server/pom.xml b/jetty-http2/http2-server/pom.xml
index a882ab53477..77eb61f3b35 100644
--- a/jetty-http2/http2-server/pom.xml
+++ b/jetty-http2/http2-server/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.http2http2-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-http2/pom.xml b/jetty-http2/pom.xml
index b0bed74236d..d11f82a9f25 100644
--- a/jetty-http2/pom.xml
+++ b/jetty-http2/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-infinispan/infinispan-common/pom.xml b/jetty-infinispan/infinispan-common/pom.xml
index 8179109783f..620748bdb67 100644
--- a/jetty-infinispan/infinispan-common/pom.xml
+++ b/jetty-infinispan/infinispan-common/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyinfinispan-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0infinispan-common
diff --git a/jetty-infinispan/infinispan-embedded-query/pom.xml b/jetty-infinispan/infinispan-embedded-query/pom.xml
index 5a2247ca7d2..d2555f9d845 100644
--- a/jetty-infinispan/infinispan-embedded-query/pom.xml
+++ b/jetty-infinispan/infinispan-embedded-query/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyinfinispan-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0infinispan-embedded-query
diff --git a/jetty-infinispan/infinispan-embedded/pom.xml b/jetty-infinispan/infinispan-embedded/pom.xml
index 29e41e4c692..f33d4474e97 100644
--- a/jetty-infinispan/infinispan-embedded/pom.xml
+++ b/jetty-infinispan/infinispan-embedded/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyinfinispan-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0infinispan-embedded
diff --git a/jetty-infinispan/infinispan-remote-query/pom.xml b/jetty-infinispan/infinispan-remote-query/pom.xml
index d49d6822640..a9ec47afbaa 100644
--- a/jetty-infinispan/infinispan-remote-query/pom.xml
+++ b/jetty-infinispan/infinispan-remote-query/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyinfinispan-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0infinispan-remote-query
diff --git a/jetty-infinispan/infinispan-remote/pom.xml b/jetty-infinispan/infinispan-remote/pom.xml
index c21b228bf94..63235a66170 100644
--- a/jetty-infinispan/infinispan-remote/pom.xml
+++ b/jetty-infinispan/infinispan-remote/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyinfinispan-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0infinispan-remote
diff --git a/jetty-infinispan/pom.xml b/jetty-infinispan/pom.xml
index 026bb5d9d57..75103e182df 100644
--- a/jetty-infinispan/pom.xml
+++ b/jetty-infinispan/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-io/pom.xml b/jetty-io/pom.xml
index 605a8aaa02e..5efa95c6c7e 100644
--- a/jetty-io/pom.xml
+++ b/jetty-io/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-io
diff --git a/jetty-jaas/pom.xml b/jetty-jaas/pom.xml
index 174444c0eb9..72b221acfc5 100644
--- a/jetty-jaas/pom.xml
+++ b/jetty-jaas/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-jaas
diff --git a/jetty-jaspi/pom.xml b/jetty-jaspi/pom.xml
index d07dd54c914..eb3f6deedc1 100644
--- a/jetty-jaspi/pom.xml
+++ b/jetty-jaspi/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-jmh/pom.xml b/jetty-jmh/pom.xml
index f6febdf1185..ab479d23d85 100644
--- a/jetty-jmh/pom.xml
+++ b/jetty-jmh/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-jmx/pom.xml b/jetty-jmx/pom.xml
index d1f4fb8fe72..3a650bd610b 100644
--- a/jetty-jmx/pom.xml
+++ b/jetty-jmx/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-jmx
diff --git a/jetty-jndi/pom.xml b/jetty-jndi/pom.xml
index 4a362722962..5a8c24718a2 100644
--- a/jetty-jndi/pom.xml
+++ b/jetty-jndi/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-jndi
diff --git a/jetty-jspc-maven-plugin/pom.xml b/jetty-jspc-maven-plugin/pom.xml
index e6a033ad8fb..c75714cf367 100644
--- a/jetty-jspc-maven-plugin/pom.xml
+++ b/jetty-jspc-maven-plugin/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-jspc-maven-plugin
diff --git a/jetty-maven-plugin/pom.xml b/jetty-maven-plugin/pom.xml
index 623c6db72b1..ff00b926c2d 100644
--- a/jetty-maven-plugin/pom.xml
+++ b/jetty-maven-plugin/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-maven-plugin
diff --git a/jetty-memcached/jetty-memcached-sessions/pom.xml b/jetty-memcached/jetty-memcached-sessions/pom.xml
index 3e3fb5b4af0..270f026712a 100644
--- a/jetty-memcached/jetty-memcached-sessions/pom.xml
+++ b/jetty-memcached/jetty-memcached-sessions/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.memcachedmemcached-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-memcached/pom.xml b/jetty-memcached/pom.xml
index ef1ea741272..e96cf2d6061 100644
--- a/jetty-memcached/pom.xml
+++ b/jetty-memcached/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-nosql/pom.xml b/jetty-nosql/pom.xml
index 9f5baa72093..8b6c28b4465 100644
--- a/jetty-nosql/pom.xml
+++ b/jetty-nosql/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-nosql
diff --git a/jetty-openid/pom.xml b/jetty-openid/pom.xml
index 62e4120046d..32f4dda6715 100644
--- a/jetty-openid/pom.xml
+++ b/jetty-openid/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-osgi/jetty-osgi-alpn/pom.xml b/jetty-osgi/jetty-osgi-alpn/pom.xml
index 963027b94ec..921e25a3a70 100644
--- a/jetty-osgi/jetty-osgi-alpn/pom.xml
+++ b/jetty-osgi/jetty-osgi-alpn/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-osgi-alpn
diff --git a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
index d1958d9f06d..f9214dab3b1 100644
--- a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-osgi-boot-jsp
diff --git a/jetty-osgi/jetty-osgi-boot-warurl/pom.xml b/jetty-osgi/jetty-osgi-boot-warurl/pom.xml
index 336ed587621..ba3721467c0 100644
--- a/jetty-osgi/jetty-osgi-boot-warurl/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot-warurl/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT../pom.xml4.0.0
diff --git a/jetty-osgi/jetty-osgi-boot/pom.xml b/jetty-osgi/jetty-osgi-boot/pom.xml
index 9fef576bfb6..e31b62d64d7 100644
--- a/jetty-osgi/jetty-osgi-boot/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-osgi-boot
diff --git a/jetty-osgi/jetty-osgi-httpservice/pom.xml b/jetty-osgi/jetty-osgi-httpservice/pom.xml
index 15847b7db59..4d20c4f092a 100644
--- a/jetty-osgi/jetty-osgi-httpservice/pom.xml
+++ b/jetty-osgi/jetty-osgi-httpservice/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-httpservice
diff --git a/jetty-osgi/pom.xml b/jetty-osgi/pom.xml
index cd3e4ae41f8..eade9567f7e 100644
--- a/jetty-osgi/pom.xml
+++ b/jetty-osgi/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-osgi/test-jetty-osgi-context/pom.xml b/jetty-osgi/test-jetty-osgi-context/pom.xml
index 63496271666..5cc2e8d5fea 100644
--- a/jetty-osgi/test-jetty-osgi-context/pom.xml
+++ b/jetty-osgi/test-jetty-osgi-context/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0test-jetty-osgi-context
diff --git a/jetty-osgi/test-jetty-osgi-fragment/pom.xml b/jetty-osgi/test-jetty-osgi-fragment/pom.xml
index 16ce2bb7979..ff6c269a776 100644
--- a/jetty-osgi/test-jetty-osgi-fragment/pom.xml
+++ b/jetty-osgi/test-jetty-osgi-fragment/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT../pom.xml4.0.0
diff --git a/jetty-osgi/test-jetty-osgi-server/pom.xml b/jetty-osgi/test-jetty-osgi-server/pom.xml
index 9f8d8b6c455..1ca62e2f40f 100644
--- a/jetty-osgi/test-jetty-osgi-server/pom.xml
+++ b/jetty-osgi/test-jetty-osgi-server/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0test-jetty-osgi-server
diff --git a/jetty-osgi/test-jetty-osgi-webapp/pom.xml b/jetty-osgi/test-jetty-osgi-webapp/pom.xml
index 685531fe2ca..a2c965a77a2 100644
--- a/jetty-osgi/test-jetty-osgi-webapp/pom.xml
+++ b/jetty-osgi/test-jetty-osgi-webapp/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT../pom.xml4.0.0
diff --git a/jetty-osgi/test-jetty-osgi/pom.xml b/jetty-osgi/test-jetty-osgi/pom.xml
index a2bb0c47e2b..48d65d94c39 100644
--- a/jetty-osgi/test-jetty-osgi/pom.xml
+++ b/jetty-osgi/test-jetty-osgi/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jetty.osgijetty-osgi-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT../pom.xml4.0.0
diff --git a/jetty-plus/pom.xml b/jetty-plus/pom.xml
index 83895ad6219..40959c65258 100644
--- a/jetty-plus/pom.xml
+++ b/jetty-plus/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-plus
diff --git a/jetty-proxy/pom.xml b/jetty-proxy/pom.xml
index 3bb5e9aed93..43cc78553d8 100644
--- a/jetty-proxy/pom.xml
+++ b/jetty-proxy/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-proxy
diff --git a/jetty-quickstart/pom.xml b/jetty-quickstart/pom.xml
index ef81c7d9f50..8b9619227d0 100644
--- a/jetty-quickstart/pom.xml
+++ b/jetty-quickstart/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0org.eclipse.jetty
diff --git a/jetty-rewrite/pom.xml b/jetty-rewrite/pom.xml
index e48c3db8c42..2d9265edf06 100644
--- a/jetty-rewrite/pom.xml
+++ b/jetty-rewrite/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-rewrite
diff --git a/jetty-runner/pom.xml b/jetty-runner/pom.xml
index 1e218b4ed60..4bd67315249 100644
--- a/jetty-runner/pom.xml
+++ b/jetty-runner/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-runner
diff --git a/jetty-security/pom.xml b/jetty-security/pom.xml
index b5a7ec859b6..8a74827cb50 100644
--- a/jetty-security/pom.xml
+++ b/jetty-security/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-security
diff --git a/jetty-server/pom.xml b/jetty-server/pom.xml
index e1b8513a5be..8e549a01400 100644
--- a/jetty-server/pom.xml
+++ b/jetty-server/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-server
diff --git a/jetty-servlet/pom.xml b/jetty-servlet/pom.xml
index ba3d42d4139..54eee174c55 100644
--- a/jetty-servlet/pom.xml
+++ b/jetty-servlet/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-servlet
diff --git a/jetty-servlets/pom.xml b/jetty-servlets/pom.xml
index 0cd87fe7d60..4a97a896d45 100644
--- a/jetty-servlets/pom.xml
+++ b/jetty-servlets/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-servlets
diff --git a/jetty-spring/pom.xml b/jetty-spring/pom.xml
index 8a73d6ab249..6bbbbfb8752 100644
--- a/jetty-spring/pom.xml
+++ b/jetty-spring/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-spring
diff --git a/jetty-start/pom.xml b/jetty-start/pom.xml
index 21c8aa5f615..e4f9c8adda1 100644
--- a/jetty-start/pom.xml
+++ b/jetty-start/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-start
diff --git a/jetty-unixsocket/pom.xml b/jetty-unixsocket/pom.xml
index fe6dfe32879..5f7bfe1722d 100644
--- a/jetty-unixsocket/pom.xml
+++ b/jetty-unixsocket/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-unixsocket
diff --git a/jetty-util-ajax/pom.xml b/jetty-util-ajax/pom.xml
index 50009f5ea60..bb793877902 100644
--- a/jetty-util-ajax/pom.xml
+++ b/jetty-util-ajax/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-util-ajax
diff --git a/jetty-util/pom.xml b/jetty-util/pom.xml
index 455f784102e..8c4eed9715e 100644
--- a/jetty-util/pom.xml
+++ b/jetty-util/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-util
diff --git a/jetty-webapp/pom.xml b/jetty-webapp/pom.xml
index 194abb28d41..f405910ae23 100644
--- a/jetty-webapp/pom.xml
+++ b/jetty-webapp/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-webapp
diff --git a/jetty-websocket/javax-websocket-client-impl/pom.xml b/jetty-websocket/javax-websocket-client-impl/pom.xml
index 693d47beabf..045ef095e7b 100644
--- a/jetty-websocket/javax-websocket-client-impl/pom.xml
+++ b/jetty-websocket/javax-websocket-client-impl/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.websocketwebsocket-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-websocket/javax-websocket-server-impl/pom.xml b/jetty-websocket/javax-websocket-server-impl/pom.xml
index 0e0c0a0b318..abeb0bc434e 100644
--- a/jetty-websocket/javax-websocket-server-impl/pom.xml
+++ b/jetty-websocket/javax-websocket-server-impl/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.websocketwebsocket-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-websocket/jetty-websocket-tests/pom.xml b/jetty-websocket/jetty-websocket-tests/pom.xml
index aecb624ee35..ac4e5befd7b 100644
--- a/jetty-websocket/jetty-websocket-tests/pom.xml
+++ b/jetty-websocket/jetty-websocket-tests/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.websocketwebsocket-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-websocket/pom.xml b/jetty-websocket/pom.xml
index 9c0f8af898f..1215318c6c7 100644
--- a/jetty-websocket/pom.xml
+++ b/jetty-websocket/pom.xml
@@ -3,7 +3,7 @@
jetty-projectorg.eclipse.jetty
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-websocket/websocket-api/pom.xml b/jetty-websocket/websocket-api/pom.xml
index 929ecae0165..975d700d283 100644
--- a/jetty-websocket/websocket-api/pom.xml
+++ b/jetty-websocket/websocket-api/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.websocketwebsocket-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-websocket/websocket-client/pom.xml b/jetty-websocket/websocket-client/pom.xml
index ca624c66825..a5c7894f114 100644
--- a/jetty-websocket/websocket-client/pom.xml
+++ b/jetty-websocket/websocket-client/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.websocketwebsocket-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-websocket/websocket-common/pom.xml b/jetty-websocket/websocket-common/pom.xml
index f0ed7a19eb0..ced91037eab 100644
--- a/jetty-websocket/websocket-common/pom.xml
+++ b/jetty-websocket/websocket-common/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.websocketwebsocket-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-websocket/websocket-server/pom.xml b/jetty-websocket/websocket-server/pom.xml
index 4ea0c98a90f..48ec0c302cc 100644
--- a/jetty-websocket/websocket-server/pom.xml
+++ b/jetty-websocket/websocket-server/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.websocketwebsocket-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-websocket/websocket-servlet/pom.xml b/jetty-websocket/websocket-servlet/pom.xml
index fe62e9468a6..52759d5c128 100644
--- a/jetty-websocket/websocket-servlet/pom.xml
+++ b/jetty-websocket/websocket-servlet/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.websocketwebsocket-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/jetty-xml/pom.xml b/jetty-xml/pom.xml
index f27aefed9a0..5c1653c42cd 100644
--- a/jetty-xml/pom.xml
+++ b/jetty-xml/pom.xml
@@ -2,7 +2,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jetty-xml
diff --git a/pom.xml b/pom.xml
index c73480e9fc1..87af7001236 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTJetty :: ProjectThe Eclipse Jetty Projectpom
diff --git a/tests/pom.xml b/tests/pom.xml
index 860d9ad1ca8..c13d4db6ea4 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jettyjetty-project
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT../pom.xmlorg.eclipse.jetty.tests
diff --git a/tests/test-continuation/pom.xml b/tests/test-continuation/pom.xml
index 6df822f6b45..1494e53b224 100644
--- a/tests/test-continuation/pom.xml
+++ b/tests/test-continuation/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststests-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT../pom.xml4.0.0
diff --git a/tests/test-distribution/pom.xml b/tests/test-distribution/pom.xml
index 38d2189ce3b..85d592e07c6 100644
--- a/tests/test-distribution/pom.xml
+++ b/tests/test-distribution/pom.xml
@@ -2,7 +2,7 @@
tests-parentorg.eclipse.jetty.tests
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/tests/test-http-client-transport/pom.xml b/tests/test-http-client-transport/pom.xml
index 838d2e6a259..55fc990bddc 100644
--- a/tests/test-http-client-transport/pom.xml
+++ b/tests/test-http-client-transport/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststests-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/tests/test-integration/pom.xml b/tests/test-integration/pom.xml
index c5788875a98..6a1feabddbc 100644
--- a/tests/test-integration/pom.xml
+++ b/tests/test-integration/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststests-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0test-integration
diff --git a/tests/test-jmx/jmx-webapp-it/pom.xml b/tests/test-jmx/jmx-webapp-it/pom.xml
index 8a780fcfdf8..5046fcdde62 100644
--- a/tests/test-jmx/jmx-webapp-it/pom.xml
+++ b/tests/test-jmx/jmx-webapp-it/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-jmx-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0jmx-webapp-it
diff --git a/tests/test-jmx/jmx-webapp/pom.xml b/tests/test-jmx/jmx-webapp/pom.xml
index 0f18e74ab0c..01c5fdc2fe0 100644
--- a/tests/test-jmx/jmx-webapp/pom.xml
+++ b/tests/test-jmx/jmx-webapp/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-jmx-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTjmx-webappwar
diff --git a/tests/test-jmx/pom.xml b/tests/test-jmx/pom.xml
index 5d9f49b83b4..0604c93a4c9 100644
--- a/tests/test-jmx/pom.xml
+++ b/tests/test-jmx/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststests-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0test-jmx-parent
diff --git a/tests/test-loginservice/pom.xml b/tests/test-loginservice/pom.xml
index 4bec180d21f..e64495991a7 100644
--- a/tests/test-loginservice/pom.xml
+++ b/tests/test-loginservice/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststests-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtest-loginserviceJetty Tests :: Login Service
diff --git a/tests/test-quickstart/pom.xml b/tests/test-quickstart/pom.xml
index f84a342be8f..a748977848b 100644
--- a/tests/test-quickstart/pom.xml
+++ b/tests/test-quickstart/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststests-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT../pom.xml4.0.0
diff --git a/tests/test-sessions/pom.xml b/tests/test-sessions/pom.xml
index e4ee636a4e6..0ca22dcd1c2 100644
--- a/tests/test-sessions/pom.xml
+++ b/tests/test-sessions/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststests-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtest-sessions-parentJetty Tests :: Sessions :: Parent
diff --git a/tests/test-sessions/test-file-sessions/pom.xml b/tests/test-sessions/test-file-sessions/pom.xml
index 7d1311177b9..b828eb93c93 100644
--- a/tests/test-sessions/test-file-sessions/pom.xml
+++ b/tests/test-sessions/test-file-sessions/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-sessions-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtest-file-sessionsJetty Tests :: Sessions :: File
diff --git a/tests/test-sessions/test-gcloud-sessions/pom.xml b/tests/test-sessions/test-gcloud-sessions/pom.xml
index 7fc9f9bb6b9..ae19f2de755 100644
--- a/tests/test-sessions/test-gcloud-sessions/pom.xml
+++ b/tests/test-sessions/test-gcloud-sessions/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-sessions-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtest-gcloud-sessionsJetty Tests :: Sessions :: GCloud
diff --git a/tests/test-sessions/test-hazelcast-sessions/pom.xml b/tests/test-sessions/test-hazelcast-sessions/pom.xml
index 94f97a1a6d1..56db185935a 100644
--- a/tests/test-sessions/test-hazelcast-sessions/pom.xml
+++ b/tests/test-sessions/test-hazelcast-sessions/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-sessions-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtest-hazelcast-sessionsJetty Tests :: Sessions :: Hazelcast
diff --git a/tests/test-sessions/test-infinispan-sessions/pom.xml b/tests/test-sessions/test-infinispan-sessions/pom.xml
index 92a354df743..4a96db6f65e 100644
--- a/tests/test-sessions/test-infinispan-sessions/pom.xml
+++ b/tests/test-sessions/test-infinispan-sessions/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-sessions-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtest-infinispan-sessionsJetty Tests :: Sessions :: Infinispan
diff --git a/tests/test-sessions/test-jdbc-sessions/pom.xml b/tests/test-sessions/test-jdbc-sessions/pom.xml
index 63acd900c15..1b60ce85229 100644
--- a/tests/test-sessions/test-jdbc-sessions/pom.xml
+++ b/tests/test-sessions/test-jdbc-sessions/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-sessions-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtest-jdbc-sessionsJetty Tests :: Sessions :: JDBC
diff --git a/tests/test-sessions/test-memcached-sessions/pom.xml b/tests/test-sessions/test-memcached-sessions/pom.xml
index b5520a67486..2a691adc0f6 100644
--- a/tests/test-sessions/test-memcached-sessions/pom.xml
+++ b/tests/test-sessions/test-memcached-sessions/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-sessions-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtest-memcached-sessionsJetty Tests :: Sessions :: Memcached
diff --git a/tests/test-sessions/test-mongodb-sessions/pom.xml b/tests/test-sessions/test-mongodb-sessions/pom.xml
index 266241d83af..a3ffc36c528 100644
--- a/tests/test-sessions/test-mongodb-sessions/pom.xml
+++ b/tests/test-sessions/test-mongodb-sessions/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-sessions-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtest-mongodb-sessionsJetty Tests :: Sessions :: Mongo
diff --git a/tests/test-sessions/test-sessions-common/pom.xml b/tests/test-sessions/test-sessions-common/pom.xml
index faa26d2a9fd..aa45c63cc2a 100644
--- a/tests/test-sessions/test-sessions-common/pom.xml
+++ b/tests/test-sessions/test-sessions-common/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-sessions-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtest-sessions-commonJetty Tests :: Sessions :: Common
diff --git a/tests/test-webapps/pom.xml b/tests/test-webapps/pom.xml
index c63f0966b47..e69c4c8566a 100644
--- a/tests/test-webapps/pom.xml
+++ b/tests/test-webapps/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststests-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT../pom.xmltest-webapps-parent
diff --git a/tests/test-webapps/test-cdi-common-webapp/pom.xml b/tests/test-webapps/test-cdi-common-webapp/pom.xml
index d72012db769..7a10f068c71 100644
--- a/tests/test-webapps/test-cdi-common-webapp/pom.xml
+++ b/tests/test-webapps/test-cdi-common-webapp/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/tests/test-webapps/test-felix-webapp/pom.xml b/tests/test-webapps/test-felix-webapp/pom.xml
index 85bfc7220de..e170509eff9 100644
--- a/tests/test-webapps/test-felix-webapp/pom.xml
+++ b/tests/test-webapps/test-felix-webapp/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/tests/test-webapps/test-http2-webapp/pom.xml b/tests/test-webapps/test-http2-webapp/pom.xml
index 544682c765a..a7e2a1c358c 100644
--- a/tests/test-webapps/test-http2-webapp/pom.xml
+++ b/tests/test-webapps/test-http2-webapp/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/tests/test-webapps/test-jaas-webapp/pom.xml b/tests/test-webapps/test-jaas-webapp/pom.xml
index 7a3cff0b6bf..82adc28bbfc 100644
--- a/tests/test-webapps/test-jaas-webapp/pom.xml
+++ b/tests/test-webapps/test-jaas-webapp/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtest-jaas-webappJetty Tests :: WebApp :: JAAS
diff --git a/tests/test-webapps/test-jetty-webapp/pom.xml b/tests/test-webapps/test-jetty-webapp/pom.xml
index 07d1e669709..e37b5b22de6 100644
--- a/tests/test-webapps/test-jetty-webapp/pom.xml
+++ b/tests/test-webapps/test-jetty-webapp/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT../pom.xml4.0.0
diff --git a/tests/test-webapps/test-jndi-webapp/pom.xml b/tests/test-webapps/test-jndi-webapp/pom.xml
index 1e3cd33b08f..07c04f866e9 100644
--- a/tests/test-webapps/test-jndi-webapp/pom.xml
+++ b/tests/test-webapps/test-jndi-webapp/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtest-jndi-webappJetty Tests :: WebApp :: JNDI
diff --git a/tests/test-webapps/test-mock-resources/pom.xml b/tests/test-webapps/test-mock-resources/pom.xml
index 7b1b46c85b2..0607935a35f 100644
--- a/tests/test-webapps/test-mock-resources/pom.xml
+++ b/tests/test-webapps/test-mock-resources/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTJetty Tests :: WebApp :: Mock Resourcestest-mock-resources
diff --git a/tests/test-webapps/test-owb-cdi-webapp/pom.xml b/tests/test-webapps/test-owb-cdi-webapp/pom.xml
index 6157ecd0f4f..a673347d33e 100644
--- a/tests/test-webapps/test-owb-cdi-webapp/pom.xml
+++ b/tests/test-webapps/test-owb-cdi-webapp/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
diff --git a/tests/test-webapps/test-proxy-webapp/pom.xml b/tests/test-webapps/test-proxy-webapp/pom.xml
index 5fcd1c21c30..884d0b1afab 100644
--- a/tests/test-webapps/test-proxy-webapp/pom.xml
+++ b/tests/test-webapps/test-proxy-webapp/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT../pom.xml4.0.0
diff --git a/tests/test-webapps/test-servlet-spec/pom.xml b/tests/test-webapps/test-servlet-spec/pom.xml
index 26512ed5941..2dec350ab6c 100644
--- a/tests/test-webapps/test-servlet-spec/pom.xml
+++ b/tests/test-webapps/test-servlet-spec/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtest-servlet-spec-parentJetty Tests :: Spec Test WebApp :: Parent
diff --git a/tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml b/tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml
index 7b9b8cbc1e5..efd55e56e25 100644
--- a/tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml
+++ b/tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-servlet-spec-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtest-container-initializerjar
diff --git a/tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml b/tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml
index 5b401b80488..71aa95c4066 100644
--- a/tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml
+++ b/tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-servlet-spec-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTJetty Tests :: Webapps :: Spec Webapptest-spec-webapp
diff --git a/tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml b/tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml
index 3e38da60b57..a225644e50c 100644
--- a/tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml
+++ b/tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-servlet-spec-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTJetty Tests :: WebApp :: Servlet Spec :: Fragment Jar
diff --git a/tests/test-webapps/test-simple-webapp/pom.xml b/tests/test-webapps/test-simple-webapp/pom.xml
index f815056dcae..ddea1b73710 100644
--- a/tests/test-webapps/test-simple-webapp/pom.xml
+++ b/tests/test-webapps/test-simple-webapp/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtest-simple-webapp
diff --git a/tests/test-webapps/test-webapp-rfc2616/pom.xml b/tests/test-webapps/test-webapp-rfc2616/pom.xml
index 571534ccedc..11f24e990bd 100644
--- a/tests/test-webapps/test-webapp-rfc2616/pom.xml
+++ b/tests/test-webapps/test-webapp-rfc2616/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOTtest-webapp-rfc2616Jetty Tests :: WebApp :: RFC2616
diff --git a/tests/test-webapps/test-weld-cdi-webapp/pom.xml b/tests/test-webapps/test-weld-cdi-webapp/pom.xml
index c31107d0259..32013b4fe89 100644
--- a/tests/test-webapps/test-weld-cdi-webapp/pom.xml
+++ b/tests/test-webapps/test-weld-cdi-webapp/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.jetty.teststest-webapps-parent
- 9.4.28.v20200408
+ 9.4.29-SNAPSHOT4.0.0
From 0db9210f17cc729ed625ef5ecf59483f55cbfcf9 Mon Sep 17 00:00:00 2001
From: Jesse McConnell
Date: Thu, 9 Apr 2020 10:31:38 -0500
Subject: [PATCH 065/101] update to released web resources artifact
---
jetty-documentation/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/jetty-documentation/pom.xml b/jetty-documentation/pom.xml
index acfb503753f..4e601a707ae 100644
--- a/jetty-documentation/pom.xml
+++ b/jetty-documentation/pom.xml
@@ -41,7 +41,7 @@
1.5.0-alpha.8.11.5.8.11.7.27
- 1.1
+ 1.2${project.build.directory}/web-resources${project.build.directory}/current
From c512f41488a9085cc472745116312db2d37de393 Mon Sep 17 00:00:00 2001
From: Jesse McConnell
Date: Thu, 9 Apr 2020 10:32:25 -0500
Subject: [PATCH 066/101] set render styles
---
.../main/asciidoc/contribution-guide/index.adoc | 13 +++++--------
.../main/asciidoc/distribution-guide/index.adoc | 13 +++++--------
.../src/main/asciidoc/embedded-guide/index.adoc | 14 ++++++--------
.../main/asciidoc/quickstart-guide/index.adoc | 17 ++++++-----------
4 files changed, 22 insertions(+), 35 deletions(-)
diff --git a/jetty-documentation/src/main/asciidoc/contribution-guide/index.adoc b/jetty-documentation/src/main/asciidoc/contribution-guide/index.adoc
index 98e27818725..5543d145f28 100644
--- a/jetty-documentation/src/main/asciidoc/contribution-guide/index.adoc
+++ b/jetty-documentation/src/main/asciidoc/contribution-guide/index.adoc
@@ -23,8 +23,11 @@
:revdate: {TIMESTAMP}
:toc: left
:toc-title: Contribution Guide
-:toc-image: ../../common/images/jetty-logo.svg
-:toc-image-url: /jetty/index.html
+:toc-style:
+
+:header-style: eclipse-thin
+:breadcrumb-style: eclipse-thin
+:footer-style: default
:breadcrumb: Home:../index.html | Contribution Guide:./index.html
// docinfo lets you pull in shared content and/or influence via render type
@@ -43,12 +46,6 @@ endif::[]
// options for special blocks, code snippets, screen, etc
:sub-order: attributes+
-// suppress document footer generation
-//:nofooter:
-
-// suppress Eclipse footer
-:no-eclipse-footer:
-
// uncomment to allow include::https:// style content inclusion
//:allow-uri-read: true
diff --git a/jetty-documentation/src/main/asciidoc/distribution-guide/index.adoc b/jetty-documentation/src/main/asciidoc/distribution-guide/index.adoc
index 20a8c1a9022..130c16ce985 100644
--- a/jetty-documentation/src/main/asciidoc/distribution-guide/index.adoc
+++ b/jetty-documentation/src/main/asciidoc/distribution-guide/index.adoc
@@ -23,8 +23,11 @@
:revdate: {TIMESTAMP}
:toc: left
:toc-title: Distribution Guide
-:toc-image: ../../common/images/jetty-logo.svg
-:toc-image-url: /jetty/index.html
+:toc-style:
+
+:header-style: eclipse-thin
+:breadcrumb-style: eclipse-thin
+:footer-style: default
:breadcrumb: Home:../index.html | Distribution Guide:./index.html
// docinfo lets you pull in shared content and/or influence via render type
@@ -43,12 +46,6 @@ endif::[]
// options for special blocks, code snippets, screen, etc
:sub-order: attributes+
-// suppress document footer generation
-//:nofooter:
-
-// suppress Eclipse footer
-:no-eclipse-footer:
-
// uncomment to allow include::https:// style content inclusion
//:allow-uri-read: true
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/index.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/index.adoc
index 1dfc4bdaa93..6754f278b87 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/index.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/index.adoc
@@ -23,8 +23,12 @@
:revdate: {TIMESTAMP}
:toc: left
:toc-title: Embedded Guide
-:toc-image: ../../common/images/jetty-logo.svg
-:toc-image-url: /jetty/index.html
+:toc-style:
+
+:header-style: eclipse-thin
+:breadcrumb-style: eclipse-thin
+:footer-style: default
+
:breadcrumb: Home:../index.html | Embedded Guide:./index.html
// docinfo lets you pull in shared content and/or influence via render type
@@ -43,12 +47,6 @@ endif::[]
// options for special blocks, code snippets, screen, etc
:sub-order: attributes+
-// suppress document footer generation
-//:nofooter:
-
-// suppress Eclipse footer
-:no-eclipse-footer:
-
// uncomment to allow include::https:// style content inclusion
//:allow-uri-read: true
diff --git a/jetty-documentation/src/main/asciidoc/quickstart-guide/index.adoc b/jetty-documentation/src/main/asciidoc/quickstart-guide/index.adoc
index a3a369ae02d..0cbb0dd608c 100644
--- a/jetty-documentation/src/main/asciidoc/quickstart-guide/index.adoc
+++ b/jetty-documentation/src/main/asciidoc/quickstart-guide/index.adoc
@@ -23,15 +23,14 @@
:revdate: {TIMESTAMP}
:toc: left
:toc-title: Quickstart Guide
-:toc-image: ../../common/images/jetty-logo.svg
-:toc-image-url: /jetty/index.html
+:toc-style:
+
+:header-style: eclipse-thin
+:breadcrumb-style: eclipse-thin
+:footer-style: default
+
:breadcrumb: Home:../index.html | Quickstart Guide:./index.html
-
-// docinfo lets you pull in shared content and/or influence via render type
-//:docinfodir: {DOCINFODIR}/documentation
-//:docinfo1:
-
// html specific directives
ifdef::backend-html5[]
:safe-mode-unsafe:
@@ -44,10 +43,6 @@ endif::[]
// options for special blocks, code snippets, screen, etc
:sub-order: attributes+
-// suppress document footer generation
-//:nofooter:
-:no-eclipse-footer:
-
// uncomment to allow include::https:// style content inclusion
//:allow-uri-read: true
From d2844bb102f76b0d12c5c0663105617d04eb34e1 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Wed, 8 Apr 2020 12:51:44 +0200
Subject: [PATCH 067/101] Improvements to the Jetty server documentation.adoc
Moved old documentation to old_docs/.
Signed-off-by: Simone Bordet
---
.../jetty/embedded/SimplestServer.java | 1 +
jetty-documentation/pom.xml | 24 +-
.../embedded-guide/client/client-io-arch.adoc | 8 +-
.../main/asciidoc/embedded-guide/io-arch.adoc | 14 +-
.../{server => old_docs}/ant/chapter.adoc | 0
.../{server => old_docs}/ant/jetty-ant.adoc | 0
.../architecture/1xx-responses.adoc | 0
.../architecture/basic-architecture.adoc | 0
.../architecture/chapter.adoc | 0
.../architecture/jetty-classloading.adoc | 0
.../server-side-architecture.adoc | 0
.../contributing/bugs.adoc | 0
.../contributing/chapter.adoc | 0
.../contributing/coding-standards.adoc | 0
.../contributing/community.adoc | 0
.../contributing/documentation.adoc | 0
.../contributing/patches.adoc | 0
.../contributing/release-testing.adoc | 0
.../contributing/releasing-jetty.adoc | 0
.../contributing/security.adoc | 0
.../contributing/source-build.adoc | 0
.../debugging/chapter.adoc | 0
.../debugging/debugging-with-eclipse.adoc | 0
.../debugging/debugging-with-intellij.adoc | 0
.../debugging/enable-remote-debugging.adoc | 0
.../embedding/chapter.adoc | 0
.../embedding/embedded-examples.adoc | 0
.../embedding/embedding-jetty.adoc | 0
.../examples/embedded-file-server.adoc | 0
.../examples/embedded-many-connectors.adoc | 0
.../examples/embedded-minimal-servlet.adoc | 0
.../examples/embedded-one-webapp.adoc | 0
.../embedded-secured-hello-handler.adoc | 0
.../examples/embedded-split-file-server.adoc | 0
.../embedding/jetty-helloworld.adoc | 0
.../{server => old_docs}/faq/chapter.adoc | 0
.../frameworks/chapter.adoc | 0
.../frameworks/metro.adoc | 0
.../{server => old_docs}/frameworks/osgi.adoc | 0
.../frameworks/spring-usage.adoc | 0
.../{server => old_docs}/frameworks/weld.adoc | 0
.../handlers/chapter.adoc | 0
.../handlers/writing-custom-handlers.adoc | 0
.../images/basic-architecture-handlers.png | Bin
.../basic-architecture-nested-handlers.png | Bin
.../images/basic-architecture-patterns.png | Bin
.../basic-architecture-servlet-handler.png | Bin
.../basic-architecture-web-application.png | Bin
.../images/debug-eclipse-1.png | Bin
.../images/debug-eclipse-2.png | Bin
.../images/debug-eclipse-3.png | Bin
.../images/intellij_debug_view.png | Bin
.../images/intellij_new_remote_config.png | Bin
.../images/intellij_select_debug.png | Bin
.../images/intellij_set_breakpoint.png | Bin
.../images/jetty-high-level-architecture.png | Bin
.../jetty-xml/chapter.adoc | 0
.../jetty-xml/jetty-env-xml.adoc | 0
.../jetty-xml/jetty-web-xml-config.adoc | 0
.../jetty-xml/jetty-xml-config.adoc | 0
.../jetty-xml/jetty-xml-syntax.adoc | 0
.../jetty-xml/jetty-xml-usage.adoc | 0
.../jetty-xml/override-web-xml.adoc | 0
.../jetty-xml/webdefault-xml.adoc | 0
.../{server => old_docs}/maven/chapter.adoc | 0
.../maven/jetty-jspc-maven-plugin.adoc | 0
.../maven/jetty-maven-helloworld.adoc | 0
.../maven/jetty-maven-plugin.adoc | 0
.../maven/jetty-maven-scanning.adoc | 0
.../platforms/chapter.adoc | 0
.../platforms/cloudfoundry.adoc | 0
.../platforms/elastic-beanstalk.adoc | 0
.../platforms/fedora.adoc | 0
.../platforms/jelastic.adoc | 0
.../platforms/ubuntu.adoc | 0
.../embedded-guide/old_docs/server.adoc | 36 ++
.../troubleshooting/chapter.adoc | 0
.../preventing-memory-leaks.adoc | 0
.../troubleshooting/security-reports.adoc | 0
.../troubleshooting/slow-deployment.adoc | 0
.../troubleshooting-locked-files.adoc | 0
.../troubleshooting-zip-exceptions.adoc | 0
.../troubleshooting/watchservice.adoc | 0
.../websockets/intro/chapter.adoc | 0
.../websockets/java/chapter.adoc | 0
.../java/java-websocket-client-api.adoc | 0
.../java/java-websocket-server-api.adoc | 0
.../websockets/jetty/chapter.adoc | 0
.../jetty/jetty-websocket-api-adapter.adoc | 0
.../jetty-websocket-api-annotations.adoc | 0
.../jetty/jetty-websocket-api-events.adoc | 0
.../jetty/jetty-websocket-api-listener.adoc | 0
.../jetty-websocket-api-send-message.adoc | 0
.../jetty/jetty-websocket-api-session.adoc | 0
.../websockets/jetty/jetty-websocket-api.adoc | 0
.../jetty/jetty-websocket-client-api.adoc | 0
.../jetty/jetty-websocket-server-api.adoc | 0
.../server/http/server-http.adoc | 297 +++++++++++++++++
.../embedded-guide/server/server-io-arch.adoc | 36 ++
.../embedded-guide/server/server.adoc | 49 ++-
.../embedded/server/http/HTTPServerDocs.java | 314 ++++++++++++++++++
101 files changed, 749 insertions(+), 30 deletions(-)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/ant/chapter.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/ant/jetty-ant.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/architecture/1xx-responses.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/architecture/basic-architecture.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/architecture/chapter.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/architecture/jetty-classloading.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/architecture/server-side-architecture.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/contributing/bugs.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/contributing/chapter.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/contributing/coding-standards.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/contributing/community.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/contributing/documentation.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/contributing/patches.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/contributing/release-testing.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/contributing/releasing-jetty.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/contributing/security.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/contributing/source-build.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/debugging/chapter.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/debugging/debugging-with-eclipse.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/debugging/debugging-with-intellij.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/debugging/enable-remote-debugging.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/embedding/chapter.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/embedding/embedded-examples.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/embedding/embedding-jetty.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/embedding/examples/embedded-file-server.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/embedding/examples/embedded-many-connectors.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/embedding/examples/embedded-minimal-servlet.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/embedding/examples/embedded-one-webapp.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/embedding/examples/embedded-secured-hello-handler.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/embedding/examples/embedded-split-file-server.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/embedding/jetty-helloworld.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/faq/chapter.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/frameworks/chapter.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/frameworks/metro.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/frameworks/osgi.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/frameworks/spring-usage.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/frameworks/weld.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/handlers/chapter.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/handlers/writing-custom-handlers.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/images/basic-architecture-handlers.png (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/images/basic-architecture-nested-handlers.png (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/images/basic-architecture-patterns.png (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/images/basic-architecture-servlet-handler.png (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/images/basic-architecture-web-application.png (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/images/debug-eclipse-1.png (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/images/debug-eclipse-2.png (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/images/debug-eclipse-3.png (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/images/intellij_debug_view.png (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/images/intellij_new_remote_config.png (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/images/intellij_select_debug.png (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/images/intellij_set_breakpoint.png (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/images/jetty-high-level-architecture.png (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/jetty-xml/chapter.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/jetty-xml/jetty-env-xml.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/jetty-xml/jetty-web-xml-config.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/jetty-xml/jetty-xml-config.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/jetty-xml/jetty-xml-syntax.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/jetty-xml/jetty-xml-usage.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/jetty-xml/override-web-xml.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/jetty-xml/webdefault-xml.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/maven/chapter.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/maven/jetty-jspc-maven-plugin.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/maven/jetty-maven-helloworld.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/maven/jetty-maven-plugin.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/maven/jetty-maven-scanning.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/platforms/chapter.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/platforms/cloudfoundry.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/platforms/elastic-beanstalk.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/platforms/fedora.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/platforms/jelastic.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/platforms/ubuntu.adoc (100%)
create mode 100644 jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/server.adoc
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/troubleshooting/chapter.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/troubleshooting/preventing-memory-leaks.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/troubleshooting/security-reports.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/troubleshooting/slow-deployment.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/troubleshooting/troubleshooting-locked-files.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/troubleshooting/troubleshooting-zip-exceptions.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/troubleshooting/watchservice.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/websockets/intro/chapter.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/websockets/java/chapter.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/websockets/java/java-websocket-client-api.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/websockets/java/java-websocket-server-api.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/websockets/jetty/chapter.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/websockets/jetty/jetty-websocket-api-adapter.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/websockets/jetty/jetty-websocket-api-annotations.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/websockets/jetty/jetty-websocket-api-events.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/websockets/jetty/jetty-websocket-api-listener.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/websockets/jetty/jetty-websocket-api-send-message.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/websockets/jetty/jetty-websocket-api-session.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/websockets/jetty/jetty-websocket-api.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/websockets/jetty/jetty-websocket-client-api.adoc (100%)
rename jetty-documentation/src/main/asciidoc/embedded-guide/{server => old_docs}/websockets/jetty/jetty-websocket-server-api.adoc (100%)
create mode 100644 jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http.adoc
create mode 100644 jetty-documentation/src/main/asciidoc/embedded-guide/server/server-io-arch.adoc
create mode 100644 jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SimplestServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SimplestServer.java
index bb3c4a6641b..74f3aa0cb14 100644
--- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SimplestServer.java
+++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SimplestServer.java
@@ -23,6 +23,7 @@ import org.eclipse.jetty.server.Server;
/**
* The simplest possible Jetty server.
*/
+// TODO: remove this class, only used in documentation.
public class SimplestServer
{
public static Server createServer(int port)
diff --git a/jetty-documentation/pom.xml b/jetty-documentation/pom.xml
index 5c723d17eeb..e318d4a57b1 100644
--- a/jetty-documentation/pom.xml
+++ b/jetty-documentation/pom.xml
@@ -55,12 +55,27 @@
org.eclipse.jetty
- jetty-client
+ jetty-server
+ ${project.version}
+
+
+ org.eclipse.jetty
+ jetty-servlet
+ ${project.version}
+
+
+ org.eclipse.jetty
+ jetty-alpn-server${project.version}org.eclipse.jetty.http2
- http2-http-client-transport
+ http2-server
+ ${project.version}
+
+
+ org.eclipse.jetty
+ jetty-client${project.version}
@@ -68,6 +83,11 @@
fcgi-client${project.version}
+
+ org.eclipse.jetty.http2
+ http2-http-client-transport
+ ${project.version}
+
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/client-io-arch.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/client-io-arch.adoc
index 83e59d0ed7a..8b11d289e06 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/client-io-arch.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/client-io-arch.adoc
@@ -17,12 +17,12 @@
//
[[eg-client-io-arch]]
-=== Client Libraries Architecture
+=== Client Libraries I/O Architecture
The Jetty client libraries provide the basic components and APIs to implement
a network client.
-They build on the common link:#io-arch[Jetty I/O Architecture] and provide client
+They build on the common xref:eg-io-arch[Jetty I/O Architecture] and provide client
specific concepts (such as establishing a connection to a server).
There are conceptually two layers that compose the Jetty client libraries:
@@ -37,7 +37,7 @@ network.
==== Client Libraries Network Layer
The Jetty client libraries use the common I/O design described in
-link:#io-arch[this section].
+link:#eg-io-arch[this section].
The main client-side component is the
link:{JDURL}/org/eclipse/jetty/io/ClientConnector.html[`ClientConnector`].
@@ -122,7 +122,7 @@ The protocol layer builds on top of the network layer to generate the
bytes to be written to the network and to parse the bytes read from the
network.
-Recall from link:#io-arch-connection[this section] that Jetty uses the
+Recall from link:#eg-io-arch-connection[this section] that Jetty uses the
`Connection` abstraction to produce and interpret the network bytes.
On the client side, a `ClientConnectionFactory` implementation is the
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc
index 178e04eaaa0..3d11dc73008 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/io-arch.adoc
@@ -17,13 +17,13 @@
//
[appendix]
-[[io-arch]]
+[[eg-io-arch]]
== Jetty I/O Architecture
Jetty libraries (both client and server) use Java NIO to handle I/O, so that
at its core Jetty I/O is completely non-blocking.
-[[io-arch-selector-manager]]
+[[eg-io-arch-selector-manager]]
=== Jetty I/O: `SelectorManager`
The core class of Jetty I/O is
@@ -62,7 +62,7 @@ This example shows how a server accepts a client connection:
include::{doc_code}/embedded/SelectorManagerDocs.java[tags=accept]
----
-[[io-arch-endpoint-connection]]
+[[eg-io-arch-endpoint-connection]]
=== Jetty I/O: `EndPoint` and `Connection`
``SocketChannel``s that are passed to `SelectorManager` are wrapped into two
@@ -138,7 +138,7 @@ subclasses.
NOTE: TODO: add a link to a client-side specific architecture section
-[[io-arch-endpoint]]
+[[eg-io-arch-endpoint]]
=== Jetty I/O: `EndPoint`
The Jetty I/O library use Java NIO to handle I/O, so that I/O is non-blocking.
@@ -162,9 +162,9 @@ fully written, even if they are delayed by TCP congestion/uncongestion).
The `EndPoint` APIs abstract out the Java NIO details by providing non-blocking
APIs based on `Callback` objects for I/O operations.
The `EndPoint` APIs are typically called by `Connection` implementations, see
-link:#io-arch-connection[this section].
+xref:eg-io-arch-connection[this section].
-[[io-arch-connection]]
+[[eg-io-arch-connection]]
=== Jetty I/O: `Connection`
`Connection` is the abstraction that deserializes incoming bytes into objects,
@@ -196,7 +196,7 @@ extends `AbstractConnection`:
include::{doc_code}/embedded/SelectorManagerDocs.java[tags=connection]
----
-[[io-arch-echo]]
+[[eg-io-arch-echo]]
=== Jetty I/O: Network Echo
With the concepts above it is now possible to write a simple, fully non-blocking,
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/ant/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/ant/chapter.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/ant/chapter.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/ant/chapter.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/ant/jetty-ant.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/ant/jetty-ant.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/ant/jetty-ant.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/ant/jetty-ant.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/architecture/1xx-responses.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/1xx-responses.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/architecture/1xx-responses.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/1xx-responses.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/architecture/basic-architecture.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/basic-architecture.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/architecture/basic-architecture.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/basic-architecture.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/architecture/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/chapter.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/architecture/chapter.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/chapter.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/architecture/jetty-classloading.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/jetty-classloading.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/architecture/jetty-classloading.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/jetty-classloading.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/architecture/server-side-architecture.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/server-side-architecture.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/architecture/server-side-architecture.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/server-side-architecture.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/bugs.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/bugs.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/bugs.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/bugs.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/chapter.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/chapter.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/chapter.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/coding-standards.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/coding-standards.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/coding-standards.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/coding-standards.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/community.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/community.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/community.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/community.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/documentation.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/documentation.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/documentation.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/documentation.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/patches.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/patches.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/patches.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/patches.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/release-testing.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/release-testing.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/release-testing.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/release-testing.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/releasing-jetty.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/releasing-jetty.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/releasing-jetty.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/releasing-jetty.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/security.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/security.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/security.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/security.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/source-build.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/source-build.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/contributing/source-build.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/contributing/source-build.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/debugging/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/debugging/chapter.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/debugging/chapter.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/debugging/chapter.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/debugging/debugging-with-eclipse.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/debugging/debugging-with-eclipse.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/debugging/debugging-with-eclipse.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/debugging/debugging-with-eclipse.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/debugging/debugging-with-intellij.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/debugging/debugging-with-intellij.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/debugging/debugging-with-intellij.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/debugging/debugging-with-intellij.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/debugging/enable-remote-debugging.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/debugging/enable-remote-debugging.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/debugging/enable-remote-debugging.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/debugging/enable-remote-debugging.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/chapter.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/chapter.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/chapter.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/embedded-examples.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/embedded-examples.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/embedded-examples.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/embedded-examples.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/embedding-jetty.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/embedding-jetty.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/embedding-jetty.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/embedding-jetty.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-file-server.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/examples/embedded-file-server.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-file-server.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/examples/embedded-file-server.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-many-connectors.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/examples/embedded-many-connectors.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-many-connectors.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/examples/embedded-many-connectors.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-minimal-servlet.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/examples/embedded-minimal-servlet.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-minimal-servlet.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/examples/embedded-minimal-servlet.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-one-webapp.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/examples/embedded-one-webapp.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-one-webapp.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/examples/embedded-one-webapp.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-secured-hello-handler.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/examples/embedded-secured-hello-handler.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-secured-hello-handler.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/examples/embedded-secured-hello-handler.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-split-file-server.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/examples/embedded-split-file-server.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/examples/embedded-split-file-server.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/examples/embedded-split-file-server.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/jetty-helloworld.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/jetty-helloworld.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/embedding/jetty-helloworld.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/embedding/jetty-helloworld.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/faq/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/faq/chapter.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/faq/chapter.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/faq/chapter.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/frameworks/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/chapter.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/frameworks/chapter.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/chapter.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/frameworks/metro.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/metro.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/frameworks/metro.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/metro.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/frameworks/osgi.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/osgi.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/frameworks/osgi.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/osgi.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/frameworks/spring-usage.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/spring-usage.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/frameworks/spring-usage.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/spring-usage.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/frameworks/weld.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/weld.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/frameworks/weld.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/weld.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/handlers/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/handlers/chapter.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/handlers/chapter.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/handlers/chapter.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/handlers/writing-custom-handlers.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/handlers/writing-custom-handlers.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/handlers/writing-custom-handlers.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/handlers/writing-custom-handlers.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/images/basic-architecture-handlers.png b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/basic-architecture-handlers.png
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/images/basic-architecture-handlers.png
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/basic-architecture-handlers.png
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/images/basic-architecture-nested-handlers.png b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/basic-architecture-nested-handlers.png
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/images/basic-architecture-nested-handlers.png
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/basic-architecture-nested-handlers.png
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/images/basic-architecture-patterns.png b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/basic-architecture-patterns.png
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/images/basic-architecture-patterns.png
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/basic-architecture-patterns.png
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/images/basic-architecture-servlet-handler.png b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/basic-architecture-servlet-handler.png
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/images/basic-architecture-servlet-handler.png
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/basic-architecture-servlet-handler.png
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/images/basic-architecture-web-application.png b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/basic-architecture-web-application.png
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/images/basic-architecture-web-application.png
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/basic-architecture-web-application.png
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/images/debug-eclipse-1.png b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/debug-eclipse-1.png
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/images/debug-eclipse-1.png
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/debug-eclipse-1.png
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/images/debug-eclipse-2.png b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/debug-eclipse-2.png
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/images/debug-eclipse-2.png
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/debug-eclipse-2.png
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/images/debug-eclipse-3.png b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/debug-eclipse-3.png
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/images/debug-eclipse-3.png
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/debug-eclipse-3.png
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/images/intellij_debug_view.png b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/intellij_debug_view.png
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/images/intellij_debug_view.png
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/intellij_debug_view.png
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/images/intellij_new_remote_config.png b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/intellij_new_remote_config.png
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/images/intellij_new_remote_config.png
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/intellij_new_remote_config.png
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/images/intellij_select_debug.png b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/intellij_select_debug.png
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/images/intellij_select_debug.png
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/intellij_select_debug.png
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/images/intellij_set_breakpoint.png b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/intellij_set_breakpoint.png
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/images/intellij_set_breakpoint.png
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/intellij_set_breakpoint.png
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/images/jetty-high-level-architecture.png b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/jetty-high-level-architecture.png
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/images/jetty-high-level-architecture.png
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/images/jetty-high-level-architecture.png
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/jetty-xml/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/jetty-xml/chapter.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/jetty-xml/chapter.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/jetty-xml/chapter.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/jetty-xml/jetty-env-xml.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/jetty-xml/jetty-env-xml.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/jetty-xml/jetty-env-xml.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/jetty-xml/jetty-env-xml.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/jetty-xml/jetty-web-xml-config.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/jetty-xml/jetty-web-xml-config.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/jetty-xml/jetty-web-xml-config.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/jetty-xml/jetty-web-xml-config.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/jetty-xml/jetty-xml-config.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/jetty-xml/jetty-xml-config.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/jetty-xml/jetty-xml-config.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/jetty-xml/jetty-xml-config.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/jetty-xml/jetty-xml-syntax.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/jetty-xml/jetty-xml-syntax.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/jetty-xml/jetty-xml-syntax.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/jetty-xml/jetty-xml-syntax.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/jetty-xml/jetty-xml-usage.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/jetty-xml/jetty-xml-usage.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/jetty-xml/jetty-xml-usage.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/jetty-xml/jetty-xml-usage.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/jetty-xml/override-web-xml.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/jetty-xml/override-web-xml.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/jetty-xml/override-web-xml.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/jetty-xml/override-web-xml.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/jetty-xml/webdefault-xml.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/jetty-xml/webdefault-xml.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/jetty-xml/webdefault-xml.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/jetty-xml/webdefault-xml.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/maven/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/maven/chapter.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/maven/chapter.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/maven/chapter.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/maven/jetty-jspc-maven-plugin.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/maven/jetty-jspc-maven-plugin.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/maven/jetty-jspc-maven-plugin.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/maven/jetty-jspc-maven-plugin.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/maven/jetty-maven-helloworld.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/maven/jetty-maven-helloworld.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/maven/jetty-maven-helloworld.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/maven/jetty-maven-helloworld.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/maven/jetty-maven-plugin.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/maven/jetty-maven-plugin.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/maven/jetty-maven-plugin.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/maven/jetty-maven-plugin.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/maven/jetty-maven-scanning.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/maven/jetty-maven-scanning.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/maven/jetty-maven-scanning.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/maven/jetty-maven-scanning.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/platforms/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/platforms/chapter.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/platforms/chapter.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/platforms/chapter.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/platforms/cloudfoundry.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/platforms/cloudfoundry.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/platforms/cloudfoundry.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/platforms/cloudfoundry.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/platforms/elastic-beanstalk.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/platforms/elastic-beanstalk.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/platforms/elastic-beanstalk.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/platforms/elastic-beanstalk.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/platforms/fedora.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/platforms/fedora.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/platforms/fedora.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/platforms/fedora.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/platforms/jelastic.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/platforms/jelastic.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/platforms/jelastic.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/platforms/jelastic.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/platforms/ubuntu.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/platforms/ubuntu.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/platforms/ubuntu.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/platforms/ubuntu.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/server.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/server.adoc
new file mode 100644
index 00000000000..82d815d7d1b
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/server.adoc
@@ -0,0 +1,36 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+[[server]]
+== Jetty Server Libraries
+
+include::embedding/chapter.adoc[]
+include::maven/chapter.adoc[]
+include::clients/http/chapter.adoc[]
+include::handlers/chapter.adoc[]
+include::websockets/intro/chapter.adoc[]
+include::websockets/jetty/chapter.adoc[]
+include::ant/chapter.adoc[]
+include::frameworks/chapter.adoc[]
+include::architecture/chapter.adoc[]
+include::platforms/chapter.adoc[]
+include::jetty-xml/chapter.adoc[]
+include::troubleshooting/chapter.adoc[]
+include::debugging/chapter.adoc[]
+//include::contributing/chapter.adoc[]
+include::upgrading/chapter.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/troubleshooting/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/troubleshooting/chapter.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/troubleshooting/chapter.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/troubleshooting/chapter.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/troubleshooting/preventing-memory-leaks.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/troubleshooting/preventing-memory-leaks.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/troubleshooting/preventing-memory-leaks.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/troubleshooting/preventing-memory-leaks.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/troubleshooting/security-reports.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/troubleshooting/security-reports.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/troubleshooting/security-reports.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/troubleshooting/security-reports.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/troubleshooting/slow-deployment.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/troubleshooting/slow-deployment.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/troubleshooting/slow-deployment.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/troubleshooting/slow-deployment.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/troubleshooting/troubleshooting-locked-files.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/troubleshooting/troubleshooting-locked-files.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/troubleshooting/troubleshooting-locked-files.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/troubleshooting/troubleshooting-locked-files.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/troubleshooting/troubleshooting-zip-exceptions.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/troubleshooting/troubleshooting-zip-exceptions.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/troubleshooting/troubleshooting-zip-exceptions.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/troubleshooting/troubleshooting-zip-exceptions.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/troubleshooting/watchservice.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/troubleshooting/watchservice.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/troubleshooting/watchservice.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/troubleshooting/watchservice.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/intro/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/intro/chapter.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/intro/chapter.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/intro/chapter.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/java/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/java/chapter.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/java/chapter.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/java/chapter.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/java/java-websocket-client-api.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/java/java-websocket-client-api.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/java/java-websocket-client-api.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/java/java-websocket-client-api.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/java/java-websocket-server-api.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/java/java-websocket-server-api.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/java/java-websocket-server-api.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/java/java-websocket-server-api.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/chapter.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/chapter.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/chapter.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-api-adapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-api-adapter.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-api-adapter.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-api-adapter.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-api-annotations.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-api-annotations.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-api-annotations.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-api-annotations.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-api-events.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-api-events.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-api-events.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-api-events.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-api-listener.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-api-listener.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-api-listener.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-api-listener.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-api-send-message.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-api-send-message.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-api-send-message.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-api-send-message.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-api-session.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-api-session.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-api-session.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-api-session.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-api.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-api.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-api.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-api.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-client-api.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-client-api.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-client-api.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-client-api.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-server-api.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-server-api.adoc
similarity index 100%
rename from jetty-documentation/src/main/asciidoc/embedded-guide/server/websockets/jetty/jetty-websocket-server-api.adoc
rename to jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/websockets/jetty/jetty-websocket-server-api.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http.adoc
new file mode 100644
index 00000000000..1b2a86adc43
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http.adoc
@@ -0,0 +1,297 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+[[eg-server-http]]
+=== HTTP Server Libraries
+
+The Eclipse Jetty Project has historically provided libraries to embed an HTTP
+server and a Servlet Container.
+
+An `org.eclipse.jetty.server.Server` instance is the central component that
+links together a collection of ``Connector``s and a collection of
+``Handler``s, with threads from a `ThreadPool` doing the work.
+
+[plantuml]
+----
+skinparam backgroundColor transparent
+skinparam monochrome true
+skinparam shadowing false
+skinparam padding 5
+
+scale 1.5
+
+hide members
+hide circle
+
+Server - ThreadPool
+Connectors - Server
+Server -- Handlers
+----
+
+The components that accept connections from clients are
+`org.eclipse.jetty.server.Connector` instances.
+
+When a Jetty server interprets the HTTP protocol (both HTTP/1.1 and HTTP/2),
+it uses `org.eclipse.jetty.server.Handler` instances to process incoming
+requests and eventually produce responses.
+
+A `Server` must be created, configured and started:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=simple]
+----
+
+The example above shows the simplest HTTP/1.1 server; it has no support for
+HTTP sessions, for HTTP authentication, or for any of the features required
+by the Servlet specification.
+
+All these features are provided by the Jetty Server Libraries and server
+applications only need to put the required components together to provide
+all the required features.
+// TODO: link to a place where we discuss the handlers in more details.
+
+[[eg-server-connector]]
+=== Server Connectors
+
+A `Connector` is the component that handles incoming requests from clients,
+and works in conjunction with `ConnectionFactory` instances.
+
+The primary implementation is `org.eclipse.jetty.server.ServerConnector`.
+`ServerConnector` uses a `java.nio.channels.ServerSocketChannel` to listen
+to a TCP port and to accept TCP connections.
+
+Since `ServerConnector` wraps a `ServerSocketChannel`, it can be configured
+in a similar way, for example the port to listen to, the network address
+to bind to, etc.:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=configureConnector]
+----
+
+The _acceptors_ are threads that compete to accept TCP connections on the
+listening port, typically only one.
+When a connection is accepted, `ServerConnector` wraps it and passes it to
+the xref:eg-io-arch-selector-manager[`SelectorManager`].
+Therefore there is a little moment where the acceptor thread is not accepting
+new connections because it is busy wrapping the just accepted one to pass it
+to the `SelectorManager`.
+Connections that are ready to be accepted but are not accepted yet are queued
+in a bounded queue (at the OS level) whose capacity can be configured with the
+`ServerConnector.acceptQueueSize` parameter.
+
+If your application must withstand a very high rate of connections opened,
+configuring more than one acceptor thread may be beneficial: when one acceptor
+thread accepts one connection, another acceptor thread can take over accepting
+connections.
+
+The _selectors_ are components that manage a set of connected sockets,
+implemented by xref:eg-io-arch-selector-manager[`ManagedSelector`].
+Each selector requires one thread and uses the Java NIO mechanism to
+efficiently handle the set of connected sockets.
+As a rule of thumb, a single selector can easily manage 1000-5000 sockets,
+although the number may vary greatly depending on the application.
+
+It is possible to configure more than one `ServerConnector`, each listening
+on different ports:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=configureConnectors]
+----
+
+[[eg-server-connector-protocol]]
+==== Configuring Protocols
+
+For each accepted TCP connection, `ServerConnector` asks a `ConnectionFactory`
+to create a `Connection` object that handles the network traffic on that TCP
+connection, parsing and generating bytes for a specific protocol (see
+xref:eg-io-arch[this section] for more details about `Connection` objects).
+
+A `ServerConnector` can be configured with one or more ``ConnectionFactory``s.
+If no `ConnectionFactory` is specified then `HttpConnectionFactory` is
+implicitly configured.
+
+[[eg-server-connector-protocol-http11]]
+===== Configuring HTTP/1.1
+
+`HttpConnectionFactory` creates `HttpConnection` objects that parse bytes
+and generate bytes for the HTTP/1.1 protocol.
+
+This is how you configure Jetty to support clear-text HTTP/1.1:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=http11]
+----
+
+Supporting encrypted HTTP/1.1 (that is, requests with the HTTPS scheme)
+is supported by configuring an `SslContextFactory` that has access to the
+keyStore containing the private server key and public server certificate,
+in this way:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=tlsHttp11]
+----
+
+[[eg-server-connector-protocol-proxy-http11]]
+===== Configuring Jetty behind a Load Balancer
+
+It is often the case that Jetty receives connections from a load balancer
+configured to distribute the load among many Jetty backend servers.
+
+From the Jetty point of view, all the connections arrive from the load
+balancer, rather than the real clients, but is possible to forward the real
+client IP address and port to the backend Jetty server using the
+link:https://www.haproxy.org/download/2.1/doc/proxy-protocol.txt[PROXY protocol].
+
+NOTE: The PROXY protocol is widely supported by load balancers such as
+link:http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#5.2-send-proxy[HAProxy]
+(via its `send-proxy` directive), or
+link:https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol[Nginx]
+(via its `proxy_protocol on` directive), and others.
+
+To support this case, Jetty can be configured in this way:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=proxyHTTP]
+----
+
+Note how the ``ConnectionFactory``s passed to `ServerConnector` are in order:
+first PROXY, then HTTP/1.1.
+Note also how the PROXY `ConnectionFactory` needs to know its _next_ protocol
+(in this example, HTTP/1.1).
+
+Each `ConnectionFactory` is asked to create a `Connection` object for each
+accepted TCP connection; the `Connection` objects will be chained together
+to handle the bytes, each for its own protocol.
+Therefore the `ProxyConnection` will handle the PROXY protocol bytes and
+`HttpConnection` will handle the HTTP/1.1 bytes producing a request object
+and response object that will be processed by ``Handler``s.
+
+[[eg-server-connector-protocol-http2]]
+===== Configuring HTTP/2
+
+It is well know that the HTTP ports are `80` (for clear-text HTTP) and `443`
+for encrypted HTTP.
+By using those ports, a client had _prior knowledge_ that the server would
+speak, respectively, the HTTP/1.x protocol and the TLS protocol (and, after
+decryption, the HTTP/1.x protocol).
+
+HTTP/2 was designed to be a smooth transition from HTTP/1.1 for users and
+as such the HTTP ports were not changed.
+However the HTTP/2 protocol is, on the wire, a binary protocol, completely
+different from HTTP/1.1.
+Therefore, with HTTP/2, clients that connect to port `80` may speak either
+HTTP/1.1 or HTTP/2, and the server must figure out which version of the HTTP
+protocol the client is speaking.
+
+Jetty can support both HTTP/1.1 and HTTP/2 on the same clear-text port by
+configuring both the HTTP/1.1 and the HTTP/2 ``ConnectionFactory``s:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=http11H2C]
+----
+
+Note how the ``ConnectionFactory``s passed to `ServerConnector` are in order:
+first HTTP/1.1, then HTTP/2.
+This is necessary to support both protocols on the same port: Jetty will
+start parsing the incoming bytes as HTTP/1.1, but then realize that they
+are HTTP/2 bytes and will therefore _upgrade_ from HTTP/1.1 to HTTP/2.
+
+This configuration is also typical when Jetty is installed in backend servers
+behind a load balancer that also takes care of offloading TLS.
+When Jetty is behind a load balancer, you can always prepend the PROXY
+protocol as described in
+xref:eg-server-connector-protocol-proxy-http11[this section].
+
+When using encrypted HTTP/2, the unencrypted protocol is negotiated by client
+and server using an extension to the TLS protocol called ALPN.
+
+Jetty supports ALPN and encrypted HTTP/2 with this configuration:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=tlsALPNHTTP]
+----
+
+[[eg-server-handler]]
+=== Server Handlers
+
+A `Handler` is the component that processes incoming HTTP requests and
+eventually produces HTTP responses.
+
+``Handler``s can be organized in different ways:
+
+* in a sequence, where ``Handler``s are invoked one after the other
+** `HandlerCollection` invokes _all_ ``Handler``s one after the other
+** `HandlerList` invokes ``Handlers``s until one calls `Request.setHandled(true)`
+to indicate that the request has been handled and no further `Handler` should
+be invoked.
+* nested, where one `Handler` invokes the next `Handler`
+** `HandlerWrapper` implements this behavior
+
+The `HandlerCollection` behavior (invoking _all_ handlers) is useful when
+for example the last `Handler` is a logging `Handler` that logs the the
+request(that may have been modified by previous handlers).
+
+The `HandlerList` behavior (invoking handlers up to the first that calls
+`Request.setHandled(true)`) is useful when different handlers process different
+URIs or different virtual hosts: invoke one after the other until one matches
+the URI or virtual host.
+
+The nested behavior is useful to enrich the request with additional services
+such as HTTP session support (`SessionHandler`), or with specific behaviors
+dictated by the Servlet specification (`ServletHandler`).
+
+``Handler``s can be organized in a tree by composing them together:
+
+[plantuml]
+----
+skinparam backgroundColor transparent
+skinparam monochrome true
+skinparam shadowing false
+skinparam padding 5
+
+scale 1.5
+
+hide members
+hide circle
+
+HandlerCollection -- HandlerList
+HandlerCollection -- LoggingHandler
+HandlerList -- App1Handler
+HandlerList -- App2Handler
+App2Handler -- ServletHandler
+----
+
+In code it looks like this:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=tree]
+----
+
+// TODO: old docs introduces briefly ServletHandler but I think it deserves its own section
+
+// TODO: old docs introduce ContextHandler here and WebAppContext
+
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/server-io-arch.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/server-io-arch.adoc
new file mode 100644
index 00000000000..2efff583192
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/server-io-arch.adoc
@@ -0,0 +1,36 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+[[eg-server-io-arch]]
+=== Server Libraries I/O Architecture
+
+The Jetty server libraries provide the basic components and APIs to implement
+a network server.
+
+They build on the common xref:eg-io-arch[Jetty I/O Architecture] and provide server
+specific concepts.
+
+The main I/O server-side class is `org.eclipse.jetty.server.ServerConnector`.
+
+A `ServerConnector` manages a list of factories.
+
+// TODO: here we want to be generic and talk about writing a custom factory
+// without the use of Handler. Existing factories we document elsewhere.
+
+// TODO
+include::../old_docs/architecture/server-side-architecture.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/server.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/server.adoc
index 82d815d7d1b..a6dcc1feb45 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/server/server.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/server.adoc
@@ -16,21 +16,36 @@
// ========================================================================
//
-[[server]]
-== Jetty Server Libraries
+[[eg-server]]
+== Server Libraries
-include::embedding/chapter.adoc[]
-include::maven/chapter.adoc[]
-include::clients/http/chapter.adoc[]
-include::handlers/chapter.adoc[]
-include::websockets/intro/chapter.adoc[]
-include::websockets/jetty/chapter.adoc[]
-include::ant/chapter.adoc[]
-include::frameworks/chapter.adoc[]
-include::architecture/chapter.adoc[]
-include::platforms/chapter.adoc[]
-include::jetty-xml/chapter.adoc[]
-include::troubleshooting/chapter.adoc[]
-include::debugging/chapter.adoc[]
-//include::contributing/chapter.adoc[]
-include::upgrading/chapter.adoc[]
+The Eclipse Jetty Project provides server-side libraries
+that allow you to embed an HTTP or WebSocket server in your applications.
+A typical example is a HTTP server that needs to expose a REST endpoint.
+Another example is a proxy application that receives HTTP requests and
+forwards them to third party services possibly using also the Jetty
+xref:eg-client[client libraries].
+
+While historically Jetty is an HTTP server, it is possible to use the Jetty
+server-side libraries to write a generic network server that interprets
+any network protocol (not only HTTP).
+
+If you are interested in the low-level details of how the Eclipse Jetty
+server libraries work, or are interested in writing a custom protocol,
+look at the xref:eg-server-io-arch[Server I/O Architecture].
+
+The Jetty server-side libraries provide:
+
+* HTTP support for HTTP/1.0, HTTP/1.1, HTTP/2, clear-text or encrypted, for
+applications that want to embed Jetty as a generic HTTP server or proxy,
+via the xref:eg-server-http[HTTP libraries]
+* HTTP/2 low-level support, for applications that want to explicitly handle
+low-level HTTP/2 _sessions_, _streams_ and _frames_, via the
+xref:eg-server-http2[HTTP/2 libraries]
+* WebSocket support, for applications that want to embed a WebSocket server,
+via the xref:eg-server-websocket[WebSocket libraries]
+
+include::http/server-http.adoc[]
+include::http/server-http2.adoc[]
+include::http/server-websocket.adoc[]
+include::server-io-arch.adoc[]
diff --git a/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java b/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java
new file mode 100644
index 00000000000..1fdd72eafcd
--- /dev/null
+++ b/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java
@@ -0,0 +1,314 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package embedded.server.http;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.http.HttpCompliance;
+import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.ProxyConnectionFactory;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.handler.HandlerCollection;
+import org.eclipse.jetty.server.handler.HandlerList;
+import org.eclipse.jetty.server.handler.HandlerWrapper;
+import org.eclipse.jetty.servlet.ServletHandler;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+
+@SuppressWarnings("unused")
+public class HTTPServerDocs
+{
+ public void simple() throws Exception
+ {
+ // tag::simple[]
+ // Create and configure a ThreadPool.
+ QueuedThreadPool threadPool = new QueuedThreadPool();
+ threadPool.setName("server");
+
+ // Create a Server instance.
+ Server server = new Server(threadPool);
+
+ // Create a ServerConnector to accept connections from clients.
+ Connector connector = new ServerConnector(server);
+
+ // Add the Connector to the Server
+ server.addConnector(connector);
+
+ // Set a simple Handler to handle requests/responses.
+ server.setHandler(new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response)
+ {
+ // Mark the request as handled so that it
+ // will not be processed by other handlers.
+ jettyRequest.setHandled(true);
+ }
+ });
+
+ // Start the Server so it starts accepting connections from clients.
+ server.start();
+ // end::simple[]
+ }
+
+ public void configureConnector() throws Exception
+ {
+ // tag::configureConnector[]
+ Server server = new Server();
+
+ // The number of acceptor threads.
+ int acceptors = 1;
+
+ // The number of selectors.
+ int selectors = 1;
+
+ // Create a ServerConnector instance.
+ ServerConnector connector = new ServerConnector(server, 1, 1, new HttpConnectionFactory());
+
+ // Configure TCP parameters.
+
+ // The TCP port to listen to.
+ connector.setPort(8080);
+ // The TCP address to bind to.
+ connector.setHost("127.0.0.1");
+ // The TCP accept queue size.
+ connector.setAcceptQueueSize(128);
+
+ server.addConnector(connector);
+ server.start();
+ // end::configureConnector[]
+ }
+
+ public void configureConnectors() throws Exception
+ {
+ // tag::configureConnectors[]
+ Server server = new Server();
+
+ // Create a ServerConnector instance on port 8080.
+ ServerConnector connector1 = new ServerConnector(server, 1, 1, new HttpConnectionFactory());
+ connector1.setPort(8080);
+ server.addConnector(connector1);
+
+ // Create another ServerConnector instance on port 9090,
+ // for example with a different HTTP configuration.
+ HttpConfiguration httpConfig2 = new HttpConfiguration();
+ httpConfig2.setHttpCompliance(HttpCompliance.LEGACY);
+ ServerConnector connector2 = new ServerConnector(server, 1, 1, new HttpConnectionFactory(httpConfig2));
+ connector2.setPort(9090);
+ server.addConnector(connector2);
+
+ server.start();
+ // end::configureConnectors[]
+ }
+
+ public void http11() throws Exception
+ {
+ // tag::http11[]
+ Server server = new Server();
+
+ // The HTTP configuration object.
+ HttpConfiguration httpConfig = new HttpConfiguration();
+ // Configure the HTTP support, for example:
+ httpConfig.setSendServerVersion(false);
+
+ // The ConnectionFactory for HTTP/1.1.
+ HttpConnectionFactory http11 = new HttpConnectionFactory(httpConfig);
+
+ // Create the ServerConnector.
+ ServerConnector connector = new ServerConnector(server, http11);
+ connector.setPort(8080);
+
+ server.addConnector(connector);
+ server.start();
+ // end::http11[]
+ }
+
+ public void proxyHTTP() throws Exception
+ {
+ // tag::proxyHTTP[]
+ Server server = new Server();
+
+ // The HTTP configuration object.
+ HttpConfiguration httpConfig = new HttpConfiguration();
+ // Configure the HTTP support, for example:
+ httpConfig.setSendServerVersion(false);
+
+ // The ConnectionFactory for HTTP/1.1.
+ HttpConnectionFactory http11 = new HttpConnectionFactory(httpConfig);
+
+ // The ConnectionFactory for the PROXY protocol.
+ ProxyConnectionFactory proxy = new ProxyConnectionFactory(http11.getProtocol());
+
+ // Create the ServerConnector.
+ ServerConnector connector = new ServerConnector(server, proxy, http11);
+ connector.setPort(8080);
+
+ server.addConnector(connector);
+ server.start();
+ // end::proxyHTTP[]
+ }
+
+ public void tlsHttp11() throws Exception
+ {
+ // tag::tlsHttp11[]
+ Server server = new Server();
+
+ // The HTTP configuration object.
+ HttpConfiguration httpConfig = new HttpConfiguration();
+ // Add the SecureRequestCustomizer because we are using TLS.
+ httpConfig.addCustomizer(new SecureRequestCustomizer());
+
+ // The ConnectionFactory for HTTP/1.1.
+ HttpConnectionFactory http11 = new HttpConnectionFactory(httpConfig);
+
+ // Configure the SslContextFactory with the keyStore information.
+ SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
+ sslContextFactory.setKeyStorePath("/path/to/keystore");
+ sslContextFactory.setKeyStorePassword("secret");
+
+ // The ConnectionFactory for TLS.
+ SslConnectionFactory tls = new SslConnectionFactory(sslContextFactory, http11.getProtocol());
+
+ // The ServerConnector instance.
+ ServerConnector connector = new ServerConnector(server, tls, http11);
+ connector.setPort(8443);
+
+ server.addConnector(connector);
+ server.start();
+ // end::tlsHttp11[]
+ }
+
+ public void http11H2C() throws Exception
+ {
+ // tag::http11H2C[]
+ Server server = new Server();
+
+ // The HTTP configuration object.
+ HttpConfiguration httpConfig = new HttpConfiguration();
+
+ // The ConnectionFactory for HTTP/1.1.
+ HttpConnectionFactory http11 = new HttpConnectionFactory(httpConfig);
+
+ // The ConnectionFactory for clear-text HTTP/2.
+ HTTP2CServerConnectionFactory h2c = new HTTP2CServerConnectionFactory(httpConfig);
+
+ // The ServerConnector instance.
+ ServerConnector connector = new ServerConnector(server, http11, h2c);
+ connector.setPort(8080);
+
+ server.addConnector(connector);
+ server.start();
+ // end::http11H2C[]
+ }
+
+ public void tlsALPNHTTP() throws Exception
+ {
+ // tag::tlsALPNHTTP[]
+ Server server = new Server();
+
+ // The HTTP configuration object.
+ HttpConfiguration httpConfig = new HttpConfiguration();
+ // Add the SecureRequestCustomizer because we are using TLS.
+ httpConfig.addCustomizer(new SecureRequestCustomizer());
+
+ // The ConnectionFactory for HTTP/1.1.
+ HttpConnectionFactory http11 = new HttpConnectionFactory(httpConfig);
+
+ // The ConnectionFactory for HTTP/2.
+ HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpConfig);
+
+ // The ALPN ConnectionFactory.
+ ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
+ // The default protocol to use in case there is no negotiation.
+ alpn.setDefaultProtocol(http11.getProtocol());
+
+ // Configure the SslContextFactory with the keyStore information.
+ SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
+ sslContextFactory.setKeyStorePath("/path/to/keystore");
+ sslContextFactory.setKeyStorePassword("secret");
+
+ // The ConnectionFactory for TLS.
+ SslConnectionFactory tls = new SslConnectionFactory(sslContextFactory, alpn.getProtocol());
+
+ // The ServerConnector instance.
+ ServerConnector connector = new ServerConnector(server, tls, alpn, http11, h2);
+ connector.setPort(8443);
+
+ server.addConnector(connector);
+ server.start();
+ // end::tlsALPNHTTP[]
+ }
+
+ public void tree() throws Exception
+ {
+ class LoggingHandler extends AbstractHandler
+ {
+ @Override
+ public void handle(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response)
+ {
+ }
+ }
+
+ class App1Handler extends AbstractHandler
+ {
+ @Override
+ public void handle(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response)
+ {
+ }
+ }
+
+ class App2Handler extends HandlerWrapper
+ {
+ @Override
+ public void handle(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response)
+ {
+ }
+ }
+
+ // tag::tree[]
+ // Create a Server instance.
+ Server server = new Server();
+
+ HandlerCollection collection = new HandlerCollection();
+ // Link the root Handler with the Server.
+ server.setHandler(collection);
+
+ HandlerList list = new HandlerList();
+ collection.addHandler(list);
+ collection.addHandler(new LoggingHandler());
+
+ list.addHandler(new App1Handler());
+ App2Handler app2Handler = new App2Handler();
+ list.addHandler(app2Handler);
+
+ app2Handler.setHandler(new ServletHandler());
+ // end::tree[]
+ }
+}
From 35dce5f31510ce5a5d52010e76f624f3f0f46993 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Fri, 10 Apr 2020 15:40:02 +0200
Subject: [PATCH 068/101] Improvements to the Jetty server documentation.
Written connector and handler sections.
Signed-off-by: Simone Bordet
---
jetty-documentation/pom.xml | 41 +-
.../client/http/client-http-cookie.adoc | 6 +-
.../client/http/client-http-intro.adoc | 2 +-
.../client/http/client-http-transport.adoc | 4 +-
.../client/http2/client-http2.adoc | 4 +-
.../embedded-guide/old_docs/server.adoc | 2 +-
.../server/http/server-http-connector.adoc | 195 +++++++
.../http/server-http-handler-implement.adoc | 71 +++
.../server/http/server-http-handler-use.adoc | 464 +++++++++++++++++
.../server/http/server-http-handler.adoc | 103 ++++
.../server/http/server-http.adoc | 244 +--------
.../server/http2/server-http2.adoc | 22 +
.../embedded-guide/server/server.adoc | 5 +-
.../server/websocket/server-websocket.adoc | 22 +
.../embedded/server/http/HTTPServerDocs.java | 488 +++++++++++++++++-
.../jetty/server/handler/DefaultHandler.java | 2 +-
.../handler/SecuredRedirectHandler.java | 4 +-
17 files changed, 1408 insertions(+), 271 deletions(-)
create mode 100644 jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-connector.adoc
create mode 100644 jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler-implement.adoc
create mode 100644 jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler-use.adoc
create mode 100644 jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler.adoc
create mode 100644 jetty-documentation/src/main/asciidoc/embedded-guide/server/http2/server-http2.adoc
create mode 100644 jetty-documentation/src/main/asciidoc/embedded-guide/server/websocket/server-websocket.adoc
diff --git a/jetty-documentation/pom.xml b/jetty-documentation/pom.xml
index e318d4a57b1..acf0277731c 100644
--- a/jetty-documentation/pom.xml
+++ b/jetty-documentation/pom.xml
@@ -1,24 +1,4 @@
-
-
org.eclipse.jetty
@@ -63,6 +43,21 @@
jetty-servlet${project.version}
+
+ org.eclipse.jetty
+ jetty-servlets
+ ${project.version}
+
+
+ org.eclipse.jetty
+ jetty-rewrite
+ ${project.version}
+
+
+ org.eclipse.jetty
+ jetty-webapp
+ ${project.version}
+ org.eclipse.jettyjetty-alpn-server
@@ -88,6 +83,12 @@
http2-http-client-transport${project.version}
+
+ org.eclipse.jetty
+ jetty-slf4j-impl
+ ${project.version}
+ compile
+
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-cookie.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-cookie.adoc
index 8c414de749f..c5b92f7c723 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-cookie.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-cookie.adoc
@@ -75,14 +75,14 @@ Jetty is compliant with link:https://tools.ietf.org/html/rfc6265[RFC6265], and a
Previously, Version=1 cookies defined in link:https://tools.ietf.org/html/rfc2109[RFC2109] (and continued in link:https://tools.ietf.org/html/rfc2965[RFC2965]) allowed for special/reserved characters to be enclosed within double quotes when declared in a `Set-Cookie` response header:
-[source,subs="{sub-order}"]
+[source,screen]
----
Set-Cookie: foo="bar;baz";Version=1;Path="/secur"
----
This was added to the HTTP Response as follows:
-[source,java,subs="{sub-order}"]
+[source,java]
----
protected void service(HttpServletRequest request, HttpServletResponse response)
{
@@ -95,7 +95,7 @@ protected void service(HttpServletRequest request, HttpServletResponse response)
The introduction of RFC6265 has rendered this approach no longer possible; users are now required to encode cookie values that use these special characters.
This can be done utilizing `javax.servlet.http.Cookie` as follows:
-[source,java,subs="{sub-order}"]
+[source,java]
----
javax.servlet.http.Cookie cookie = new Cookie("foo", URLEncoder.encode("bar;baz", "UTF-8"));
----
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc
index ae1c4a364a3..ab78e59dedc 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-intro.adoc
@@ -49,7 +49,7 @@ Out of the box features that you get with the Jetty HTTP client include:
The Jetty artifact that provides the main HTTP client implementation is `jetty-client`.
The Maven artifact coordinates are the following:
-[source,xml,subs="{sub-order}"]
+[source,xml,subs=normal]
----
org.eclipse.jetty
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-transport.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-transport.adoc
index e5840c61390..5c59f5f8920 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-transport.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http/client-http-transport.adoc
@@ -34,7 +34,7 @@ semantic objects that can be used by applications.
The most common protocol format is HTTP/1.1, a textual protocol with lines
separated by `\r\n`:
-[source,screen,subs="{sub-order}"]
+[source,screen]
----
GET /index.html HTTP/1.1\r\n
Host: domain.com\r\n
@@ -44,7 +44,7 @@ Host: domain.com\r\n
However, the same request can be made using FastCGI, a binary protocol:
-[source,screen,subs="{sub-order}"]
+[source,screen]
----
x01 x01 x00 x01 x00 x08 x00 x00
x00 x01 x01 x00 x00 x00 x00 x00
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http2/client-http2.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http2/client-http2.adoc
index 546b61fa9fc..e7c48438eaa 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http2/client-http2.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http2/client-http2.adoc
@@ -38,7 +38,7 @@ _frames_, and this is quite a rare use case.
The Maven artifact coordinates for the HTTP/2 client library are the following:
-[source,xml,subs="{sub-order}"]
+[source,xml,subs=normal]
----
org.eclipse.jetty.http2
@@ -198,7 +198,7 @@ In order to send a HTTP request to the server, the client must send a
headers.
Sending the `HEADERS` frame opens the `Stream`:
-[source,java,indent=0,subs={sub-order}]
+[source,java,indent=0,subs=normal]
----
include::../../{doc_code}/embedded/client/http2/HTTP2ClientDocs.java[tags=newStream]
----
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/server.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/server.adoc
index 82d815d7d1b..bfd8444bab5 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/server.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/server.adoc
@@ -17,7 +17,7 @@
//
[[server]]
-== Jetty Server Libraries
+== OLD DOCUMENTATION
include::embedding/chapter.adoc[]
include::maven/chapter.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-connector.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-connector.adoc
new file mode 100644
index 00000000000..d5598d5adeb
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-connector.adoc
@@ -0,0 +1,195 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+[[eg-server-http-connector]]
+=== Server Connectors
+
+A `Connector` is the component that handles incoming requests from clients,
+and works in conjunction with `ConnectionFactory` instances.
+
+The primary implementation is `org.eclipse.jetty.server.ServerConnector`.
+`ServerConnector` uses a `java.nio.channels.ServerSocketChannel` to listen
+to a TCP port and to accept TCP connections.
+
+Since `ServerConnector` wraps a `ServerSocketChannel`, it can be configured
+in a similar way, for example the port to listen to, the network address
+to bind to, etc.:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=configureConnector]
+----
+
+The _acceptors_ are threads (typically only one) that compete to accept TCP
+connections on the listening port.
+When a connection is accepted, `ServerConnector` wraps the accepted
+`SocketChannel` and passes it to the
+xref:eg-io-arch-selector-manager[`SelectorManager`].
+Therefore there is a little moment where the acceptor thread is not accepting
+new connections because it is busy wrapping the just accepted one to pass it
+to the `SelectorManager`.
+Connections that are ready to be accepted but are not accepted yet are queued
+in a bounded queue (at the OS level) whose capacity can be configured with the
+`ServerConnector.acceptQueueSize` parameter.
+
+If your application must withstand a very high rate of connections opened,
+configuring more than one acceptor thread may be beneficial: when one acceptor
+thread accepts one connection, another acceptor thread can take over accepting
+connections.
+
+The _selectors_ are components that manage a set of connected sockets,
+implemented by xref:eg-io-arch-selector-manager[`ManagedSelector`].
+Each selector requires one thread and uses the Java NIO mechanism to
+efficiently handle the set of connected sockets.
+As a rule of thumb, a single selector can easily manage up to 1000-5000
+sockets, although the number may vary greatly depending on the application.
+
+It is possible to configure more than one `ServerConnector`, each listening
+on a different port:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=configureConnectors]
+----
+
+[[eg-server-http-connector-protocol]]
+==== Configuring Protocols
+
+For each accepted TCP connection, `ServerConnector` asks a `ConnectionFactory`
+to create a `Connection` object that handles the network traffic on that TCP
+connection, parsing and generating bytes for a specific protocol (see
+xref:eg-io-arch[this section] for more details about `Connection` objects).
+
+A `ServerConnector` can be configured with one or more ``ConnectionFactory``s.
+If no `ConnectionFactory` is specified then `HttpConnectionFactory` is
+implicitly configured.
+
+[[eg-server-http-connector-protocol-http11]]
+===== Configuring HTTP/1.1
+
+`HttpConnectionFactory` creates `HttpConnection` objects that parse bytes
+and generate bytes for the HTTP/1.1 protocol.
+
+This is how you configure Jetty to support clear-text HTTP/1.1:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=http11]
+----
+
+Supporting encrypted HTTP/1.1 (that is, requests with the HTTPS scheme)
+is supported by configuring an `SslContextFactory` that has access to the
+keyStore containing the private server key and public server certificate,
+in this way:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=tlsHttp11]
+----
+
+[[eg-server-http-connector-protocol-proxy-http11]]
+===== Configuring Jetty behind a Load Balancer
+
+It is often the case that Jetty receives connections from a load balancer
+configured to distribute the load among many Jetty backend servers.
+
+From the Jetty point of view, all the connections arrive from the load
+balancer, rather than the real clients, but is possible to configure the load
+balancer to forward the real client IP address and port to the backend Jetty
+server using the
+link:https://www.haproxy.org/download/2.1/doc/proxy-protocol.txt[PROXY protocol].
+
+NOTE: The PROXY protocol is widely supported by load balancers such as
+link:http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#5.2-send-proxy[HAProxy]
+(via its `send-proxy` directive),
+link:https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol[Nginx]
+(via its `proxy_protocol on` directive) and others.
+
+To support this case, Jetty can be configured in this way:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=proxyHTTP]
+----
+
+Note how the ``ConnectionFactory``s passed to `ServerConnector` are in order:
+first PROXY, then HTTP/1.1.
+Note also how the PROXY `ConnectionFactory` needs to know its _next_ protocol
+(in this example, HTTP/1.1).
+
+Each `ConnectionFactory` is asked to create a `Connection` object for each
+accepted TCP connection; the `Connection` objects will be chained together
+to handle the bytes, each for its own protocol.
+Therefore the `ProxyConnection` will handle the PROXY protocol bytes and
+`HttpConnection` will handle the HTTP/1.1 bytes producing a request object
+and response object that will be processed by ``Handler``s.
+
+[[eg-server-http-connector-protocol-http2]]
+===== Configuring HTTP/2
+
+It is well known that the HTTP ports are `80` (for clear-text HTTP) and `443`
+for encrypted HTTP.
+By using those ports, a client had _prior knowledge_ that the server would
+speak, respectively, the HTTP/1.x protocol and the TLS protocol (and, after
+decryption, the HTTP/1.x protocol).
+
+HTTP/2 was designed to be a smooth transition from HTTP/1.1 for users and
+as such the HTTP ports were not changed.
+However the HTTP/2 protocol is, on the wire, a binary protocol, completely
+different from HTTP/1.1.
+Therefore, with HTTP/2, clients that connect to port `80` may speak either
+HTTP/1.1 or HTTP/2, and the server must figure out which version of the HTTP
+protocol the client is speaking.
+
+Jetty can support both HTTP/1.1 and HTTP/2 on the same clear-text port by
+configuring both the HTTP/1.1 and the HTTP/2 ``ConnectionFactory``s:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=http11H2C]
+----
+
+Note how the ``ConnectionFactory``s passed to `ServerConnector` are in order:
+first HTTP/1.1, then HTTP/2.
+This is necessary to support both protocols on the same port: Jetty will
+start parsing the incoming bytes as HTTP/1.1, but then realize that they
+are HTTP/2 bytes and will therefore _upgrade_ from HTTP/1.1 to HTTP/2.
+
+This configuration is also typical when Jetty is installed in backend servers
+behind a load balancer that also takes care of offloading TLS.
+When Jetty is behind a load balancer, you can always prepend the PROXY
+protocol as described in
+xref:eg-server-http-connector-protocol-proxy-http11[this section].
+
+When using encrypted HTTP/2, the unencrypted protocol is negotiated by client
+and server using an extension to the TLS protocol called ALPN.
+
+Jetty supports ALPN and encrypted HTTP/2 with this configuration:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=tlsALPNHTTP]
+----
+
+Note how the ``ConnectionFactory``s passed to `ServerConnector` are in order:
+TLS, ALPN, HTTP/1.1, HTTP/2.
+
+Jetty starts parsing TLS bytes so that it can obtain the ALPN extension.
+With the ALPN extension information, Jetty can negotiate a protocol and
+pick, among the ``ConnectionFactory``s supported by the `ServerConnector`,
+the `ConnectionFactory` correspondent to the negotiated protocol.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler-implement.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler-implement.adoc
new file mode 100644
index 00000000000..625d206f2e0
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler-implement.adoc
@@ -0,0 +1,71 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+[[eg-server-http-handler-implement]]
+==== Implementing Handler
+
+The `Handler` API consist fundamentally of just one method:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=handlerAPI]
+----
+
+The `target` parameter is an identifier for the resource.
+This is normally the URI that is parsed from an HTTP request.
+However, a request could be forwarded to either a named resource, in which case
+`target` will be the name of the resource, or to a different URI, in which case
+`target` will be the new URI.
+
+Applications may wrap the request or response (or both) and forward the wrapped
+request or response to a different URI (which may be possibly handled by a
+different `Handler`).
+This is the reason why there are two request parameters in the `Handler` APIs:
+the first is the unwrapped, original, request while the second is the
+application-wrapped request.
+
+[[eg-server-http-handler-impl-hello]]
+===== Hello World Handler
+
+A simple "Hello World" `Handler` is the following:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=handlerHello]
+----
+
+Such a simple `Handler` extends from `AbstractHandler` and can access the
+request and response main features, such as reading request headers and
+content, or writing response headers and content.
+
+[[eg-server-http-handler-impl-filter]]
+===== Filtering Handler
+
+A filtering `Handler` is a handler that perform some modification to the
+request or response, and then either forwards the request to another
+`Handler` or produces an error response:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=handlerFilter]
+----
+
+Note how a filtering `Handler` extends from `HandlerWrapper` and as such
+needs another handler to forward the request processing to, and how the
+two``Handler``s needs to be linked together to work properly.
+
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler-use.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler-use.adoc
new file mode 100644
index 00000000000..50c46854783
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler-use.adoc
@@ -0,0 +1,464 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+[[eg-server-http-handler-use]]
+==== Using Provided Handlers
+
+Web applications are the unit of deployment in an HTTP server or Servlet
+container such as Jetty.
+
+Two different web applications are typically deployed on different
+__context path__s, where a _context path_ is the initial segment of the URI
+path.
+For example, web application `webappA` that implements a web user interface
+for an e-commerce site may be deployed to context path `/shop`, while web
+application `webappB` that implements a REST API for the e-commerce business
+may be deployed to `/api`.
+
+A client making a request to URI `/shop/cart` is directed by Jetty to
+`webappA`, while a request to URI `/api/products` is directed to `webappB`.
+
+An alternative way to deploy the two web applications of the example above
+is to use _virtual hosts_.
+A _virtual host_ is a subdomain of the primary domain that shares the same
+IP address with the primary domain.
+If the e-commerce business primary domain is `domain.com`, then a virtual
+host for `webappA` could be `shop.domain.com`, while a virtual host for
+`webappB` could be `api.domain.com`.
+
+Web application `webappA` can now be deployed to virtual host
+`shop.domain.com` and context path `/`, while web application `webappB`
+can be deployed to virtual host `api.domain.com` and context path `/`.
+Both applications have the same context path `/`, but they can be
+distinguished by the subdomain.
+
+A client making a request to `+https://shop.domain.com/cart+` is
+directed by Jetty to `webappA`, while a request to
+`+https://api.domain.com/products+` is directed to `webappB`.
+
+Therefore, in general, a web application is deployed to a _context_
+which can be seen as the pair `(virtual_host, context_path)`.
+In the first case the contexts were `(domain.com, /shop)` and
+`(domain.com, /api)`, while in the second case the contexts were
+`(shop.domain.com, /)` and `(api.domain.com, /)`.
+Server applications using the Jetty Server Libraries create and
+configure a _context_ for each web application.
+
+[[eg-server-http-handler-use-context]]
+===== ContextHandler
+
+`ContextHandler` is a `Handler` that represents a _context_ for a web
+application. It is a `HandlerWrapper` that performs some action before
+and after delegating to the nested `Handler`.
+// TODO: expand on what the ContextHandler does, e.g. ServletContext.
+
+The simplest use of `ContextHandler` is the following:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=contextHandler]
+----
+
+The `Handler` tree structure looks like the following:
+
+[source,screen]
+----
+Server
+└── ContextHandler /shop
+ └── ShopHandler
+----
+
+[[eg-server-http-handler-use-context-collection]]
+===== ContextHandlerCollection
+
+Server applications may need to deploy to Jetty more than one web application.
+
+Recall from the xref:eg-server-http-handler[introduction] that Jetty offers
+`HandlerCollection` and `HandlerList` that may contain a sequence of children
+``Handler``s.
+However, both of these have no knowledge of the concept of _context_ and just
+iterate through the sequence of ``Handler``s.
+
+A better choice for multiple web application is `ContextHandlerCollection`,
+that matches a _context_ from either its _context path_ or _virtual host_,
+without iterating through the ``Handler``s.
+
+If `ContextHandlerCollection` does not find a match, it just returns.
+What happens next depends on the `Handler` tree structure: other ``Handler``s
+may be invoked after `ContextHandlerCollection`, for example `DefaultHandler`
+(see xref:eg-server-http-handler-use-util-default-handler[this section]).
+Eventually, if `Request.setHandled(true)` is not called, Jetty returns a HTTP
+`404` response to the client.
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=contextHandlerCollection]
+----
+
+The `Handler` tree structure looks like the following:
+
+[source,screen]
+----
+Server
+└── ContextHandlerCollection
+ ├── ContextHandler /shop
+ │ └── ShopHandler
+ └── ContextHandler /api
+ └── RESTHandler
+----
+
+[[eg-server-http-handler-use-servlet-context]]
+===== ServletContextHandler
+
+``Handler``s are easy to write, but often web applications have already been
+written using the Servlet APIs, using ``Servlet``s and ``Filter``s.
+
+`ServletContextHandler` is a `ContextHandler` that provides support for the
+Servlet APIs and implements the behaviors required by the Servlet specification.
+
+The Maven artifact coordinates are:
+
+[source,xml,subs=normal]
+----
+
+ org.eclipse.jetty
+ jetty-servlet
+ {version}
+
+----
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=servletContextHandler]
+----
+
+The `Handler` and Servlet components tree structure looks like the following:
+
+[source,screen,subs=normal]
+----
+Server
+└── ServletContextHandler /shop
+ ├── _ShopCartServlet /cart/*_
+ └── _CrossOriginFilter /*_
+----
+
+Note how the Servlet components (they are not ``Handler``s) are represented in
+_italic_.
+
+Note also how adding a `Servlet` or a `Filter` returns a _holder_ object that
+can be used to specify additional configuration for that particular `Servlet`
+or `Filter`.
+
+When a request arrives to `ServletContextHandler` the request URI will be
+matched against the ``Filter``s and ``Servlet`` mappings and only those that
+match will process the request, as dictated by the Servlet specification.
+
+IMPORTANT: `ServletContextHandler` is a terminal `Handler`, that is it always
+calls `Request.setHandled(true)` when invoked.
+Server applications must be careful when creating the `Handler`
+tree to put ``ServletContextHandler``s as last ``Handler``s in a `HandlerList`
+or as children of `ContextHandlerCollection`.
+
+[[eg-server-http-handler-use-webapp-context]]
+===== WebAppContext
+
+`WebAppContext` is a `ServletContextHandler` that auto configures itself by
+reading a `web.xml` Servlet configuration file.
+
+Server applications can specify a `+*.war+` file or a directory with the
+structure of a `+*.war+` file to `WebAppContext` to deploy a standard Servlet
+web application packaged as a `war` (as defined by the Servlet specification).
+
+Where server applications using `ServletContextHandler` must manually invoke
+methods to add ``Servlet``s and ``Filter``s, `WebAppContext` reads
+`WEB-INF/web.xml` to add ``Servlet``s and ``Filter``s.
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=webAppContextHandler]
+----
+
+[[eg-server-http-handler-use-resource-handler]]
+===== ResourceHandler -- Static Content
+
+Static content such as images or files (HTML, JavaScript, CSS) can be sent
+by Jetty very efficiently because Jetty can write the content asynchronously,
+using direct ``ByteBuffer``s to minimize data copy, and using a memory cache
+for faster access to the data to send.
+
+Being able to write content asynchronously means that if the network gets
+congested (for example, the client reads the content very slowly) and the
+server stalls the send of the requested data, then Jetty will wait to resume
+the send _without_ blocking a thread to finish the send.
+
+`ResourceHandler` supports the following features:
+
+* Welcome files, for example serving `/index.html` for request URI `/`
+* Precompressed resources, serving a precompressed `/document.txt.gz` for
+request URI `/document.txt`
+* link:https://tools.ietf.org/html/rfc7233[Range requests], for requests
+containing the `Range` header, which allows clients to pause and resume
+downloads of large files
+* Directory listing, serving a HTML page with the file list of the requested
+directory
+* Conditional headers, for requests containing the `If-Match`, `If-None-Match`,
+`If-Modified-Since`, `If-Unmodified-Since` headers.
+
+The number of features supported and the efficiency in sending static content
+are on the same level as those of common front-end servers used to serve
+static content such as Nginx or Apache.
+Therefore, the traditional architecture where Nginx/Apache was the front-end
+server used only to send static content and Jetty was the back-end server used
+only to send dynamic content is somehow obsolete as Jetty can perform
+efficiently both tasks.
+This leads to simpler systems (less components to configure and manage) and
+more performance (no need to proxy dynamic requests from front-end servers
+to back-end servers).
+
+NOTE: It is common to use Nginx/Apache as load balancers, or as rewrite/redirect
+servers. We typically recommend link:https://haproxy.org[HAProxy] as load
+balancer, and Jetty has
+xref:eg-server-http-handler-use-util-rewrite-handler[rewrite/redirect features]
+as well.
+
+This is how you configure a `ResourceHandler` to create a simple file server:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=resourceHandler]
+----
+
+If you need to serve static resources from multiple directories:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=multipleResourcesHandler]
+----
+
+If the resource is not found, `ResourceHandler` will not call
+`Request.setHandled(true)` so what happens next depends on the `Handler`
+tree structure. See also
+xref:eg-server-http-handler-use-util-default-handler[how to use] `DefaultHandler`.
+
+[[eg-server-http-handler-use-default-servlet]]
+===== DefaultServlet -- Static Content for Servlets
+
+If you have a
+xref:eg-server-http-handler-use-servlet-context[Servlet web application],
+you may want to use a `DefaultServlet` instead of `ResourceHandler`.
+The features are similar, but `DefaultServlet` is more commonly used to
+serve static files for Servlet web applications.
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=defaultServlet]
+----
+
+[[eg-server-http-handler-use-util-gzip-handler]]
+===== GzipHandler
+
+`GzipHandler` provides supports for automatic decompression of compressed
+request content and automatic compression of response content.
+
+`GzipHandler` is a `HandlerWrapper` that inspects the request and, if the
+request matches the `GzipHandler` configuration, just installs the required
+components to eventually perform decompression of the request content or
+compression of the response content.
+The decompression/compression is not performed until the web application
+reads request content or writes response content.
+
+`GzipHandler` can be configured at the server level in this way:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=serverGzipHandler]
+----
+
+The `Handler` tree structure looks like the following:
+
+[source,screen]
+----
+Server
+└── GzipHandler
+ └── ContextHandlerCollection
+ ├── ContextHandler 1
+ :── ...
+ └── ContextHandler N
+----
+
+However, in less common cases, you can configure `GzipHandler` on a
+per-context basis, for example because you want to configure `GzipHandler`
+with different parameters for each context, or because you want only some
+contexts to have compression support:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=contextGzipHandler]
+----
+
+The `Handler` tree structure looks like the following:
+
+[source,screen]
+----
+Server
+└── ContextHandlerCollection
+ └── ContextHandlerCollection
+ ├── GzipHandler
+ │ └── ContextHandler /shop
+ │ └── ShopHandler
+ └── ContextHandler /api
+ └── RESTHandler
+----
+
+// TODO: does ServletContextHandler really need a special configuration?
+
+[[eg-server-http-handler-use-util-rewrite-handler]]
+===== RewriteHandler
+
+`RewriteHandler` provides support for URL rewriting, very similarly to
+link:https://httpd.apache.org/docs/current/mod/mod_rewrite.html[Apache's mod_rewrite]
+or
+link:https://nginx.org/en/docs/http/ngx_http_rewrite_module.html[Nginx rewrite module].
+
+The Maven artifact coordinates are:
+
+[source,xml,subs=normal]
+----
+
+ org.eclipse.jetty
+ jetty-rewrite
+ {version}
+
+----
+
+`RewriteHandler` can be configured with a set of __rule__s; a _rule_ inspects
+the request and when it matches it performs some change to the request (for
+example, changes the URI path, adds/removes headers, etc.).
+
+The Jetty Server Libraries provide rules for the most common usages, but you
+can write your own rules by extending the
+`org.eclipse.jetty.rewrite.handler.Rule` class.
+
+Please refer to the `jetty-rewrite` module
+link:{JDURL}/org/eclipse/jetty/rewrite/handler/package-summary.html[javadocs]
+for the complete list of available rules.
+
+You typically want to configure `RewriteHandler` at the server level, although
+it is possible to configure it on a per-context basis.
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=rewriteHandler]
+----
+
+The `Handler` tree structure looks like the following:
+
+[source,screen]
+----
+Server
+└── RewriteHandler
+ └── ContextHandlerCollection
+ ├── ContextHandler 1
+ :── ...
+ └── ContextHandler N
+----
+
+[[eg-server-http-handler-use-util-stats-handler]]
+===== StatisticsHandler
+
+`StatisticsHandler` gathers and exposes a number of statistic values related
+to request processing such as:
+
+* Total number of requests
+* Current number of concurrent requests
+* Minimum, maximum, average and standard deviation of request processing times
+* Number of responses grouped by HTTP code (i.e. how many `2xx` responses, how
+many `3xx` responses, etc.)
+* Total response content bytes
+
+Server applications can read these values and use them internally, or expose
+them via some service, or export them via JMX.
+// TODO: xref to the JMX section.
+
+`StatisticsHandler` can be configured at the server level or at the context
+level.
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=statsHandler]
+----
+
+The `Handler` tree structure looks like the following:
+
+[source,screen]
+----
+Server
+└── StatisticsHandler
+ └── ContextHandlerCollection
+ ├── ContextHandler 1
+ :── ...
+ └── ContextHandler N
+----
+
+[[eg-server-http-handler-use-util-secure-handler]]
+===== SecuredRedirectHandler -- Redirect from HTTP to HTTPS
+
+// TODO: wait for issue #4766
+TODO
+
+[[eg-server-http-handler-use-util-default-handler]]
+===== DefaultHandler
+
+`DefaultHandler` is a terminal `Handler` that always calls
+`Request.setHandled(true)` and performs the following:
+
+* Serves the `favicon.ico` Jetty icon when it is requested
+* Sends a HTTP `404` response for any other request
+* The HTTP `404` response content nicely shows a HTML table with all the
+contexts deployed on the `Server` instance
+
+`DefaultHandler` is best used as the last `Handler` of a `HandlerList`,
+for example:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=defaultHandler]
+----
+
+The `Handler` tree structure looks like the following:
+
+[source,screen]
+----
+Server
+└── HandlerList
+ ├── ContextHandlerCollection
+ │ ├── ContextHandler 1
+ │ :── ...
+ │ └── ContextHandler N
+ └── DefaultHandler
+----
+
+In the example above, `ContextHandlerCollection` will try to match a request
+to one of the contexts; if the match fails, `HandlerList` will call the next
+`Handler` which is `DefaultHandler` that will return a HTTP `404` with an
+HTML page showing the existing contexts deployed on the `Server`.
+
+NOTE: `DefaultHandler` just sends a nicer HTTP `404` response in case of
+wrong requests from clients.
+Jetty will send an HTTP `404` response anyway if `DefaultHandler` is not
+used.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler.adoc
new file mode 100644
index 00000000000..104f2c50ece
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler.adoc
@@ -0,0 +1,103 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+[[eg-server-http-handler]]
+=== Server Handlers
+
+An `org.eclipse.jetty.server.Handler` is the component that processes
+incoming HTTP requests and eventually produces HTTP responses.
+
+``Handler``s can be organized in different ways:
+
+* in a sequence, where ``Handler``s are invoked one after the other
+** `HandlerCollection` invokes _all_ ``Handler``s one after the other
+** `HandlerList` invokes ``Handlers``s until one calls `Request.setHandled(true)`
+to indicate that the request has been handled and no further `Handler` should
+be invoked
+* nested, where one `Handler` invokes the next, nested, `Handler`
+** `HandlerWrapper` implements this behavior
+
+The `HandlerCollection` behavior (invoking _all_ handlers) is useful when
+for example the last `Handler` is a logging `Handler` that logs the request
+(that may have been modified by previous handlers).
+
+The `HandlerList` behavior (invoking handlers up to the first that calls
+`Request.setHandled(true)`) is useful when each handler processes a different
+URIs or a different virtual hosts: ``Handler``s are invoked one after the
+other until one matches the URI or virtual host.
+
+The nested behavior is useful to enrich the request with additional services
+such as HTTP session support (`SessionHandler`), or with specific behaviors
+dictated by the Servlet specification (`ServletHandler`).
+
+``Handler``s can be organized in a tree by composing them together:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=handlerTree]
+----
+
+The corresponding `Handler` tree structure looks like the following:
+
+[source,screen]
+----
+HandlerCollection
+├── HandlerList
+│ ├── App1Handler
+│ └── HandlerWrapper
+│ └── App2Handler
+└── LoggingHandler
+----
+
+////
+PlantUML cannot render a tree left-aligned :(
+[plantuml]
+----
+skinparam backgroundColor transparent
+skinparam monochrome true
+skinparam shadowing false
+skinparam padding 5
+
+scale 1.5
+
+hide members
+hide circle
+
+HandlerCollection -- HandlerList
+HandlerCollection -- LoggingHandler
+HandlerList -- App1Handler
+HandlerList -- App2Handler
+App2Handler -- ServletHandler
+----
+////
+
+Server applications should rarely write custom ``Handler``s, preferring
+instead to use existing ``Handler``s provided by the Jetty Server Libraries
+for managing web application contexts, security, HTTP sessions and Servlet
+support.
+Refer to xref:eg-server-http-handler-use[this section] for more information about
+how to use the ``Handler``s provided by the Jetty Server Libraries.
+
+However, in some cases the additional features are not required, or additional
+constraints on memory footprint, or performance, or just simplicity must be met.
+In these cases, implementing your own `Handler` may be a better solution.
+Refer to xref:eg-server-http-handler-implement[this section] for more information
+about how to write your own ``Handler``s.
+
+include::server-http-handler-use.adoc[]
+include::server-http-handler-implement.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http.adoc
index 1b2a86adc43..39334067a3b 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http.adoc
@@ -22,6 +22,17 @@
The Eclipse Jetty Project has historically provided libraries to embed an HTTP
server and a Servlet Container.
+The Maven artifact coordinates are:
+
+[source,xml,subs=normal]
+----
+
+ org.eclipse.jetty
+ jetty-server
+ {version}
+
+----
+
An `org.eclipse.jetty.server.Server` instance is the central component that
links together a collection of ``Connector``s and a collection of
``Handler``s, with threads from a `ThreadPool` doing the work.
@@ -44,7 +55,7 @@ Server -- Handlers
----
The components that accept connections from clients are
-`org.eclipse.jetty.server.Connector` instances.
+`org.eclipse.jetty.server.Connector` implementations.
When a Jetty server interprets the HTTP protocol (both HTTP/1.1 and HTTP/2),
it uses `org.eclipse.jetty.server.Handler` instances to process incoming
@@ -66,232 +77,5 @@ applications only need to put the required components together to provide
all the required features.
// TODO: link to a place where we discuss the handlers in more details.
-[[eg-server-connector]]
-=== Server Connectors
-
-A `Connector` is the component that handles incoming requests from clients,
-and works in conjunction with `ConnectionFactory` instances.
-
-The primary implementation is `org.eclipse.jetty.server.ServerConnector`.
-`ServerConnector` uses a `java.nio.channels.ServerSocketChannel` to listen
-to a TCP port and to accept TCP connections.
-
-Since `ServerConnector` wraps a `ServerSocketChannel`, it can be configured
-in a similar way, for example the port to listen to, the network address
-to bind to, etc.:
-
-[source,java,indent=0]
-----
-include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=configureConnector]
-----
-
-The _acceptors_ are threads that compete to accept TCP connections on the
-listening port, typically only one.
-When a connection is accepted, `ServerConnector` wraps it and passes it to
-the xref:eg-io-arch-selector-manager[`SelectorManager`].
-Therefore there is a little moment where the acceptor thread is not accepting
-new connections because it is busy wrapping the just accepted one to pass it
-to the `SelectorManager`.
-Connections that are ready to be accepted but are not accepted yet are queued
-in a bounded queue (at the OS level) whose capacity can be configured with the
-`ServerConnector.acceptQueueSize` parameter.
-
-If your application must withstand a very high rate of connections opened,
-configuring more than one acceptor thread may be beneficial: when one acceptor
-thread accepts one connection, another acceptor thread can take over accepting
-connections.
-
-The _selectors_ are components that manage a set of connected sockets,
-implemented by xref:eg-io-arch-selector-manager[`ManagedSelector`].
-Each selector requires one thread and uses the Java NIO mechanism to
-efficiently handle the set of connected sockets.
-As a rule of thumb, a single selector can easily manage 1000-5000 sockets,
-although the number may vary greatly depending on the application.
-
-It is possible to configure more than one `ServerConnector`, each listening
-on different ports:
-
-[source,java,indent=0]
-----
-include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=configureConnectors]
-----
-
-[[eg-server-connector-protocol]]
-==== Configuring Protocols
-
-For each accepted TCP connection, `ServerConnector` asks a `ConnectionFactory`
-to create a `Connection` object that handles the network traffic on that TCP
-connection, parsing and generating bytes for a specific protocol (see
-xref:eg-io-arch[this section] for more details about `Connection` objects).
-
-A `ServerConnector` can be configured with one or more ``ConnectionFactory``s.
-If no `ConnectionFactory` is specified then `HttpConnectionFactory` is
-implicitly configured.
-
-[[eg-server-connector-protocol-http11]]
-===== Configuring HTTP/1.1
-
-`HttpConnectionFactory` creates `HttpConnection` objects that parse bytes
-and generate bytes for the HTTP/1.1 protocol.
-
-This is how you configure Jetty to support clear-text HTTP/1.1:
-
-[source,java,indent=0]
-----
-include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=http11]
-----
-
-Supporting encrypted HTTP/1.1 (that is, requests with the HTTPS scheme)
-is supported by configuring an `SslContextFactory` that has access to the
-keyStore containing the private server key and public server certificate,
-in this way:
-
-[source,java,indent=0]
-----
-include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=tlsHttp11]
-----
-
-[[eg-server-connector-protocol-proxy-http11]]
-===== Configuring Jetty behind a Load Balancer
-
-It is often the case that Jetty receives connections from a load balancer
-configured to distribute the load among many Jetty backend servers.
-
-From the Jetty point of view, all the connections arrive from the load
-balancer, rather than the real clients, but is possible to forward the real
-client IP address and port to the backend Jetty server using the
-link:https://www.haproxy.org/download/2.1/doc/proxy-protocol.txt[PROXY protocol].
-
-NOTE: The PROXY protocol is widely supported by load balancers such as
-link:http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#5.2-send-proxy[HAProxy]
-(via its `send-proxy` directive), or
-link:https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol[Nginx]
-(via its `proxy_protocol on` directive), and others.
-
-To support this case, Jetty can be configured in this way:
-
-[source,java,indent=0]
-----
-include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=proxyHTTP]
-----
-
-Note how the ``ConnectionFactory``s passed to `ServerConnector` are in order:
-first PROXY, then HTTP/1.1.
-Note also how the PROXY `ConnectionFactory` needs to know its _next_ protocol
-(in this example, HTTP/1.1).
-
-Each `ConnectionFactory` is asked to create a `Connection` object for each
-accepted TCP connection; the `Connection` objects will be chained together
-to handle the bytes, each for its own protocol.
-Therefore the `ProxyConnection` will handle the PROXY protocol bytes and
-`HttpConnection` will handle the HTTP/1.1 bytes producing a request object
-and response object that will be processed by ``Handler``s.
-
-[[eg-server-connector-protocol-http2]]
-===== Configuring HTTP/2
-
-It is well know that the HTTP ports are `80` (for clear-text HTTP) and `443`
-for encrypted HTTP.
-By using those ports, a client had _prior knowledge_ that the server would
-speak, respectively, the HTTP/1.x protocol and the TLS protocol (and, after
-decryption, the HTTP/1.x protocol).
-
-HTTP/2 was designed to be a smooth transition from HTTP/1.1 for users and
-as such the HTTP ports were not changed.
-However the HTTP/2 protocol is, on the wire, a binary protocol, completely
-different from HTTP/1.1.
-Therefore, with HTTP/2, clients that connect to port `80` may speak either
-HTTP/1.1 or HTTP/2, and the server must figure out which version of the HTTP
-protocol the client is speaking.
-
-Jetty can support both HTTP/1.1 and HTTP/2 on the same clear-text port by
-configuring both the HTTP/1.1 and the HTTP/2 ``ConnectionFactory``s:
-
-[source,java,indent=0]
-----
-include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=http11H2C]
-----
-
-Note how the ``ConnectionFactory``s passed to `ServerConnector` are in order:
-first HTTP/1.1, then HTTP/2.
-This is necessary to support both protocols on the same port: Jetty will
-start parsing the incoming bytes as HTTP/1.1, but then realize that they
-are HTTP/2 bytes and will therefore _upgrade_ from HTTP/1.1 to HTTP/2.
-
-This configuration is also typical when Jetty is installed in backend servers
-behind a load balancer that also takes care of offloading TLS.
-When Jetty is behind a load balancer, you can always prepend the PROXY
-protocol as described in
-xref:eg-server-connector-protocol-proxy-http11[this section].
-
-When using encrypted HTTP/2, the unencrypted protocol is negotiated by client
-and server using an extension to the TLS protocol called ALPN.
-
-Jetty supports ALPN and encrypted HTTP/2 with this configuration:
-
-[source,java,indent=0]
-----
-include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=tlsALPNHTTP]
-----
-
-[[eg-server-handler]]
-=== Server Handlers
-
-A `Handler` is the component that processes incoming HTTP requests and
-eventually produces HTTP responses.
-
-``Handler``s can be organized in different ways:
-
-* in a sequence, where ``Handler``s are invoked one after the other
-** `HandlerCollection` invokes _all_ ``Handler``s one after the other
-** `HandlerList` invokes ``Handlers``s until one calls `Request.setHandled(true)`
-to indicate that the request has been handled and no further `Handler` should
-be invoked.
-* nested, where one `Handler` invokes the next `Handler`
-** `HandlerWrapper` implements this behavior
-
-The `HandlerCollection` behavior (invoking _all_ handlers) is useful when
-for example the last `Handler` is a logging `Handler` that logs the the
-request(that may have been modified by previous handlers).
-
-The `HandlerList` behavior (invoking handlers up to the first that calls
-`Request.setHandled(true)`) is useful when different handlers process different
-URIs or different virtual hosts: invoke one after the other until one matches
-the URI or virtual host.
-
-The nested behavior is useful to enrich the request with additional services
-such as HTTP session support (`SessionHandler`), or with specific behaviors
-dictated by the Servlet specification (`ServletHandler`).
-
-``Handler``s can be organized in a tree by composing them together:
-
-[plantuml]
-----
-skinparam backgroundColor transparent
-skinparam monochrome true
-skinparam shadowing false
-skinparam padding 5
-
-scale 1.5
-
-hide members
-hide circle
-
-HandlerCollection -- HandlerList
-HandlerCollection -- LoggingHandler
-HandlerList -- App1Handler
-HandlerList -- App2Handler
-App2Handler -- ServletHandler
-----
-
-In code it looks like this:
-
-[source,java,indent=0]
-----
-include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=tree]
-----
-
-// TODO: old docs introduces briefly ServletHandler but I think it deserves its own section
-
-// TODO: old docs introduce ContextHandler here and WebAppContext
-
+include::server-http-connector.adoc[]
+include::server-http-handler.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http2/server-http2.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http2/server-http2.adoc
new file mode 100644
index 00000000000..c6728c6ef0e
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http2/server-http2.adoc
@@ -0,0 +1,22 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+[[eg-server-http2]]
+=== HTTP/2 Server Libraries
+
+TODO
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/server.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/server.adoc
index a6dcc1feb45..a8cc9ceffd2 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/server/server.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/server.adoc
@@ -46,6 +46,7 @@ xref:eg-server-http2[HTTP/2 libraries]
via the xref:eg-server-websocket[WebSocket libraries]
include::http/server-http.adoc[]
-include::http/server-http2.adoc[]
-include::http/server-websocket.adoc[]
+include::http2/server-http2.adoc[]
+include::websocket/server-websocket.adoc[]
include::server-io-arch.adoc[]
+include::../old_docs/server.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/websocket/server-websocket.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/websocket/server-websocket.adoc
new file mode 100644
index 00000000000..0ea9473f72b
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/websocket/server-websocket.adoc
@@ -0,0 +1,22 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+[[eg-server-websocket]]
+=== WebSocket Server Libraries
+
+TODO
diff --git a/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java b/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java
index 1fdd72eafcd..263d94484f5 100644
--- a/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java
+++ b/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java
@@ -18,13 +18,24 @@
package embedded.server.http;
+import java.io.IOException;
+import java.util.EnumSet;
+import javax.servlet.DispatcherType;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.http.HttpCompliance;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.rewrite.handler.CompactPathRule;
+import org.eclipse.jetty.rewrite.handler.RedirectRegexRule;
+import org.eclipse.jetty.rewrite.handler.RewriteHandler;
+import org.eclipse.jetty.rewrite.handler.RewriteRegexRule;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
@@ -35,12 +46,26 @@ import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.HandlerWrapper;
-import org.eclipse.jetty.servlet.ServletHandler;
+import org.eclipse.jetty.server.handler.ResourceHandler;
+import org.eclipse.jetty.server.handler.StatisticsHandler;
+import org.eclipse.jetty.server.handler.gzip.GzipHandler;
+import org.eclipse.jetty.servlet.DefaultServlet;
+import org.eclipse.jetty.servlet.FilterHolder;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.servlets.CrossOriginFilter;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.util.resource.ResourceCollection;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.eclipse.jetty.webapp.WebAppContext;
@SuppressWarnings("unused")
public class HTTPServerDocs
@@ -266,7 +291,7 @@ public class HTTPServerDocs
// end::tlsALPNHTTP[]
}
- public void tree() throws Exception
+ public void handlerTree()
{
class LoggingHandler extends AbstractHandler
{
@@ -292,7 +317,7 @@ public class HTTPServerDocs
}
}
- // tag::tree[]
+ // tag::handlerTree[]
// Create a Server instance.
Server server = new Server();
@@ -305,10 +330,459 @@ public class HTTPServerDocs
collection.addHandler(new LoggingHandler());
list.addHandler(new App1Handler());
- App2Handler app2Handler = new App2Handler();
- list.addHandler(app2Handler);
+ HandlerWrapper wrapper = new HandlerWrapper();
+ list.addHandler(wrapper);
- app2Handler.setHandler(new ServletHandler());
- // end::tree[]
+ wrapper.setHandler(new App2Handler());
+ // end::handlerTree[]
+ }
+
+ public void handlerAPI()
+ {
+ class MyHandler extends AbstractHandler
+ {
+ @Override
+ // tag::handlerAPI[]
+ public void handle(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response)
+ {
+ }
+ // end::handlerAPI[]
+ }
+ }
+
+ public void handlerHello() throws Exception
+ {
+ // tag::handlerHello[]
+ class HelloWorldHandler extends AbstractHandler
+ {
+ @Override
+ public void handle(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
+ {
+ // Mark the request as handled by this Handler.
+ jettyRequest.setHandled(true);
+
+ response.setStatus(200);
+ response.setContentType("text/html; charset=UTF-8");
+
+ // Write a Hello World response.
+ response.getWriter().print("" +
+ "" +
+ "" +
+ "" +
+ " Jetty Hello World Handler" +
+ "" +
+ "" +
+ "
Hello World
" +
+ "" +
+ "" +
+ "");
+ }
+ }
+
+ Server server = new Server();
+ Connector connector = new ServerConnector(server);
+ server.addConnector(connector);
+
+ // Set the Hello World Handler.
+ server.setHandler(new HelloWorldHandler());
+
+ server.start();
+ // end::handlerHello[]
+ }
+
+ public void handlerFilter() throws Exception
+ {
+ class HelloWorldHandler extends AbstractHandler
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
+ {
+ }
+ }
+
+ // tag::handlerFilter[]
+ class FilterHandler extends HandlerWrapper
+ {
+ @Override
+ public void handle(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ String path = request.getRequestURI();
+ if (path.startsWith("/old_path/"))
+ {
+ // Rewrite old paths to new paths.
+ HttpURI uri = jettyRequest.getHttpURI();
+ HttpURI newURI = new HttpURI(uri);
+ String newPath = "/new_path/" + path.substring("/old_path/".length());
+ newURI.setPath(newPath);
+ // Modify the request object.
+ jettyRequest.setHttpURI(newURI);
+ }
+
+ // This Handler is not handling the request, so
+ // it does not call jettyRequest.setHandled(true).
+
+ // Forward to the next Handler.
+ super.handle(target, jettyRequest, request, response);
+ }
+ }
+
+ Server server = new Server();
+ Connector connector = new ServerConnector(server);
+ server.addConnector(connector);
+
+ // Link the Handlers.
+ FilterHandler filter = new FilterHandler();
+ filter.setHandler(new HelloWorldHandler());
+ server.setHandler(filter);
+
+ server.start();
+ // end::handlerFilter[]
+ }
+
+ public void contextHandler() throws Exception
+ {
+ // tag::contextHandler[]
+ class ShopHandler extends AbstractHandler
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
+ {
+ baseRequest.setHandled(true);
+ // Implement the shop.
+ }
+ }
+
+ Server server = new Server();
+ Connector connector = new ServerConnector(server);
+ server.addConnector(connector);
+
+ // Create a ContextHandler with contextPath.
+ ContextHandler context = new ContextHandler();
+ context.setContextPath("/shop");
+ context.setHandler(new ShopHandler());
+
+ // Link the context to the server.
+ server.setHandler(context);
+
+ server.start();
+ // end::contextHandler[]
+ }
+
+ public void contextHandlerCollection() throws Exception
+ {
+ // tag::contextHandlerCollection[]
+ class ShopHandler extends AbstractHandler
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
+ {
+ baseRequest.setHandled(true);
+ // Implement the shop.
+ }
+ }
+
+ class RESTHandler extends AbstractHandler
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
+ {
+ baseRequest.setHandled(true);
+ // Implement the REST APIs.
+ }
+ }
+
+ Server server = new Server();
+ Connector connector = new ServerConnector(server);
+ server.addConnector(connector);
+
+ // Create a ContextHandlerCollection to hold contexts.
+ ContextHandlerCollection contextCollection = new ContextHandlerCollection();
+ // Link the ContextHandlerCollection to the Server.
+ server.setHandler(contextCollection);
+
+ // Create the context for the shop web application.
+ ContextHandler shopContext = new ContextHandler("/shop");
+ shopContext.setHandler(new ShopHandler());
+ // Add it to ContextHandlerCollection.
+ contextCollection.addHandler(shopContext);
+
+ server.start();
+
+ // Create the context for the API web application.
+ ContextHandler apiContext = new ContextHandler("/api");
+ apiContext.setHandler(new RESTHandler());
+ // Web applications can be deployed after the Server is started.
+ contextCollection.deployHandler(apiContext, Callback.NOOP);
+ // end::contextHandlerCollection[]
+ }
+
+ public void servletContextHandler() throws Exception
+ {
+ // tag::servletContextHandler[]
+ class ShopCartServlet extends HttpServlet
+ {
+ @Override
+ protected void service(HttpServletRequest request, HttpServletResponse response)
+ {
+ // Implement the shop cart functionality.
+ }
+ }
+
+ Server server = new Server();
+ Connector connector = new ServerConnector(server);
+ server.addConnector(connector);
+
+ // Create a ServletContextHandler with contextPath.
+ ServletContextHandler context = new ServletContextHandler();
+ context.setContextPath("/shop");
+
+ // Add the Servlet implementing the cart functionality to the context.
+ ServletHolder servletHolder = context.addServlet(ShopCartServlet.class, "/cart/*");
+ // Configure the Servlet with init-parameters.
+ servletHolder.setInitParameter("maxItems", "128");
+
+ // Add the CrossOriginFilter to protect from CSRF attacks.
+ FilterHolder filterHolder = context.addFilter(CrossOriginFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
+ // Configure the filter.
+ filterHolder.setAsyncSupported(true);
+
+ // Link the context to the server.
+ server.setHandler(context);
+
+ server.start();
+ // end::servletContextHandler[]
+ }
+
+ public void webAppContextHandler() throws Exception
+ {
+ // tag::webAppContextHandler[]
+ Server server = new Server();
+ Connector connector = new ServerConnector(server);
+ server.addConnector(connector);
+
+ // Create a WebAppContext.
+ WebAppContext context = new WebAppContext();
+ // Configure the path of the packaged web application (file or directory).
+ context.setWar("/path/to/webapp.war");
+ // Configure the contextPath.
+ context.setContextPath("/app");
+
+ // Link the context to the server.
+ server.setHandler(context);
+
+ server.start();
+ // end::webAppContextHandler[]
+ }
+
+ public void resourceHandler() throws Exception
+ {
+ // tag::resourceHandler[]
+ Server server = new Server();
+ Connector connector = new ServerConnector(server);
+ server.addConnector(connector);
+
+ // Create and configure a ResourceHandler.
+ ResourceHandler handler = new ResourceHandler();
+ // Configure the directory where static resources are located.
+ handler.setBaseResource(Resource.newResource("/path/to/static/resources/"));
+ // Configure directory listing.
+ handler.setDirectoriesListed(false);
+ // Configure welcome files.
+ handler.setWelcomeFiles(new String[]{"index.html"});
+ // Configure whether to accept range requests.
+ handler.setAcceptRanges(true);
+
+ // Link the context to the server.
+ server.setHandler(handler);
+
+ server.start();
+ // end::resourceHandler[]
+ }
+
+ public void multipleResourcesHandler() throws Exception
+ {
+ // tag::multipleResourcesHandler[]
+ ResourceHandler handler = new ResourceHandler();
+
+ // For multiple directories, use ResourceCollection.
+ ResourceCollection directories = new ResourceCollection();
+ directories.addPath("/path/to/static/resources/");
+ directories.addPath("/another/path/to/static/resources/");
+
+ handler.setBaseResource(directories);
+ // end::multipleResourcesHandler[]
+ }
+
+ public void defaultServlet()
+ {
+ // tag::defaultServlet[]
+ // Create a ServletContextHandler with contextPath.
+ ServletContextHandler context = new ServletContextHandler();
+ context.setContextPath("/app");
+
+ // Add the DefaultServlet to serve static content.
+ ServletHolder servletHolder = context.addServlet(DefaultServlet.class, "/");
+ // Configure the DefaultServlet with init-parameters.
+ servletHolder.setInitParameter("resourceBase", "/path/to/static/resources/");
+ servletHolder.setAsyncSupported(true);
+ // end::defaultServlet[]
+ }
+
+ public void serverGzipHandler() throws Exception
+ {
+ // tag::serverGzipHandler[]
+ Server server = new Server();
+ Connector connector = new ServerConnector(server);
+ server.addConnector(connector);
+
+ // Create and configure GzipHandler.
+ GzipHandler gzipHandler = new GzipHandler();
+ // Only compress response content larger than this.
+ gzipHandler.setMinGzipSize(1024);
+ // Do not compress these URI paths.
+ gzipHandler.setExcludedPaths("/uncompressed");
+ // Also compress POST responses.
+ gzipHandler.addIncludedMethods("POST");
+ // Do not compress these mime types.
+ gzipHandler.addExcludedMimeTypes("font/ttf");
+
+ // Link a ContextHandlerCollection to manage contexts.
+ ContextHandlerCollection contexts = new ContextHandlerCollection();
+ gzipHandler.setHandler(contexts);
+
+ // Link the GzipHandler to the Server.
+ server.setHandler(gzipHandler);
+
+ server.start();
+ // end::serverGzipHandler[]
+ }
+
+ public void contextGzipHandler() throws Exception
+ {
+ class ShopHandler extends AbstractHandler
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
+ {
+ baseRequest.setHandled(true);
+ // Implement the shop.
+ }
+ }
+
+ class RESTHandler extends AbstractHandler
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
+ {
+ baseRequest.setHandled(true);
+ // Implement the REST APIs.
+ }
+ }
+
+ Server server = new Server();
+ ServerConnector connector = new ServerConnector(server);
+ server.addConnector(connector);
+
+ // tag::contextGzipHandler[]
+ // Create a ContextHandlerCollection to hold contexts.
+ ContextHandlerCollection contextCollection = new ContextHandlerCollection();
+ // Link the ContextHandlerCollection to the Server.
+ server.setHandler(contextCollection);
+
+ // Create the context for the shop web application.
+ ContextHandler shopContext = new ContextHandler("/shop");
+ shopContext.setHandler(new ShopHandler());
+
+ // You want to gzip the shop web application only.
+ GzipHandler shopGzipHandler = new GzipHandler();
+ shopGzipHandler.setHandler(shopContext);
+
+ // Add it to ContextHandlerCollection.
+ contextCollection.addHandler(shopGzipHandler);
+
+ // Create the context for the API web application.
+ ContextHandler apiContext = new ContextHandler("/api");
+ apiContext.setHandler(new RESTHandler());
+
+ // Add it to ContextHandlerCollection.
+ contextCollection.addHandler(apiContext);
+ // end::contextGzipHandler[]
+
+ server.start();
+ }
+
+ public void rewriteHandler() throws Exception
+ {
+ // tag::rewriteHandler[]
+ Server server = new Server();
+ ServerConnector connector = new ServerConnector(server);
+ server.addConnector(connector);
+
+ RewriteHandler rewriteHandler = new RewriteHandler();
+ // Compacts URI paths with double slashes, e.g. /ctx//path/to//resource.
+ rewriteHandler.addRule(new CompactPathRule());
+ // Rewrites */products/* to */p/*.
+ rewriteHandler.addRule(new RewriteRegexRule("/(.*)/product/(.*)", "/$1/p/$2"));
+ // Redirects permanently to a different URI.
+ RedirectRegexRule redirectRule = new RedirectRegexRule("/documentation/(.*)", "https://docs.domain.com/$1");
+ redirectRule.setStatusCode(HttpStatus.MOVED_PERMANENTLY_301);
+ rewriteHandler.addRule(redirectRule);
+
+ // Link the RewriteHandler to the Server.
+ server.setHandler(rewriteHandler);
+
+ // Create a ContextHandlerCollection to hold contexts.
+ ContextHandlerCollection contextCollection = new ContextHandlerCollection();
+ // Link the ContextHandlerCollection to the RewriteHandler.
+ rewriteHandler.setHandler(contextCollection);
+
+ server.start();
+ // end::rewriteHandler[]
+ }
+
+ public void statsHandler() throws Exception
+ {
+ // tag::statsHandler[]
+ Server server = new Server();
+ ServerConnector connector = new ServerConnector(server);
+ server.addConnector(connector);
+
+ StatisticsHandler statsHandler = new StatisticsHandler();
+
+ // Link the StatisticsHandler to the Server.
+ server.setHandler(statsHandler);
+
+ // Create a ContextHandlerCollection to hold contexts.
+ ContextHandlerCollection contextCollection = new ContextHandlerCollection();
+ // Link the ContextHandlerCollection to the StatisticsHandler.
+ statsHandler.setHandler(contextCollection);
+
+ server.start();
+ // end::statsHandler[]
+ }
+
+ public void defaultHandler() throws Exception
+ {
+ // tag::defaultHandler[]
+ Server server = new Server();
+ Connector connector = new ServerConnector(server);
+ server.addConnector(connector);
+
+ // Create a HandlerList.
+ HandlerList handlerList = new HandlerList();
+
+ // Add as first a ContextHandlerCollection to manage contexts.
+ ContextHandlerCollection contexts = new ContextHandlerCollection();
+ handlerList.addHandler(contexts);
+
+ // Add as last a DefaultHandler.
+ DefaultHandler defaultHandler = new DefaultHandler();
+ handlerList.addHandler(defaultHandler);
+
+ // Link the HandlerList to the Server.
+ server.setHandler(handlerList);
+
+ server.start();
+ // end::defaultHandler[]
}
}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java
index 67c7c1b3efb..38f76792078 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java
@@ -47,7 +47,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
*
* This handle will deal with unhandled requests in the server.
* For requests for favicon.ico, the Jetty icon is served.
- * For reqests to '/' a 404 with a list of known contexts is served.
+ * For requests to '/' a 404 with a list of known contexts is served.
* For all other requests a normal 404 is served.
*/
public class DefaultHandler extends AbstractHandler
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java
index bef74df7b94..450ced06987 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java
@@ -47,6 +47,8 @@ public class SecuredRedirectHandler extends AbstractHandler
return;
}
+ baseRequest.setHandled(true);
+
HttpConfiguration httpConfig = channel.getHttpConfiguration();
if (httpConfig == null)
{
@@ -68,7 +70,5 @@ public class SecuredRedirectHandler extends AbstractHandler
{
response.sendError(HttpStatus.FORBIDDEN_403, "Not Secure");
}
-
- baseRequest.setHandled(true);
}
}
From 5b1d4d492ee970ae000b0dbc670b3501ea3ff33d Mon Sep 17 00:00:00 2001
From: Chris Walker
Date: Fri, 20 Mar 2020 15:39:11 -0500
Subject: [PATCH 069/101] Manual move of Weld documentation changes.
---
.../old_docs/frameworks/weld.adoc | 143 ++++++++++++++----
1 file changed, 113 insertions(+), 30 deletions(-)
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/weld.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/weld.adoc
index dd48cc34531..57f35e0a6bb 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/weld.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/weld.adoc
@@ -1,19 +1,19 @@
//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+// ========================================================================
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
//
-// This program and the accompanying materials are made available under
-// the terms of the Eclipse Public License 2.0 which is available at
-// https://www.eclipse.org/legal/epl-2.0
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
//
-// This Source Code may also be made available under the following
-// Secondary Licenses when the conditions for such availability set
-// forth in the Eclipse Public License, v. 2.0 are satisfied:
-// the Apache License v2.0 which is available at
-// https://www.apache.org/licenses/LICENSE-2.0
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
//
-// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
-// ========================================================================
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
//
[[framework-weld]]
@@ -25,30 +25,74 @@ It is easily configured with Jetty 9.
[[weld-setup-distro]]
==== Weld Setup
-The easiest way to configure weld is within the jetty distribution itself:
+The easiest way to configure weld is within the Jetty distribution itself.
+This can be accomplished either by enabling one of the startup link:#startup-modules[modules] for Weld, or by creating/editing a `jetty-web.xml` descriptor (see also https://www.eclipse.org/jetty/documentation/current/jetty-web-xml-config.html[Jetty XML Reference]).
-1. Enable the startup link:#startup-modules[module] called "cdi".
-2. Ensure your `WEB-INF/web.xml` contains the following:
-+
-[source, xml, subs="{sub-order}"]
-----
-
- org.jboss.weld.environment.servlet.Listener
-
+===== Jetty Weld Modules
-
- Object factory for the CDI Bean Manager
- BeanManager
- javax.enterprise.inject.spi.BeanManager
-
-----
+====== Jetty `cdi-decorate` Module
-That should be it so when you start up your jetty distribution with the webapp you should see output similar to the following (providing your logging is the default configuration):
+Since Jetty 9.4.20 and Weld 3.1.2.Final, the Weld/Jetty integration uses the jetty `cdi-decorate` module.
+To activate this module in Jetty the `cdi-decorate` module needs activated on the command line, which can be done as follows:
+
+-------------------------
+cd $JETTY_BASE
+java -jar $JETTY_HOME/start.jar --add-to-start=cdi-decorate
+-------------------------
+
+====== Jetty `cdi2` Module
+
+For versions prior to Jetty 9.4.20 and Weld 3.1.2, the Weld/Jetty integration required some internal Jetty APIs to be made visible to the web application.
+This can be done using the deprecated `cdi2` module either by activating the `cdi2` module:
+
+-------------------------
+cd $JETTY_BASE
+java -jar $JETTY_HOME/start.jar --add-to-start=cdi2
+-------------------------
+
+
+===== Jetty-Web XML
+
+
+[source.XML, xml]
+-------------------------------------------------------------
+
+
+
+
+ -org.eclipse.jetty.util.Decorator
+
+
+ -org.eclipse.jetty.util.DecoratedObjectFactory
+
+
+ -org.eclipse.jetty.server.handler.ContextHandler.
+
+
+ -org.eclipse.jetty.server.handler.ContextHandler
+
+
+ -org.eclipse.jetty.servlet.ServletContextHandler
+
+
+-------------------------------------------------------------
+
+//TODO Fix for 10
+____
+[TIP]
+Directly modifying the web application classpath via `jetty-web.xml` will not work for Jetty 10.0.0 and later.
+____
+
+===== Jetty `cdi-spi` Module
+Since Jetty 9.4.20 the Jetty `cdi-spi` module has been available that integrates any compliant CDI implementation by directly calling the CDI SPI.
+Since Weld support specific Jetty integration, it is not recommended to use this module with Weld.
+
+When you start up your Jetty distribution with the webapp you should see output similar to the following (providing your logging is the default configuration):
[source, screen, subs="{sub-order}"]
....
2015-06-18 12:13:54.924:INFO::main: Logging initialized @485ms
-2015-06-18 12:13:55.231:INFO:oejs.Server:main: jetty-9.3.1-SNAPSHOT
+2015-06-18 12:13:55.231:INFO:oejs.Server:main: jetty-{VERSION}
2015-06-18 12:13:55.264:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///tmp/cdi-demo/webapps/] at interval 1
2015-06-18 12:13:55.607:WARN:oeja.AnnotationConfiguration:main: ServletContainerInitializers: detected. Class hierarchy: empty
Jun 18, 2015 12:13:55 PM org.jboss.weld.environment.servlet.EnhancedListener onStartup
@@ -80,4 +124,43 @@ INFO: WELD-ENV-001009: org.jboss.weld.environment.servlet.Listener used for Serv
....
-For use with the jetty-maven-plugin, the best idea is to make the org.jboss.weld.servlet:weld-servlet artifact a _plugin_ dependency (__not__ a webapp dependency), then follow step 2 above.
+For use with the jetty-maven-plugin, the best idea is to make the org.jboss.weld.servlet:weld-servlet artifact a _plugin_ dependency (__not__ a webapp dependency).
+
+[[weld-embedded]]
+==== Embedded Jetty
+
+When starting embedded Jetty programmatically from the `main` method it is necessary to register Weld's listener:
+
+[source.JAVA, java]
+----
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+ Server jetty = new Server(8080);
+ WebAppContext context = new WebAppContext();
+ context.setContextPath("/");
+ context.setResourceBase("src/main/resources");
+ jetty.setHandler(context);
+ context.addServlet(HelloWorldServlet.class, "/*");
+
+ context.addEventListener(new DecoratingListener()); # <1>
+ context.addEventListener(new Listener()); # <2>
+
+ jetty.start();
+ jetty.join();
+ }
+
+ public static class HelloWorldServlet extends HttpServlet {
+
+ @Inject BeanManager manager;
+
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ resp.setContentType("text/plain");
+ resp.getWriter().append("Hello from " + manager);
+ }
+ }
+}
+
+<1> Jetty's `org.eclipse.jetty.webapp.DecoratingListener` registered programmatically (since Jetty-9.4.20)
+<2> Weld's `org.jboss.weld.environment.servlet.Listener` registered programmatically
+----
From 34a2d6e673fc1d72606a3bc45cf783f17c767c80 Mon Sep 17 00:00:00 2001
From: olivier lamy
Date: Sat, 11 Apr 2020 10:41:47 +1000
Subject: [PATCH 070/101] fix header
Signed-off-by: olivier lamy
---
.../old_docs/frameworks/weld.adoc | 24 +++++++++----------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/weld.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/weld.adoc
index 57f35e0a6bb..cfd3e7fccd6 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/weld.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/frameworks/weld.adoc
@@ -1,19 +1,19 @@
//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-// ========================================================================
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
-// The Eclipse Public License is available at
-// http://www.eclipse.org/legal/epl-v10.html
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
//
-// The Apache License v2.0 is available at
-// http://www.opensource.org/licenses/apache2.0.php
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
//
-// You may elect to redistribute this code under either of these licenses.
-// ========================================================================
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
//
[[framework-weld]]
From 14fda86944ca5bce9d5f090e22dd1e3ab67d8b68 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Mon, 13 Apr 2020 12:30:46 +0200
Subject: [PATCH 071/101] Improvements to the Jetty server documentation.
Written HTTP/2 low-level section.
Signed-off-by: Simone Bordet
---
.../client/http2/client-http2.adoc | 118 +------
.../main/asciidoc/embedded-guide/http2.adoc | 118 +++++++
.../server/http2/server-http2.adoc | 170 +++++++++-
.../src/main/java/embedded/HTTP2Docs.java | 97 ++++++
.../client/http2/HTTP2ClientDocs.java | 60 +---
.../server/http2/HTTP2ServerDocs.java | 316 ++++++++++++++++++
.../org/eclipse/jetty/http2/api/Stream.java | 33 +-
.../RawHTTP2ServerConnectionFactory.java | 5 +
8 files changed, 753 insertions(+), 164 deletions(-)
create mode 100644 jetty-documentation/src/main/asciidoc/embedded-guide/http2.adoc
create mode 100644 jetty-documentation/src/main/java/embedded/HTTP2Docs.java
create mode 100644 jetty-documentation/src/main/java/embedded/server/http2/HTTP2ServerDocs.java
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http2/client-http2.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http2/client-http2.adoc
index e7c48438eaa..8cdc48187e6 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/client/http2/client-http2.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/client/http2/client-http2.adoc
@@ -33,6 +33,8 @@ The HTTP/2 client library has been designed for those applications that need
low-level access to HTTP/2 features such as _sessions_, _streams_ and
_frames_, and this is quite a rare use case.
+See also the correspondent xref:eg-server-http2[HTTP/2 server library].
+
[[eg-client-http2-intro]]
==== Introducing HTTP2Client
@@ -63,79 +65,24 @@ it should stop the `HTTP2Client` instance (or instances) that were started:
include::../../{doc_code}/embedded/client/http2/HTTP2ClientDocs.java[tags=stop]
----
-`HTTP2Client` allows client applications to connect to a HTTP/2 server.
-A _session_ represents a single TCP connection to a HTTP/2 server and is defined
-by class `org.eclipse.jetty.http2.api.Session`.
+`HTTP2Client` allows client applications to connect to an HTTP/2 server.
+A _session_ represents a single TCP connection to an HTTP/2 server and is
+defined by class `org.eclipse.jetty.http2.api.Session`.
A _session_ typically has a long life - once the TCP connection is established,
it remains open until it is not used anymore (and therefore it is closed by
the idle timeout mechanism), until a fatal error occurs (for example, a network
failure), or if one of the peers decides unilaterally to close the TCP
connection.
-HTTP/2 is a multiplexed protocol: it allows multiple HTTP/2 requests to be sent
-on the same TCP connection.
-Each request/response cycle is represented by a _stream_.
-Therefore, a single _session_ manages multiple concurrent _streams_.
-A _stream_ has typically a very short life compared to the _session_: a
-_stream_ only exists for the duration of the request/response cycle and then
-disappears.
+include::../../http2.adoc[tag=multiplex]
[[eg-client-http2-flow-control]]
===== HTTP/2 Flow Control
-The HTTP/2 protocol is _flow controlled_ (see
-link:https://tools.ietf.org/html/rfc7540#section-5.2[the specification]).
-This means that a sender and a receiver maintain a _flow control window_ that
-tracks the number of data bytes sent and received, respectively.
-When a sender sends data bytes, it reduces its flow control window. When a
-receiver receives data bytes, it also reduces its flow control window, and
-then passes the received data bytes to the application.
-The application consumes the data bytes and tells back the receiver that it
-has consumed the data bytes.
-The receiver then enlarges the flow control window, and arranges to send a
-message to the sender with the number of bytes consumed, so that the sender
-can enlarge its flow control window.
-
-A sender can send data bytes up to its whole flow control window, then it must
-stop sending until it receives a message from the receiver that the data bytes
-have been consumed, which enlarges the flow control window, which allows the
-sender to send more data bytes.
-
-HTTP/2 defines _two_ flow control windows: one for each _session_, and one
-for each _stream_. Let's see with an example how they interact, assuming that
-in this example the session flow control window is 120 bytes and the stream
-flow control window is 100 bytes.
-
-The sender opens a session, and then opens `stream_1` on that session, and
-sends `80` data bytes.
-At this point the session flow control window is `40` bytes (`120 - 80`), and
-``stream_1``'s flow control window is `20` bytes (`100 - 80`).
-The sender now opens `stream_2` on the same session and sends `40` data bytes.
-At this point, the session flow control window is `0` bytes (`40 - 40`),
-while ``stream_2``'s flow control window is `60` (`100 - 40`).
-Since now the session flow control window is `0`, the sender cannot send more
-data bytes, neither on `stream_1` nor on `stream_2` despite both have their
-stream flow control windows greater than `0`.
-
-The receiver consumes ``stream_2``'s `40` data bytes and sends a message to
-the sender with this information.
-At this point, the session flow control window is `40` (`0 + 40`),
-``stream_1``'s flow control window is still `20` and ``stream_2``'s flow
-control window is `100` (`60 + 40`).
-If the sender opens `stream_3` and would like to send 50 data bytes, it would
-only be able to send `40` because that is the maximum allowed by the session
-flow control window at this point.
-
-It is therefore very important that applications notify the fact that they
-have consumed data bytes as soon as possible, so that the implementation
-(the receiver) can send a message to the sender (in the form of a
-`WINDOW_UPDATE` frame) with the information to enlarge the flow control
-window, therefore reducing the possibility that sender stalls due to the flow
-control windows being reduced to `0`.
-This is discussed in details in xref:eg-client-http2-response[this section].
-
-
+include::../../http2.adoc[tag=flowControl]
+How a client application should handle HTTP/2 flow control is discussed in
+details in xref:eg-client-http2-response[this section].
[[eg-client-http2-connect]]
==== Connecting to the Server
@@ -192,7 +139,7 @@ Sending an HTTP request to the server, and receiving a response, creates a
_stream_ that encapsulates the exchange of HTTP/2 frames that compose the
request and the response.
-In order to send a HTTP request to the server, the client must send a
+In order to send an HTTP request to the server, the client must send a
`HEADERS` frame.
`HEADERS` frames carry the request method, the request URI and the request
headers.
@@ -230,9 +177,9 @@ Use the `Callback` APIs or `CompletableFuture` APIs to ensure that the second
Response events are delivered to the `Stream.Listener` passed to
`Session.newStream(...)`.
-A HTTP response is typically composed of a `HEADERS` frame containing the HTTP
-status code and the response headers, and optionally one or more `DATA` frames
-containing the response content bytes.
+An HTTP response is typically composed of a `HEADERS` frame containing the
+HTTP status code and the response headers, and optionally one or more `DATA`
+frames containing the response content bytes.
The HTTP/2 protocol also supports response trailers (that is, headers that are
sent after the response content) that also are sent using a `HEADERS` frame.
@@ -245,38 +192,7 @@ by implementing the relevant methods in `Stream.Listener`:
include::../../{doc_code}/embedded/client/http2/HTTP2ClientDocs.java[tags=responseListener]
----
-NOTE: Returning from the `onData(...)` method implicitly demands for
-more `DATA` frames (unless the one just delivered was the last).
-Additional `DATA` frames may be delivered immediately if they are available
-or later, asynchronously, when they arrive.
-
-Client applications that consume the content buffer within `onData(...)`
-(for example, writing it to a file, or copying the bytes to another storage)
-should succeed the callback as soon as they have consumed the content buffer.
-This allows the implementation to reuse the buffer, reducing the memory
-requirements needed to handle the response content.
-
-Alternatively, a client application may store away _both_ the buffer and the
-callback to consume the buffer bytes later.
-
-IMPORTANT: Completing the `Callback` is very important not only to allow the
-implementation to reuse the buffer, but also tells the implementation to
-enlarge the stream and session flow control windows so that the server will
-be able to send more `DATA` frames without stalling.
-
-Client applications can also precisely control _when_ to demand more `DATA`
-frames, by implementing the `onDataDemanded(...)` method instead of
-`onData(...)`:
-
-[source,java,indent=0]
-----
-include::../../{doc_code}/embedded/client/http2/HTTP2ClientDocs.java[tags=responseDataDemanded]
-----
-
-IMPORTANT: Applications that implement `onDataDemanded(...)` must remember
-to call `Stream.demand(...)`. If they don't, the implementation will not
-deliver `DATA` frames and the application will stall threadlessly until an
-idle timeout fires to close the stream or the session.
+include::../../http2.adoc[tag=apiFlowControl]
[[eg-client-http2-reset]]
==== Resetting a Request or Response
@@ -298,9 +214,9 @@ include::../../{doc_code}/embedded/client/http2/HTTP2ClientDocs.java[tags=reset]
HTTP/2 servers have the ability to push resources related to a primary
resource.
-When a HTTP/2 server pushes a resource, it send to the client a `PUSH_PROMISE`
-frame that contains the request URI and headers that a client would use to
-request explicitly that resource.
+When an HTTP/2 server pushes a resource, it sends to the client a
+`PUSH_PROMISE` frame that contains the request URI and headers that a client
+would use to request explicitly that resource.
Client applications can be configured to tell the server to never push
resources, see xref:eg-client-http2-configure[this section].
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/http2.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/http2.adoc
new file mode 100644
index 00000000000..b77a1768235
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/http2.adoc
@@ -0,0 +1,118 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+// Snippets of HTTP/2 documentation that are common between client and server.
+
+tag::multiplex[]
+HTTP/2 is a multiplexed protocol: it allows multiple HTTP/2 requests to be sent
+on the same TCP connection.
+Each request/response cycle is represented by a _stream_.
+Therefore, a single _session_ manages multiple concurrent _streams_.
+A _stream_ has typically a very short life compared to the _session_: a
+_stream_ only exists for the duration of the request/response cycle and then
+disappears.
+end::multiplex[]
+
+tag::flowControl[]
+The HTTP/2 protocol is _flow controlled_ (see
+link:https://tools.ietf.org/html/rfc7540#section-5.2[the specification]).
+This means that a sender and a receiver maintain a _flow control window_ that
+tracks the number of data bytes sent and received, respectively.
+When a sender sends data bytes, it reduces its flow control window. When a
+receiver receives data bytes, it also reduces its flow control window, and
+then passes the received data bytes to the application.
+The application consumes the data bytes and tells back the receiver that it
+has consumed the data bytes.
+The receiver then enlarges the flow control window, and arranges to send a
+message to the sender with the number of bytes consumed, so that the sender
+can enlarge its flow control window.
+
+A sender can send data bytes up to its whole flow control window, then it must
+stop sending until it receives a message from the receiver that the data bytes
+have been consumed, which enlarges the flow control window, which allows the
+sender to send more data bytes.
+
+HTTP/2 defines _two_ flow control windows: one for each _session_, and one
+for each _stream_. Let's see with an example how they interact, assuming that
+in this example the session flow control window is 120 bytes and the stream
+flow control window is 100 bytes.
+
+The sender opens a session, and then opens `stream_1` on that session, and
+sends `80` data bytes.
+At this point the session flow control window is `40` bytes (`120 - 80`), and
+``stream_1``'s flow control window is `20` bytes (`100 - 80`).
+The sender now opens `stream_2` on the same session and sends `40` data bytes.
+At this point, the session flow control window is `0` bytes (`40 - 40`),
+while ``stream_2``'s flow control window is `60` (`100 - 40`).
+Since now the session flow control window is `0`, the sender cannot send more
+data bytes, neither on `stream_1` nor on `stream_2` despite both have their
+stream flow control windows greater than `0`.
+
+The receiver consumes ``stream_2``'s `40` data bytes and sends a message to
+the sender with this information.
+At this point, the session flow control window is `40` (`0 + 40`),
+``stream_1``'s flow control window is still `20` and ``stream_2``'s flow
+control window is `100` (`60 + 40`).
+If the sender opens `stream_3` and would like to send 50 data bytes, it would
+only be able to send `40` because that is the maximum allowed by the session
+flow control window at this point.
+
+It is therefore very important that applications notify the fact that they
+have consumed data bytes as soon as possible, so that the implementation
+(the receiver) can send a message to the sender (in the form of a
+`WINDOW_UPDATE` frame) with the information to enlarge the flow control
+window, therefore reducing the possibility that sender stalls due to the flow
+control windows being reduced to `0`.
+end::flowControl[]
+
+tag::apiFlowControl[]
+NOTE: Returning from the `onData(...)` method implicitly demands for
+more `DATA` frames (unless the one just delivered was the last).
+Additional `DATA` frames may be delivered immediately if they are available
+or later, asynchronously, when they arrive.
+
+Applications that consume the content buffer within `onData(...)`
+(for example, writing it to a file, or copying the bytes to another storage)
+should succeed the callback as soon as they have consumed the content buffer.
+This allows the implementation to reuse the buffer, reducing the memory
+requirements needed to handle the content buffers.
+
+Alternatively, a client application may store away _both_ the buffer and the
+callback to consume the buffer bytes later, or pass _both_ the buffer and
+the callback to another asynchronous API (this is typical in proxy
+applications).
+
+IMPORTANT: Completing the `Callback` is very important not only to allow the
+implementation to reuse the buffer, but also tells the implementation to
+enlarge the stream and session flow control windows so that the sender will
+be able to send more `DATA` frames without stalling.
+
+Applications can also precisely control _when_ to demand more `DATA`
+frames, by implementing the `onDataDemanded(...)` method instead of
+`onData(...)`:
+
+[source,java,indent=0]
+----
+include::{doc_code}/embedded/HTTP2Docs.java[tags=dataDemanded]
+----
+
+IMPORTANT: Applications that implement `onDataDemanded(...)` must remember
+to call `Stream.demand(...)`. If they don't, the implementation will not
+deliver `DATA` frames and the application will stall threadlessly until an
+idle timeout fires to close the stream or the session.
+end::apiFlowControl[]
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http2/server-http2.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http2/server-http2.adoc
index c6728c6ef0e..470ac968793 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http2/server-http2.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http2/server-http2.adoc
@@ -17,6 +17,172 @@
//
[[eg-server-http2]]
-=== HTTP/2 Server Libraries
+=== HTTP/2 Server Library
-TODO
+In the vast majority of cases, server applications should use the generic,
+high-level, xref:eg-server-http[HTTP server library] that also provides
+HTTP/2 support via the HTTP/2 ``ConnectionFactory``s as described in details
+xref:eg-server-http-connector-protocol-http2[here].
+
+The low-level HTTP/2 server library has been designed for those applications
+that need low-level access to HTTP/2 features such as _sessions_, _streams_
+and _frames_, and this is quite a rare use case.
+
+See also the correspondent xref:eg-client-http2[HTTP/2 client library].
+
+[[eg-server-http2-intro]]
+==== Introduction
+
+The Maven artifact coordinates for the HTTP/2 client library are the following:
+
+[source,xml,subs=normal]
+----
+
+ org.eclipse.jetty.http2
+ http2-server
+ {version}
+
+----
+
+include::../../http2.adoc[tag=multiplex]
+
+[[eg-server-http2-flow-control]]
+===== HTTP/2 Flow Control
+
+include::../../http2.adoc[tag=flowControl]
+
+How a server application should handle HTTP/2 flow control is discussed in
+details in xref:eg-server-http2-request[this section].
+
+[[eg-server-http2-setup]]
+==== Server Setup
+
+The low-level HTTP/2 support is provided by
+`org.eclipse.jetty.http2.server.RawHTTP2ServerConnectionFactory` and
+`org.eclipse.jetty.http2.api.server.ServerSessionListener`:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http2/HTTP2ServerDocs.java[tags=setup]
+----
+
+Where server applications using the
+xref:eg-server-http[high-level server library] deal with HTTP
+requests and responses in ``Handler``s, server applications using the
+low-level HTTP/2 server library deal directly with HTTP/2 __session__s,
+__stream__s and __frame__s in a `ServerSessionListener` implementation.
+
+The `ServerSessionListener` interface defines a number of methods that are
+invoked by the implementation upon the occurrence of HTTP/2 events, and that
+server applications can override to react to those events.
+
+Please refer to the `ServerSessionListener`
+link:{JDURL}/org/eclipse/jetty/http2/api/server/ServerSessionListener.html[javadocs]
+for the complete list of events.
+
+The first event is the _accept_ event and happens when a client opens a new
+TCP connection to the server and the server accepts the connection.
+This is the first occasion where server applications have access to the HTTP/2
+`Session` object:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http2/HTTP2ServerDocs.java[tags=accept]
+----
+
+After connecting to the server, a compliant HTTP/2 client must send the
+link:https://tools.ietf.org/html/rfc7540#section-3.5[HTTP/2 client preface],
+and when the server receives it, it generates the _preface_ event on the server.
+This is where server applications can customize the connection settings by
+returning a map of settings that the implementation will send to the client:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http2/HTTP2ServerDocs.java[tags=preface]
+----
+
+[[eg-server-http2-request]]
+==== Receiving a Request
+
+Receiving an HTTP request from the client, and sending a response, creates
+a _stream_ that encapsulates the exchange of HTTP/2 frames that compose the
+request and the response.
+
+An HTTP request is made of a `HEADERS` frame, that carries the request method,
+the request URI and the request headers, and optional `DATA` frames that carry
+the request content.
+
+Receiving the `HEADERS` frame opens the `Stream`:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http2/HTTP2ServerDocs.java[tags=request]
+----
+
+Server applications should return a `Stream.Listener` implementation from
+`onNewStream(...)` to be notified of events generated by the client, such as
+`DATA` frames carrying request content, or a `RST_STREAM` frame indicating
+that the client wants to _reset_ the request, or an idle timeout event
+indicating that the client was supposed to send more frames but it did not.
+
+The example below shows how to receive request content:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http2/HTTP2ServerDocs.java[tags=requestContent]
+----
+
+include::../../http2.adoc[tag=apiFlowControl]
+
+[[eg-server-http2-response]]
+==== Sending a Response
+
+After receiving an HTTP request, a server application must send an HTTP
+response.
+
+An HTTP response is typically composed of a `HEADERS` frame containing the
+HTTP status code and the response headers, and optionally one or more `DATA`
+frames containing the response content bytes.
+
+The HTTP/2 protocol also supports response trailers (that is, headers that
+are sent after the response content) that also are sent using a `HEADERS`
+frame.
+
+A server application can send a response in this way:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http2/HTTP2ServerDocs.java[tags=response;!exclude]
+----
+
+[[eg-server-http2-reset]]
+==== Resetting a Request
+
+A server application may decide that it does not want to accept the request.
+For example, it may throttle the client because it sent too many requests in
+a time window, or the request is invalid (and does not deserve a proper HTTP
+response), etc.
+
+A request can be reset in this way:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http2/HTTP2ServerDocs.java[tags=reset;!exclude]
+----
+
+[[eg-server-http2-push]]
+==== HTTP/2 Push of Resources
+
+A server application may _push_ secondary resources related to a primary
+resource.
+
+A client may inform the server that it does not accept pushed resources
+(see link:https://tools.ietf.org/html/rfc7540#section-8.2[this section]
+of the specification) via a `SETTINGS` frame.
+Server applications must track `SETTINGS` frames and verify whether the
+client supports HTTP/2 push, and only push if the client supports it:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http2/HTTP2ServerDocs.java[tags=push]
+----
diff --git a/jetty-documentation/src/main/java/embedded/HTTP2Docs.java b/jetty-documentation/src/main/java/embedded/HTTP2Docs.java
new file mode 100644
index 00000000000..cd32db6cd05
--- /dev/null
+++ b/jetty-documentation/src/main/java/embedded/HTTP2Docs.java
@@ -0,0 +1,97 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package embedded;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.util.Queue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.util.Callback;
+
+@SuppressWarnings("unused")
+public class HTTP2Docs
+{
+ public void dataDemanded() throws Exception
+ {
+ HTTP2Client http2Client = new HTTP2Client();
+ http2Client.start();
+ SocketAddress serverAddress = new InetSocketAddress("localhost", 8080);
+ CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener.Adapter());
+ Session session = sessionCF.get();
+
+ HttpFields requestHeaders = new HttpFields();
+ requestHeaders.put(HttpHeader.USER_AGENT, "Jetty HTTP2Client {version}");
+ MetaData.Request request = new MetaData.Request("GET", new HttpURI("http://localhost:8080/path"), HttpVersion.HTTP_2, requestHeaders);
+ HeadersFrame headersFrame = new HeadersFrame(request, null, true);
+
+ // tag::dataDemanded[]
+ class Chunk
+ {
+ private final ByteBuffer buffer;
+ private final Callback callback;
+
+ Chunk(ByteBuffer buffer, Callback callback)
+ {
+ this.buffer = buffer;
+ this.callback = callback;
+ }
+ }
+
+ // A queue that consumers poll to consume content asynchronously.
+ Queue dataQueue = new ConcurrentLinkedQueue<>();
+
+ // Implementation of Stream.Listener.onDataDemanded(...)
+ // in case of asynchronous content consumption and demand.
+ Stream.Listener listener = new Stream.Listener.Adapter()
+ {
+ @Override
+ public void onDataDemanded(Stream stream, DataFrame frame, Callback callback)
+ {
+ // Get the content buffer.
+ ByteBuffer buffer = frame.getData();
+
+ // Store buffer to consume it asynchronously, and wrap the callback.
+ dataQueue.offer(new Chunk(buffer, Callback.from(() ->
+ {
+ // When the buffer has been consumed, then:
+ // A) succeed the nested callback.
+ callback.succeeded();
+ // B) demand more DATA frames.
+ stream.demand(1);
+ }, callback::failed)));
+
+ // Do not demand more content here, to avoid to overflow the queue.
+ }
+ };
+ // end::dataDemanded[]
+ }
+}
diff --git a/jetty-documentation/src/main/java/embedded/client/http2/HTTP2ClientDocs.java b/jetty-documentation/src/main/java/embedded/client/http2/HTTP2ClientDocs.java
index c5abbf7eacb..f15637ad8e5 100644
--- a/jetty-documentation/src/main/java/embedded/client/http2/HTTP2ClientDocs.java
+++ b/jetty-documentation/src/main/java/embedded/client/http2/HTTP2ClientDocs.java
@@ -24,9 +24,7 @@ import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
-import java.util.Queue;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ConcurrentLinkedQueue;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
@@ -199,11 +197,11 @@ public class HTTP2ClientDocs
// Send the first DATA frame on the stream, with endStream=false
// to signal that there are more frames in this stream.
- CompletableFuture dataCF1 = stream.data(new DataFrame(stream.getId(), buffer1, false));
+ CompletableFuture dataCF1 = stream.data(new DataFrame(stream.getId(), buffer1, false));
// Only when the first chunk has been sent we can send the second,
// with endStream=true to signal that there are no more frames.
- dataCF1.thenCompose(ignored -> stream.data(new DataFrame(stream.getId(), buffer2, true)));
+ dataCF1.thenCompose(s -> s.data(new DataFrame(s.getId(), buffer2, true)));
// end::newStreamWithData[]
}
@@ -260,60 +258,6 @@ public class HTTP2ClientDocs
// end::responseListener[]
}
- public void responseDataDemanded() throws Exception
- {
- HTTP2Client http2Client = new HTTP2Client();
- http2Client.start();
- SocketAddress serverAddress = new InetSocketAddress("localhost", 8080);
- CompletableFuture sessionCF = http2Client.connect(serverAddress, new Session.Listener.Adapter());
- Session session = sessionCF.get();
-
- HttpFields requestHeaders = new HttpFields();
- requestHeaders.put(HttpHeader.USER_AGENT, "Jetty HTTP2Client {version}");
- MetaData.Request request = new MetaData.Request("GET", new HttpURI("http://localhost:8080/path"), HttpVersion.HTTP_2, requestHeaders);
- HeadersFrame headersFrame = new HeadersFrame(request, null, true);
-
- // tag::responseDataDemanded[]
- class Chunk
- {
- private final ByteBuffer buffer;
- private final Callback callback;
-
- Chunk(ByteBuffer buffer, Callback callback)
- {
- this.buffer = buffer;
- this.callback = callback;
- }
- }
-
- // A queue that consumers poll to consume content asynchronously.
- Queue dataQueue = new ConcurrentLinkedQueue<>();
-
- // Open a Stream by sending the HEADERS frame.
- session.newStream(headersFrame, new Stream.Listener.Adapter()
- {
- @Override
- public void onDataDemanded(Stream stream, DataFrame frame, Callback callback)
- {
- // Get the content buffer.
- ByteBuffer buffer = frame.getData();
-
- // Store buffer to consume it asynchronously, and wrap the callback.
- dataQueue.offer(new Chunk(buffer, Callback.from(() ->
- {
- // When the buffer has been consumed, then:
- // A) succeed the nested callback.
- callback.succeeded();
- // B) demand more DATA frames.
- stream.demand(1);
- }, callback::failed)));
-
- // Do not demand more content here, to avoid to overflow the queue.
- }
- });
- // end::responseDataDemanded[]
- }
-
public void reset() throws Exception
{
HTTP2Client http2Client = new HTTP2Client();
diff --git a/jetty-documentation/src/main/java/embedded/server/http2/HTTP2ServerDocs.java b/jetty-documentation/src/main/java/embedded/server/http2/HTTP2ServerDocs.java
new file mode 100644
index 00000000000..5b6658f76a3
--- /dev/null
+++ b/jetty-documentation/src/main/java/embedded/server/http2/HTTP2ServerDocs.java
@@ -0,0 +1,316 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package embedded.server.http2;
+
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.ErrorCode;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.api.server.ServerSessionListener;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.http2.frames.PushPromiseFrame;
+import org.eclipse.jetty.http2.frames.ResetFrame;
+import org.eclipse.jetty.http2.frames.SettingsFrame;
+import org.eclipse.jetty.http2.server.RawHTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.resource.Resource;
+
+import static java.lang.System.Logger.Level.INFO;
+
+@SuppressWarnings("unused")
+public class HTTP2ServerDocs
+{
+ public void setup() throws Exception
+ {
+ // tag::setup[]
+ // Create a Server instance.
+ Server server = new Server();
+
+ ServerSessionListener sessionListener = new ServerSessionListener.Adapter();
+
+ // Create a ServerConnector with RawHTTP2ServerConnectionFactory.
+ RawHTTP2ServerConnectionFactory http2 = new RawHTTP2ServerConnectionFactory(sessionListener);
+
+ // Configure RawHTTP2ServerConnectionFactory, for example:
+
+ // Configure the max number of concurrent requests.
+ http2.setMaxConcurrentStreams(128);
+ // Enable support for CONNECT.
+ http2.setConnectProtocolEnabled(true);
+
+ // Create the ServerConnector.
+ ServerConnector connector = new ServerConnector(server, http2);
+
+ // Add the Connector to the Server
+ server.addConnector(connector);
+
+ // Start the Server so it starts accepting connections from clients.
+ server.start();
+ // end::setup[]
+ }
+
+ public void accept()
+ {
+ // tag::accept[]
+ ServerSessionListener sessionListener = new ServerSessionListener.Adapter()
+ {
+ @Override
+ public void onAccept(Session session)
+ {
+ InetSocketAddress remoteAddress = session.getRemoteAddress();
+ System.getLogger("http2").log(INFO, "Connection from {0}", remoteAddress);
+ }
+ };
+ // end::accept[]
+ }
+
+ public void preface()
+ {
+ // tag::preface[]
+ ServerSessionListener sessionListener = new ServerSessionListener.Adapter()
+ {
+ @Override
+ public Map onPreface(Session session)
+ {
+ // Customize the settings, for example:
+ Map settings = new HashMap<>();
+
+ // Tell the client that HTTP/2 push is disabled.
+ settings.put(SettingsFrame.ENABLE_PUSH, 0);
+
+ return settings;
+ }
+ };
+ // end::preface[]
+ }
+
+ public void request()
+ {
+ // tag::request[]
+ ServerSessionListener sessionListener = new ServerSessionListener.Adapter()
+ {
+ @Override
+ public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+ {
+ // This is the "new stream" event, so it's guaranteed to be a request.
+ MetaData.Request request = (MetaData.Request)frame.getMetaData();
+
+ // Return a Stream.Listener to handle the request events,
+ // for example request content events or a request reset.
+ return new Stream.Listener.Adapter();
+ }
+ };
+ // end::request[]
+ }
+
+ public void requestContent()
+ {
+ // tag::requestContent[]
+ ServerSessionListener sessionListener = new ServerSessionListener.Adapter()
+ {
+ @Override
+ public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+ {
+ MetaData.Request request = (MetaData.Request)frame.getMetaData();
+ // Return a Stream.Listener to handle the request events.
+ return new Stream.Listener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataFrame frame, Callback callback)
+ {
+ // Get the content buffer.
+ ByteBuffer buffer = frame.getData();
+
+ // Consume the buffer, here - as an example - just log it.
+ System.getLogger("http2").log(INFO, "Consuming buffer {0}", buffer);
+
+ // Tell the implementation that the buffer has been consumed.
+ callback.succeeded();
+
+ // By returning from the method, implicitly tell the implementation
+ // to deliver to this method more DATA frames when they are available.
+ }
+ };
+ }
+ };
+ // end::requestContent[]
+ }
+
+ public void response()
+ {
+ // tag::response[]
+ ServerSessionListener sessionListener = new ServerSessionListener.Adapter()
+ {
+ @Override
+ public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+ {
+ // Send a response after reading the request.
+ MetaData.Request request = (MetaData.Request)frame.getMetaData();
+ if (frame.isEndStream())
+ {
+ respond(stream, request);
+ return null;
+ }
+ else
+ {
+ return new Stream.Listener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataFrame frame, Callback callback)
+ {
+ // Consume the request content.
+ callback.succeeded();
+ if (frame.isEndStream())
+ respond(stream, request);
+ }
+ };
+ }
+ }
+
+ private void respond(Stream stream, MetaData.Request request)
+ {
+ // Prepare the response HEADERS frame.
+
+ // The response HTTP status and HTTP headers.
+ MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields());
+
+ if (HttpMethod.GET.is(request.getMethod()))
+ {
+ // The response content.
+ ByteBuffer resourceBytes = getResourceBytes(request);
+
+ // Send the HEADERS frame with the response status and headers,
+ // and a DATA frame with the response content bytes.
+ stream.headers(new HeadersFrame(stream.getId(), response, null, false))
+ .thenCompose(s -> s.data(new DataFrame(s.getId(), resourceBytes, true)));
+ }
+ else
+ {
+ // Send just the HEADERS frame with the response status and headers.
+ stream.headers(new HeadersFrame(stream.getId(), response, null, true));
+ }
+ }
+ // tag::exclude[]
+
+ private ByteBuffer getResourceBytes(MetaData.Request request)
+ {
+ return ByteBuffer.allocate(1024);
+ }
+ // end::exclude[]
+ };
+ // end::response[]
+ }
+
+ public void reset()
+ {
+ float maxRequestRate = 0F;
+ // tag::reset[]
+ ServerSessionListener sessionListener = new ServerSessionListener.Adapter()
+ {
+ @Override
+ public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+ {
+ float requestRate = calculateRequestRate();
+
+ if (requestRate > maxRequestRate)
+ {
+ stream.reset(new ResetFrame(stream.getId(), ErrorCode.REFUSED_STREAM_ERROR.code), Callback.NOOP);
+ return null;
+ }
+ else
+ {
+ // The request is accepted.
+ MetaData.Request request = (MetaData.Request)frame.getMetaData();
+ // Return a Stream.Listener to handle the request events.
+ return new Stream.Listener.Adapter();
+ }
+ }
+ // tag::exclude[]
+
+ private float calculateRequestRate()
+ {
+ return 0F;
+ }
+ // end::exclude[]
+ };
+ // end::reset[]
+ }
+
+ public void push() throws Exception
+ {
+ // tag::push[]
+ // The favicon bytes.
+ ByteBuffer faviconBuffer = BufferUtil.toBuffer(Resource.newResource("/path/to/favicon.ico"), true);
+
+ ServerSessionListener sessionListener = new ServerSessionListener.Adapter()
+ {
+ // By default, push is enabled.
+ private boolean pushEnabled = true;
+
+ @Override
+ public void onSettings(Session session, SettingsFrame frame)
+ {
+ // Check whether the client sent an ENABLE_PUSH setting.
+ Map settings = frame.getSettings();
+ Integer enablePush = settings.get(SettingsFrame.ENABLE_PUSH);
+ if (enablePush != null)
+ pushEnabled = enablePush == 1;
+ }
+
+ @Override
+ public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
+ {
+ MetaData.Request request = (MetaData.Request)frame.getMetaData();
+ if (pushEnabled && request.getURIString().endsWith("/index.html"))
+ {
+ // Push the favicon.
+ HttpURI pushedURI = new HttpURI(request.getURI());
+ pushedURI.setPath("/favicon.ico");
+ MetaData.Request pushedRequest = new MetaData.Request("GET", pushedURI, HttpVersion.HTTP_2, new HttpFields());
+ PushPromiseFrame promiseFrame = new PushPromiseFrame(stream.getId(), 0, pushedRequest);
+ stream.push(promiseFrame, new Stream.Listener.Adapter())
+ .thenCompose(pushedStream ->
+ {
+ // Send the favicon "response".
+ MetaData.Response pushedResponse = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields());
+ return pushedStream.headers(new HeadersFrame(pushedStream.getId(), pushedResponse, null, false))
+ .thenCompose(pushed -> pushed.data(new DataFrame(pushed.getId(), faviconBuffer, true)));
+ });
+ }
+ // Return a Stream.Listener to handle the request events.
+ return new Stream.Listener.Adapter();
+ }
+ };
+ // end::push[]
+ }
+}
diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Stream.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Stream.java
index ae48a46159a..7cd70196c89 100644
--- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Stream.java
+++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/api/Stream.java
@@ -52,6 +52,19 @@ public interface Stream
*/
public Session getSession();
+ /**
+ *
Sends the given HEADERS {@code frame} representing an HTTP response.
+ *
+ * @param frame the HEADERS frame to send
+ * @return the CompletableFuture that gets notified when the frame has been sent
+ */
+ public default CompletableFuture headers(HeadersFrame frame)
+ {
+ Promise.Completable result = new Promise.Completable<>();
+ headers(frame, Callback.from(() -> result.succeeded(this), result::failed));
+ return result;
+ }
+
/**
*
Sends the given HEADERS {@code frame} representing an HTTP response.
*
@@ -60,6 +73,20 @@ public interface Stream
*/
public void headers(HeadersFrame frame, Callback callback);
+ /**
+ *
Sends the given PUSH_PROMISE {@code frame}.
+ *
+ * @param frame the PUSH_PROMISE frame to send
+ * @param listener the listener that gets notified of stream events
+ * @return the CompletableFuture that gets notified of the pushed stream creation
+ */
+ public default CompletableFuture push(PushPromiseFrame frame, Listener listener)
+ {
+ Promise.Completable result = new Promise.Completable<>();
+ push(frame, result, listener);
+ return result;
+ }
+
/**
*
Sends the given PUSH_PROMISE {@code frame}.
*
@@ -75,10 +102,10 @@ public interface Stream
* @param frame the DATA frame to send
* @return the CompletableFuture that gets notified when the frame has been sent
*/
- public default CompletableFuture data(DataFrame frame)
+ public default CompletableFuture data(DataFrame frame)
{
- Callback.Completable result = new Callback.Completable();
- data(frame, result);
+ Promise.Completable result = new Promise.Completable<>();
+ data(frame, Callback.from(() -> result.succeeded(this), result::failed));
return result;
}
diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/RawHTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/RawHTTP2ServerConnectionFactory.java
index 6746c42516e..c4586d17ab9 100644
--- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/RawHTTP2ServerConnectionFactory.java
+++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/RawHTTP2ServerConnectionFactory.java
@@ -37,6 +37,11 @@ public class RawHTTP2ServerConnectionFactory extends AbstractHTTP2ServerConnecti
{
private final ServerSessionListener listener;
+ public RawHTTP2ServerConnectionFactory(ServerSessionListener listener)
+ {
+ this(new HttpConfiguration(), listener);
+ }
+
public RawHTTP2ServerConnectionFactory(HttpConfiguration httpConfiguration, ServerSessionListener listener)
{
super(httpConfiguration);
From 93774ae5648822262f906e1bf8954956190a46cf Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Mon, 13 Apr 2020 14:36:33 +0200
Subject: [PATCH 072/101] Fixed #4766 - SecuredRedirectHandler should extend
HandlerWrapper.
Updated the implementation to extend from HandlerWrapper.
Updated the documentation.
Signed-off-by: Simone Bordet
---
.../server/http/server-http-handler-use.adoc | 20 +++++++-
.../embedded/server/http/HTTPServerDocs.java | 51 +++++++++++++++++++
.../handler/SecuredRedirectHandler.java | 30 +++++------
3 files changed, 84 insertions(+), 17 deletions(-)
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler-use.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler-use.adoc
index 50c46854783..8a0c18903df 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler-use.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler-use.adoc
@@ -418,8 +418,24 @@ Server
[[eg-server-http-handler-use-util-secure-handler]]
===== SecuredRedirectHandler -- Redirect from HTTP to HTTPS
-// TODO: wait for issue #4766
-TODO
+`SecuredRedirectHandler` allows to redirect requests made with the `http`
+scheme (and therefore to the clear-text port) to the `https` scheme (and
+therefore to the encrypted port).
+
+For example a request to `+http://domain.com:8080/path?param=value+` is
+redirected to `+https://domain.com:8443/path?param=value+`.
+
+Server applications must configure a `HttpConfiguration` object with the
+secure scheme and secure port so that `SecuredRedirectHandler` can build
+the redirect URI.
+
+`SecuredRedirectHandler` is typically configured at the server level,
+although it can be configured on a per-context basis.
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=securedHandler]
+----
[[eg-server-http-handler-use-util-default-handler]]
===== DefaultHandler
diff --git a/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java b/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java
index 263d94484f5..d9a8395a693 100644
--- a/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java
+++ b/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java
@@ -53,6 +53,7 @@ import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.server.handler.ResourceHandler;
+import org.eclipse.jetty.server.handler.SecuredRedirectHandler;
import org.eclipse.jetty.server.handler.StatisticsHandler;
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
import org.eclipse.jetty.servlet.DefaultServlet;
@@ -761,6 +762,56 @@ public class HTTPServerDocs
// end::statsHandler[]
}
+ public void securedHandler() throws Exception
+ {
+ // tag::securedHandler[]
+ Server server = new Server();
+
+ // Configure the HttpConfiguration for the clear-text connector.
+ int securePort = 8443;
+ HttpConfiguration httpConfig = new HttpConfiguration();
+ httpConfig.setSecurePort(securePort);
+
+ // The clear-text connector.
+ ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(httpConfig));
+ connector.setPort(8080);
+ server.addConnector(connector);
+
+ // Configure the HttpConfiguration for the encrypted connector.
+ HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig);
+ // Add the SecureRequestCustomizer because we are using TLS.
+ httpConfig.addCustomizer(new SecureRequestCustomizer());
+
+ // The HttpConnectionFactory for the encrypted connector.
+ HttpConnectionFactory http11 = new HttpConnectionFactory(httpsConfig);
+
+ // Configure the SslContextFactory with the keyStore information.
+ SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
+ sslContextFactory.setKeyStorePath("/path/to/keystore");
+ sslContextFactory.setKeyStorePassword("secret");
+
+ // The ConnectionFactory for TLS.
+ SslConnectionFactory tls = new SslConnectionFactory(sslContextFactory, http11.getProtocol());
+
+ // The encrypted connector.
+ ServerConnector secureConnector = new ServerConnector(server, tls, http11);
+ secureConnector.setPort(8443);
+ server.addConnector(secureConnector);
+
+ SecuredRedirectHandler securedHandler = new SecuredRedirectHandler();
+
+ // Link the SecuredRedirectHandler to the Server.
+ server.setHandler(securedHandler);
+
+ // Create a ContextHandlerCollection to hold contexts.
+ ContextHandlerCollection contextCollection = new ContextHandlerCollection();
+ // Link the ContextHandlerCollection to the StatisticsHandler.
+ securedHandler.setHandler(contextCollection);
+
+ server.start();
+ // end::securedHandler[]
+ }
+
public void defaultHandler() throws Exception
{
// tag::defaultHandler[]
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java
index 450ced06987..28dc7a6653e 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java
@@ -30,20 +30,22 @@ import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.URIUtil;
/**
- * Secured Redirect Handler
- *
- * Using information present in the {@link HttpConfiguration}, will attempt to redirect to the {@link HttpConfiguration#getSecureScheme()} and
- * {@link HttpConfiguration#getSecurePort()} for any request that {@link HttpServletRequest#isSecure()} == false.
+ *
SecuredRedirectHandler redirects from {@code http} to {@code https}.
+ *
SecuredRedirectHandler uses the information present in {@link HttpConfiguration}
+ * attempting to redirect to the {@link HttpConfiguration#getSecureScheme()} and
+ * {@link HttpConfiguration#getSecurePort()} for any request that
+ * {@link HttpServletRequest#isSecure()} is false.
*/
-public class SecuredRedirectHandler extends AbstractHandler
+public class SecuredRedirectHandler extends HandlerWrapper
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
HttpChannel channel = baseRequest.getHttpChannel();
- if (baseRequest.isSecure() || (channel == null))
+ if (baseRequest.isSecure() || channel == null)
{
- // nothing to do
+ // Nothing to do here.
+ super.handle(target, baseRequest, request, response);
return;
}
@@ -52,23 +54,21 @@ public class SecuredRedirectHandler extends AbstractHandler
HttpConfiguration httpConfig = channel.getHttpConfiguration();
if (httpConfig == null)
{
- // no config, show error
- response.sendError(HttpStatus.FORBIDDEN_403, "No http configuration available");
+ response.sendError(HttpStatus.FORBIDDEN_403, "Missing HttpConfiguration");
return;
}
- if (httpConfig.getSecurePort() > 0)
+ int securePort = httpConfig.getSecurePort();
+ if (securePort > 0)
{
- String scheme = httpConfig.getSecureScheme();
- int port = httpConfig.getSecurePort();
-
- String url = URIUtil.newURI(scheme, baseRequest.getServerName(), port, baseRequest.getRequestURI(), baseRequest.getQueryString());
+ String secureScheme = httpConfig.getSecureScheme();
+ String url = URIUtil.newURI(secureScheme, baseRequest.getServerName(), securePort, baseRequest.getRequestURI(), baseRequest.getQueryString());
response.setContentLength(0);
response.sendRedirect(url);
}
else
{
- response.sendError(HttpStatus.FORBIDDEN_403, "Not Secure");
+ response.sendError(HttpStatus.FORBIDDEN_403, "HttpConfiguration.securePort not configured");
}
}
}
From 1a234dcf4c47d56cdec8deab21110461efb06c55 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Mon, 13 Apr 2020 14:55:16 +0200
Subject: [PATCH 073/101] Issue #4765 - Review GzipHandler inside
ServletContextHandler.
Removed GzipHandler from ServletContextHandler.
Updated ServletContextHandlerTest.
Signed-off-by: Simone Bordet
---
.../jetty/servlet/ServletContextHandler.java | 42 -------------------
.../servlet/ServletContextHandlerTest.java | 37 +---------------
2 files changed, 2 insertions(+), 77 deletions(-)
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
index d86529440b0..7e611b7803c 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
@@ -95,7 +95,6 @@ public class ServletContextHandler extends ContextHandler
public static final int SESSIONS = 1;
public static final int SECURITY = 2;
- public static final int GZIP = 4;
public static final int NO_SESSIONS = 0;
public static final int NO_SECURITY = 0;
@@ -128,7 +127,6 @@ public class ServletContextHandler extends ContextHandler
protected SessionHandler _sessionHandler;
protected SecurityHandler _securityHandler;
protected ServletHandler _servletHandler;
- protected GzipHandler _gzipHandler;
protected int _options;
protected JspConfigDescriptor _jspConfig;
@@ -281,21 +279,6 @@ public class ServletContextHandler extends ContextHandler
handler = _securityHandler;
}
- // link gzip handler
- if (getGzipHandler() != null)
- {
- while (!(handler.getHandler() instanceof GzipHandler) &&
- !(handler.getHandler() instanceof ServletHandler) &&
- handler.getHandler() instanceof HandlerWrapper)
- {
- handler = (HandlerWrapper)handler.getHandler();
- }
-
- if (handler.getHandler() != _gzipHandler)
- doSetHandler(handler, _gzipHandler);
- handler = _gzipHandler;
- }
-
// link servlet handler
if (getServletHandler() != null)
{
@@ -369,8 +352,6 @@ public class ServletContextHandler extends ContextHandler
/**
* Finish constructing handlers and link them together.
- *
- * @see org.eclipse.jetty.server.handler.ContextHandler#startContext()
*/
@Override
protected void startContext() throws Exception
@@ -440,17 +421,6 @@ public class ServletContextHandler extends ContextHandler
return _sessionHandler;
}
- /**
- * @return Returns the gzipHandler.
- */
- @ManagedAttribute(value = "context gzip handler", readonly = true)
- public GzipHandler getGzipHandler()
- {
- if (_gzipHandler == null && (_options & GZIP) != 0 && !isStarted())
- _gzipHandler = new GzipHandler();
- return _gzipHandler;
- }
-
/**
* Convenience method to add a servlet.
*
@@ -652,16 +622,6 @@ public class ServletContextHandler extends ContextHandler
relinkHandlers();
}
- /**
- * @param gzipHandler The {@link GzipHandler} to set on this context.
- */
- public void setGzipHandler(GzipHandler gzipHandler)
- {
- replaceHandler(_gzipHandler, gzipHandler);
- _gzipHandler = gzipHandler;
- relinkHandlers();
- }
-
/**
* @param servletHandler The servletHandler to set.
*/
@@ -683,8 +643,6 @@ public class ServletContextHandler extends ContextHandler
setSessionHandler((SessionHandler)handler);
else if (handler instanceof SecurityHandler)
setSecurityHandler((SecurityHandler)handler);
- else if (handler instanceof GzipHandler)
- setGzipHandler((GzipHandler)handler);
else if (handler instanceof ServletHandler)
setServletHandler((ServletHandler)handler);
else
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java
index 5df8d48658b..bef8ceef9a4 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java
@@ -79,7 +79,6 @@ import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.server.handler.ResourceHandler;
-import org.eclipse.jetty.server.handler.gzip.GzipHandler;
import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.util.DecoratedObjectFactory;
import org.eclipse.jetty.util.Decorator;
@@ -1630,30 +1629,6 @@ public class ServletContextHandlerTest
assertEquals(extra, context.getSessionHandler().getHandler());
}
- @Test
- public void testGzipHandlerOption() throws Exception
- {
- ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS | ServletContextHandler.GZIP);
- GzipHandler gzip = context.getGzipHandler();
- _server.start();
- assertEquals(context.getSessionHandler(), context.getHandler());
- assertEquals(gzip, context.getSessionHandler().getHandler());
- assertEquals(context.getServletHandler(), gzip.getHandler());
- }
-
- @Test
- public void testGzipHandlerSet() throws Exception
- {
- ServletContextHandler context = new ServletContextHandler();
- context.setSessionHandler(new SessionHandler());
- context.setGzipHandler(new GzipHandler());
- GzipHandler gzip = context.getGzipHandler();
- _server.start();
- assertEquals(context.getSessionHandler(), context.getHandler());
- assertEquals(gzip, context.getSessionHandler().getHandler());
- assertEquals(context.getServletHandler(), gzip.getHandler());
- }
-
@Test
public void testReplaceServletHandlerWithServlet() throws Exception
{
@@ -1689,14 +1664,12 @@ public class ServletContextHandlerTest
@Test
public void testSetSecurityHandler() throws Exception
{
- ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS | ServletContextHandler.SECURITY | ServletContextHandler.GZIP);
+ ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS | ServletContextHandler.SECURITY);
assertNotNull(context.getSessionHandler());
SessionHandler sessionHandler = context.getSessionHandler();
assertNotNull(context.getSecurityHandler());
SecurityHandler securityHandler = context.getSecurityHandler();
- assertNotNull(context.getGzipHandler());
- GzipHandler gzipHandler = context.getGzipHandler();
-
+
//check the handler linking order
HandlerWrapper h = (HandlerWrapper)context.getHandler();
assertSame(h, sessionHandler);
@@ -1704,9 +1677,6 @@ public class ServletContextHandlerTest
h = (HandlerWrapper)h.getHandler();
assertSame(h, securityHandler);
- h = (HandlerWrapper)h.getHandler();
- assertSame(h, gzipHandler);
-
//replace the security handler
SecurityHandler myHandler = new SecurityHandler()
{
@@ -1747,9 +1717,6 @@ public class ServletContextHandlerTest
h = (HandlerWrapper)h.getHandler();
assertSame(h, myHandler);
-
- h = (HandlerWrapper)h.getHandler();
- assertSame(h, gzipHandler);
}
@Test
From dc89f7f264c1503d7a4a70fbdcf23024fe2bd504 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Mon, 13 Apr 2020 17:57:38 +0200
Subject: [PATCH 074/101] Fixes #4764 - HTTP2 Jetty Server does not send back
content-length.
Sending Content-Length header if known at the time of sending the
response headers.
Signed-off-by: Simone Bordet
---
.../http2/client/http/ContentLengthTest.java | 105 ++++++++++++++++++
.../http2/server/HttpTransportOverHTTP2.java | 8 ++
2 files changed, 113 insertions(+)
create mode 100644 jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/ContentLengthTest.java
diff --git a/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/ContentLengthTest.java b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/ContentLengthTest.java
new file mode 100644
index 00000000000..6d0bb511513
--- /dev/null
+++ b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/ContentLengthTest.java
@@ -0,0 +1,105 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.http2.client.http;
+
+import java.io.IOException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.gzip.GzipHandler;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ContentLengthTest extends AbstractTest
+{
+ @ParameterizedTest
+ @ValueSource(strings = {"GET", "HEAD", "POST", "PUT"})
+ public void testZeroContentLengthAddedByServer(String method) throws Exception
+ {
+ start(new EmptyServerHandler());
+
+ ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+ .method(method)
+ .send();
+
+ HttpFields responseHeaders = response.getHeaders();
+ long contentLength = responseHeaders.getLongField(HttpHeader.CONTENT_LENGTH.asString());
+ assertEquals(0, contentLength);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"GET", "HEAD", "POST", "PUT"})
+ public void testContentLengthAddedByServer(String method) throws Exception
+ {
+ byte[] data = new byte[512];
+ start(new EmptyServerHandler()
+ {
+ @Override
+ protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
+ {
+ response.getOutputStream().write(data);
+ }
+ });
+
+ ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+ .method(method)
+ .send();
+
+ HttpFields responseHeaders = response.getHeaders();
+ long contentLength = responseHeaders.getLongField(HttpHeader.CONTENT_LENGTH.asString());
+ assertEquals(data.length, contentLength);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"GET", "HEAD", "POST", "PUT"})
+ public void testGzippedContentLengthAddedByServer(String method) throws Exception
+ {
+ byte[] data = new byte[4096];
+
+ GzipHandler gzipHandler = new GzipHandler();
+ gzipHandler.addIncludedMethods(method);
+ gzipHandler.setMinGzipSize(data.length / 2);
+ gzipHandler.setHandler(new EmptyServerHandler()
+ {
+ @Override
+ protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
+ {
+ response.setContentLength(data.length);
+ response.getOutputStream().write(data);
+ }
+ });
+
+ start(gzipHandler);
+
+ ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
+ .method(method)
+ .send();
+
+ HttpFields responseHeaders = response.getHeaders();
+ long contentLength = responseHeaders.getLongField(HttpHeader.CONTENT_LENGTH.asString());
+ assertTrue(0 < contentLength && contentLength < data.length);
+ }
+}
diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java
index 6504977d5d9..a19a8fa071e 100644
--- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java
+++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java
@@ -23,6 +23,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
@@ -110,6 +111,13 @@ public class HttpTransportOverHTTP2 implements HttpTransport
{
if (commit.compareAndSet(false, true))
{
+ if (lastContent)
+ {
+ HttpFields responseHeaders = info.getFields();
+ if (!responseHeaders.contains(HttpHeader.CONTENT_LENGTH))
+ responseHeaders.put(HttpHeader.CONTENT_LENGTH, String.valueOf(BufferUtil.length(content)));
+ }
+
if (hasContent)
{
Callback commitCallback = new Callback.Nested(callback)
From e17d0f4e98e3ef7a4149936691e9807662f55344 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Mon, 13 Apr 2020 23:54:31 +0200
Subject: [PATCH 075/101] Issue #4765 - Review GzipHandler inside
ServletContextHandler.
Fixed test failures.
Signed-off-by: Simone Bordet
---
.../jetty/servlets/GzipDefaultTest.java | 2 +-
.../org/eclipse/jetty/servlets/GzipTester.java | 18 +++++++++++-------
.../jetty/servlets/IncludedGzipTest.java | 2 +-
3 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipDefaultTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipDefaultTest.java
index 19475c61f29..a30150100c7 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipDefaultTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipDefaultTest.java
@@ -673,7 +673,7 @@ public class GzipDefaultTest
GzipTester tester = new GzipTester(testingdir.getEmptyPathDir(), compressionType);
// Configure Gzip Handler
- tester.getGzipHandler().setExcludedPaths("/bad.txt");
+ tester.getGzipHandler().setExcludedPaths(tester.getContextPath() + "/bad.txt");
tester.getGzipHandler().setIncludedPaths("*.txt");
// Prepare server file
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipTester.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipTester.java
index 28959a4dc42..61bb4fab8be 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipTester.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipTester.java
@@ -44,7 +44,6 @@ import org.eclipse.jetty.http.tools.HttpTester;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlet.ServletTester;
import org.eclipse.jetty.toolchain.test.FS;
@@ -82,28 +81,33 @@ public class GzipTester
private String encoding = "ISO8859_1";
private String userAgent = null;
- private final ServletTester tester = new ServletTester("/context", ServletContextHandler.GZIP);
+ private final GzipHandler gzipHandler = new GzipHandler();
+ private final ServletTester tester = new ServletTester("/context");
private Path testdir;
private String accept;
private String compressionType;
+ public GzipTester(Path testingdir, String compressionType)
+ {
+ this(testingdir, compressionType, compressionType);
+ }
+
public GzipTester(Path testingdir, String compressionType, String accept)
{
this.testdir = testingdir;
this.compressionType = compressionType;
this.accept = accept;
+ this.tester.getServer().insertHandler(gzipHandler);
}
- public GzipTester(Path testingdir, String compressionType)
+ public String getContextPath()
{
- this.testdir = testingdir;
- this.compressionType = compressionType;
- this.accept = compressionType;
+ return tester.getContextPath();
}
public GzipHandler getGzipHandler()
{
- return tester.getContext().getGzipHandler();
+ return gzipHandler;
}
public int getOutputBufferSize()
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludedGzipTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludedGzipTest.java
index 50d7526bf67..df942bb0d60 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludedGzipTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludedGzipTest.java
@@ -90,7 +90,7 @@ public class IncludedGzipTest
GzipHandler gzipHandler = new GzipHandler();
gzipHandler.setMinGzipSize(16);
- tester.getContext().insertHandler(gzipHandler);
+ tester.getServer().insertHandler(gzipHandler);
tester.start();
}
From 2e85b3e169e30d604b1af11a7518086e5545b0ce Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Tue, 14 Apr 2020 11:44:58 +0200
Subject: [PATCH 076/101] Fixes #4764 - HTTP2 Jetty Server does not send back
content-length.
Updates after review.
Now the Content-Length header is generated by HpackEncoder based on
MetaData.contentLength, so that the MetaData.HttpFields are not modified.
Signed-off-by: Simone Bordet
---
.../java/org/eclipse/jetty/http/MetaData.java | 5 +++++
.../eclipse/jetty/http2/hpack/HpackEncoder.java | 14 +++++++++++++-
.../http2/server/HttpTransportOverHTTP2.java | 16 ++++++++++++----
3 files changed, 30 insertions(+), 5 deletions(-)
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/MetaData.java b/jetty-http/src/main/java/org/eclipse/jetty/http/MetaData.java
index 7591f62e291..8035b086b72 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/MetaData.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/MetaData.java
@@ -119,6 +119,11 @@ public class MetaData implements Iterable
return _contentLength;
}
+ public void setContentLength(long contentLength)
+ {
+ _contentLength = contentLength;
+ }
+
/**
* @return an iterator over the HTTP fields
* @see #getFields()
diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java
index d71df43f044..0788806b34b 100644
--- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java
+++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java
@@ -217,7 +217,8 @@ public class HpackEncoder
// Remove fields as specified in RFC 7540, 8.1.2.2.
if (fields != null)
{
- // For example: Connection: Close, TE, Upgrade, Custom.
+ // Remove the headers specified in the Connection header,
+ // for example: Connection: Close, TE, Upgrade, Custom.
Set hopHeaders = null;
for (String value : fields.getCSV(HttpHeader.CONNECTION, false))
{
@@ -225,6 +226,8 @@ public class HpackEncoder
hopHeaders = new HashSet<>();
hopHeaders.add(StringUtil.asciiToLowerCase(value));
}
+
+ boolean contentLengthEncoded = false;
for (HttpField field : fields)
{
HttpHeader header = field.getHeader();
@@ -239,8 +242,17 @@ public class HpackEncoder
String name = field.getLowerCaseName();
if (hopHeaders != null && hopHeaders.contains(name))
continue;
+ if (header == HttpHeader.CONTENT_LENGTH)
+ contentLengthEncoded = true;
encode(buffer, field);
}
+
+ if (!contentLengthEncoded)
+ {
+ long contentLength = metadata.getContentLength();
+ if (contentLength >= 0)
+ encode(buffer, new HttpField(HttpHeader.CONTENT_LENGTH, String.valueOf(contentLength)));
+ }
}
// Check size
diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java
index a19a8fa071e..03a9812c588 100644
--- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java
+++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java
@@ -22,8 +22,8 @@ import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
+import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
@@ -113,9 +113,17 @@ public class HttpTransportOverHTTP2 implements HttpTransport
{
if (lastContent)
{
- HttpFields responseHeaders = info.getFields();
- if (!responseHeaders.contains(HttpHeader.CONTENT_LENGTH))
- responseHeaders.put(HttpHeader.CONTENT_LENGTH, String.valueOf(BufferUtil.length(content)));
+ long realContentLength = BufferUtil.length(content);
+ long contentLength = info.getContentLength();
+ if (contentLength < 0)
+ {
+ info.setContentLength(realContentLength);
+ }
+ else if (contentLength != realContentLength)
+ {
+ callback.failed(new BadMessageException(HttpStatus.INTERNAL_SERVER_ERROR_500, String.format("Incorrect Content-Length %d!=%d", contentLength, realContentLength)));
+ return;
+ }
}
if (hasContent)
From 013acca012db0a40cfecfbec33580c3be9739b1b Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Tue, 14 Apr 2020 12:19:52 +0200
Subject: [PATCH 077/101] Issue #4765 - Review GzipHandler inside
ServletContextHandler.
Fixed XML files.
Restored ServletContextHandler.setGzipHandler() and
deprecated it in case it's used from user's XML files.
Signed-off-by: Simone Bordet
---
.../jetty/servlet/ServletContextHandler.java | 13 ++++++++++++-
.../src/test/resources/add-jetty-test-webapp.xml | 12 +++++++-----
.../webapp-contexts/RFC2616/rfc2616-webapp.xml | 12 +++++++-----
.../src/main/config/demo-base/webapps/test.xml | 12 +++++++-----
4 files changed, 33 insertions(+), 16 deletions(-)
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
index 7e611b7803c..a4bcc5c039d 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
@@ -633,7 +633,18 @@ public class ServletContextHandler extends ContextHandler
}
/**
- * Insert a HandlerWrapper before the first Session,Security or ServletHandler
+ * @param gzipHandler the GzipHandler for this ServletContextHandler
+ * @deprecated use {@link #insertHandler(HandlerWrapper)} instead
+ */
+ @Deprecated
+ public void setGzipHandler(GzipHandler gzipHandler)
+ {
+ insertHandler(gzipHandler);
+ LOG.warn("ServletContextHandler.setGzipHandler(GzipHandler) is deprecated, use insertHandler(HandlerWrapper) instead.");
+ }
+
+ /**
+ * Insert a HandlerWrapper before the first Session, Security or ServletHandler
* but after any other HandlerWrappers.
*/
@Override
diff --git a/tests/test-integration/src/test/resources/add-jetty-test-webapp.xml b/tests/test-integration/src/test/resources/add-jetty-test-webapp.xml
index 07d1b2b74f0..96e6c271d45 100644
--- a/tests/test-integration/src/test/resources/add-jetty-test-webapp.xml
+++ b/tests/test-integration/src/test/resources/add-jetty-test-webapp.xml
@@ -6,11 +6,13 @@
/test-jetty-webapp/test-jetty-webapp.war
-
-
- 1024
-
-
+
+
+
+ 1024
+
+
+
diff --git a/tests/test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml b/tests/test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml
index f7c83e419f1..55cd5a5c847 100644
--- a/tests/test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml
+++ b/tests/test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml
@@ -3,9 +3,11 @@
/rfc2616-webapp/test-webapp-rfc2616.war
-
-
- 1024
-
-
+
+
+
+ 1024
+
+
+
diff --git a/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/webapps/test.xml b/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/webapps/test.xml
index 0e49d729146..b98b427615f 100644
--- a/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/webapps/test.xml
+++ b/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/webapps/test.xml
@@ -35,11 +35,13 @@ detected.
true
-
-
- 2048
-
-
+
+
+
+ 2048
+
+
+
Connection
+Connection -> Parser : parse()
+Parser -> HttpChannel : events
+Connection -> HttpChannel : handle()
+HttpChannel -> Server : handle()
+Server -> Handlers : handle()
+----
+
+[[eg-server-http-channel-events]]
+===== HttpChannel Events
+
+The central component processing HTTP requests is `HttpChannel`.
+There is a 1-to-1 relationship between an HTTP request/response and an
+`HttpChannel`, no matter what is the specific protocol that carries the
+HTTP request over the network (HTTP/1.1, HTTP/2 or FastCGI).
+
+Advanced server applications may be interested in the progress of the
+processing of an HTTP request/response by `HttpChannel`.
+A typical case is to know exactly _when_ the HTTP request/response
+processing is complete, for example to monitor processing times.
+
+NOTE: A `Handler` or a Servlet `Filter` may not report precisely when
+an HTTP request/response processing is finished.
+A server application may write a small enough content that is aggregated
+by Jetty for efficiency reasons; the write returns immediately, but
+nothing has been written to the network yet.
+
+`HttpChannel` notifies ``HttpChannel.Listener``s of the progress of the
+HTTP request/response handling.
+Currently, the following events are available:
+
+* `requestBegin`
+* `beforeDispatch`
+* `dispatchFailure`
+* `afterDispatch`
+* `requestContent`
+* `requestContentEnd`
+* `requestTrailers`
+* `requestEnd`
+* `responseBegin`
+* `responseCommit`
+* `responseContent`
+* `responseFailure`
+* `responseEnd`
+* `complete`
+
+Please refer to the `HttpChannel.Listener`
+link:{JDURL}/org/eclipse/jetty/server/HttpChannel.Listener.html[javadocs]
+for the complete list of events.
+
+Server applications can register `HttpChannel.Listener` by adding them as
+beans to the `Connector`:
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=httpChannelListener]
+----
include::server-http-connector.adoc[]
include::server-http-handler.adoc[]
diff --git a/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java b/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java
index d9a8395a693..f3e5f786764 100644
--- a/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java
+++ b/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java
@@ -20,6 +20,8 @@ package embedded.server.http;
import java.io.IOException;
import java.util.EnumSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
@@ -37,6 +39,7 @@ import org.eclipse.jetty.rewrite.handler.RedirectRegexRule;
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
import org.eclipse.jetty.rewrite.handler.RewriteRegexRule;
import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.ProxyConnectionFactory;
@@ -68,6 +71,8 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.webapp.WebAppContext;
+import static java.lang.System.Logger.Level.INFO;
+
@SuppressWarnings("unused")
public class HTTPServerDocs
{
@@ -104,6 +109,50 @@ public class HTTPServerDocs
// end::simple[]
}
+ public void httpChannelListener() throws Exception
+ {
+ // tag::httpChannelListener[]
+ class TimingHttpChannelListener implements HttpChannel.Listener
+ {
+ private final ConcurrentMap times = new ConcurrentHashMap<>();
+
+ @Override
+ public void onRequestBegin(Request request)
+ {
+ times.put(request, System.nanoTime());
+ }
+
+ @Override
+ public void onComplete(Request request)
+ {
+ long begin = times.remove(request);
+ long elapsed = System.nanoTime() - begin;
+ System.getLogger("timing").log(INFO, "Request {0} took {1} ns", request, elapsed);
+ }
+ }
+
+ Server server = new Server();
+
+ Connector connector = new ServerConnector(server);
+ server.addConnector(connector);
+
+ // Add the HttpChannel.Listener as bean to the connector.
+ connector.addBean(new TimingHttpChannelListener());
+
+ // Set a simple Handler to handle requests/responses.
+ server.setHandler(new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response)
+ {
+ jettyRequest.setHandled(true);
+ }
+ });
+
+ server.start();
+ // end::httpChannelListener[]
+ }
+
public void configureConnector() throws Exception
{
// tag::configureConnector[]
From b388b53f794c760fffee358e44a8996a0f9ae5a3 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Tue, 14 Apr 2020 22:52:20 +0200
Subject: [PATCH 083/101] Improvements to the Jetty server documentation.
Revised and moved documentation for 1xx status codes.
Signed-off-by: Simone Bordet
---
.../old_docs/architecture/1xx-responses.adoc | 41 -------------
.../old_docs/architecture/chapter.adoc | 1 -
.../server/http/server-http-application.adoc | 57 +++++++++++++++++++
.../server/http/server-http.adoc | 1 +
.../embedded/server/http/HTTPServerDocs.java | 43 ++++++++++++++
5 files changed, 101 insertions(+), 42 deletions(-)
delete mode 100644 jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/1xx-responses.adoc
create mode 100644 jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-application.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/1xx-responses.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/1xx-responses.adoc
deleted file mode 100644
index e558ab780d6..00000000000
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/1xx-responses.adoc
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-//
-// This program and the accompanying materials are made available under
-// the terms of the Eclipse Public License 2.0 which is available at
-// https://www.eclipse.org/legal/epl-2.0
-//
-// This Source Code may also be made available under the following
-// Secondary Licenses when the conditions for such availability set
-// forth in the Eclipse Public License, v. 2.0 are satisfied:
-// the Apache License v2.0 which is available at
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
-// ========================================================================
-//
-
-[[jetty-1xx-responses]]
-=== Managing 1xx Responses
-
-The http://www.ietf.org/rfc/rfc2616.txt[HTTP RFC] allows for 1xx informational responses to be sent before a real content response.
-Unfortunately the servlet specification does not provide a way for these to be sent, so Jetty has had to provide non-standard handling of these headers.
-
-[[jetty-100-continue]]
-==== 100 Continue
-
-The 100 Continue response should be sent by the server when a client sends a request with a Expect: 100-continue header, as the client will not send the body of the request until the 100 continue response has been sent.
-
-The intent of this feature is to allow a server to inspect the headers and to tell the client to not send a request body that might be too large or insufficiently private or otherwise unable to be handled.
-
-Jetty achieves this by waiting until the input stream or reader is obtained by the filter/servlet, before sending the 100 continues response.
-Thus a filter/servlet may inspect the headers of a request before getting the input stream and send an error response (or redirect etc.) rather than the 100 continues.
-
-[[jetty-102-processing]]
-==== 102 Processing
-
-http://www.ietf.org/rfc/rfc2518.txt[RFC 2518] defines the 102 processing response that can be sent "when the server has a reasonable expectation that the request will take significant time to complete.
-As guidance, if a method is taking longer than 20 seconds (a reasonable, but arbitrary value) to process the server SHOULD return a 102 (Processing) response".
-
-So if a request is received with the Expect: 102-processing header, then a filter/servlet may send a 102 response (without terminating further processing) by calling `servletResponse.sendError(102);`.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/chapter.adoc
index 4c08ba13d69..833ba3f384b 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/chapter.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/architecture/chapter.adoc
@@ -23,5 +23,4 @@ General items related to the architecture of jetty and how it deals with certain
include::basic-architecture.adoc[]
include::jetty-classloading.adoc[]
-include::1xx-responses.adoc[]
include::server-side-architecture.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-application.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-application.adoc
new file mode 100644
index 00000000000..c872ac8e57d
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-application.adoc
@@ -0,0 +1,57 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+[[eg-server-http-application]]
+=== Writing HTTP Server Applications
+
+Writing HTTP applications is typically simple, especially when using blocking APIs.
+However, there are more subtle cases where it is worth clarifying what a server application should do to obtain the desired results when deployed in Jetty.
+
+[[eg-server-http-application-1xx]]
+==== Managing 1xx Responses
+
+The link:https://tools.ietf.org/html/rfc7231#section-5.1.1[HTTP/1.1 RFC] allows for `1xx` informational responses to be sent before a real content response.
+Unfortunately the servlet specification does not provide a way for these to be sent, so Jetty has had to provide non-standard handling of these headers.
+
+[[eg-server-http-application-100]]
+===== 100 Continue
+
+The `100 Continue` response should be sent by the server when a client sends a request with an `Expect: 100-continue` header, as the client will not send the body of the request until the `100 Continue` response has been sent.
+
+The intent of this feature is to allow a server to inspect the headers and to tell the client to not send a request body that might be too large or insufficiently private or otherwise unable to be handled.
+
+Jetty achieves this by waiting until the input stream or reader is obtained by the filter/servlet, before sending the `100 Continue` response.
+Thus a filter/servlet may inspect the headers of a request before getting the input stream and send an error response (or redirect etc.) rather than the 100 continues.
+
+[source,java,indent=0]
+----
+include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=continue100]
+----
+
+[[jetty-102-processing]]
+==== 102 Processing
+
+link:https://tools.ietf.org/html/rfc2518#section-10.1[RFC 2518] defined the `102 Processing` status code that can be sent "when the server has a reasonable expectation that the request will take significant time to complete.
+As guidance, if a method is taking longer than 20 seconds (a reasonable, but arbitrary value) to process the server SHOULD return a `102 Processing` response".
+
+However a later update of RFC 2518, link:https://tools.ietf.org/html/rfc4918[RFC 4918], removed the `102 Processing` status code for link:https://tools.ietf.org/html/rfc4918#appendix-F.3["lack of implementation"].
+
+Jetty supports the `102 Processing` status code.
+If a request is received with the `Expect: 102-processing` header, then a filter/servlet may send a `102 Processing` response (without terminating further processing) by calling `response.sendError(102)`.
+
+// TODO: add section to always read request content until -1 is reached.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http.adoc
index 982ed874200..cde36774086 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http.adoc
@@ -181,3 +181,4 @@ include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=httpChan
include::server-http-connector.adoc[]
include::server-http-handler.adoc[]
+include::server-http-application.adoc[]
diff --git a/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java b/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java
index f3e5f786764..3b2cedb7fbd 100644
--- a/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java
+++ b/jetty-documentation/src/main/java/embedded/server/http/HTTPServerDocs.java
@@ -24,12 +24,15 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.http.HttpCompliance;
+import org.eclipse.jetty.http.HttpHeaderValue;
+import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
@@ -885,4 +888,44 @@ public class HTTPServerDocs
server.start();
// end::defaultHandler[]
}
+
+ public void continue100()
+ {
+ // tag::continue100[]
+ class Continue100HttpServlet extends HttpServlet
+ {
+ @Override
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
+ {
+ // Inspect the method and headers.
+ boolean isPost = HttpMethod.POST.is(request.getMethod());
+ boolean expects100 = HttpHeaderValue.CONTINUE.is(request.getHeader("Expect"));
+ long contentLength = request.getContentLengthLong();
+
+ if (isPost && expects100)
+ {
+ if (contentLength > 1024 * 1024)
+ {
+ // Rejects uploads that are too large.
+ response.sendError(HttpStatus.PAYLOAD_TOO_LARGE_413);
+ }
+ else
+ {
+ // Getting the request InputStream indicates that
+ // the application wants to read the request content.
+ // Jetty will send the 100 Continue response at this
+ // point, and the client will send the request content.
+ ServletInputStream input = request.getInputStream();
+
+ // Read and process the request input.
+ }
+ }
+ else
+ {
+ // Process normal requests.
+ }
+ }
+ }
+ // end::continue100[]
+ }
}
From fc7fb9e5d71f11a85a6ae3c9a0152dfdb8ccba79 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Tue, 14 Apr 2020 23:28:43 +0200
Subject: [PATCH 084/101] Improvements to the Jetty server documentation.
Removed the old Handler documentation, already replaced by the new one.
Signed-off-by: Simone Bordet
---
.../old_docs/handlers/chapter.adoc | 22 ---
.../handlers/writing-custom-handlers.adoc | 176 ------------------
.../embedded-guide/old_docs/server.adoc | 2 -
.../server/http/server-http-application.adoc | 13 +-
.../http/server-http-handler-implement.adoc | 2 +-
.../embedded-guide/server/server.adoc | 2 +
6 files changed, 11 insertions(+), 206 deletions(-)
delete mode 100644 jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/handlers/chapter.adoc
delete mode 100644 jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/handlers/writing-custom-handlers.adoc
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/handlers/chapter.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/handlers/chapter.adoc
deleted file mode 100644
index ec0b8bba510..00000000000
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/handlers/chapter.adoc
+++ /dev/null
@@ -1,22 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-//
-// This program and the accompanying materials are made available under
-// the terms of the Eclipse Public License 2.0 which is available at
-// https://www.eclipse.org/legal/epl-2.0
-//
-// This Source Code may also be made available under the following
-// Secondary Licenses when the conditions for such availability set
-// forth in the Eclipse Public License, v. 2.0 are satisfied:
-// the Apache License v2.0 which is available at
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
-// ========================================================================
-//
-
-[[jetty-handlers]]
-== Handlers
-
-include::writing-custom-handlers.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/handlers/writing-custom-handlers.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/handlers/writing-custom-handlers.adoc
deleted file mode 100644
index 026007fe3ee..00000000000
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/handlers/writing-custom-handlers.adoc
+++ /dev/null
@@ -1,176 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-//
-// This program and the accompanying materials are made available under
-// the terms of the Eclipse Public License 2.0 which is available at
-// https://www.eclipse.org/legal/epl-2.0
-//
-// This Source Code may also be made available under the following
-// Secondary Licenses when the conditions for such availability set
-// forth in the Eclipse Public License, v. 2.0 are satisfied:
-// the Apache License v2.0 which is available at
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
-// ========================================================================
-//
-
-[[writing-custom-handlers]]
-=== Writing Custom Handlers
-
-The Handler is the Jetty component that deals with received requests.
-
-Many users of Jetty never need to write a Jetty Handler, but instead use the link:{JDURL}/org/eclipse/jetty/servlet/package-summary.html[Servlet API.]
-You can reuse the existing Jetty handlers for context, security, sessions and servlets without the need for extension.
-However, some users might have special requirements or footprint concerns that prohibit the use of the full servlet API.
-For them implementing a Jetty handler is a straight forward way to provide dynamic web content with a minimum of fuss.
-
-See the section on xref:basic-architecture[] to understand more about Handlers vs. Servlets.
-
-[[handler-api]]
-==== The Handler API
-
-The link:{JDURL}/org/eclipse/jetty/server/Handler.html[Handler] interface provides Jetty's core of content generation or manipulation.
-Classes that implement this interface are used to coordinate requests, filter requests and generate content.
-
-The core API of the Handler interface is:
-
-[source, java, subs="{sub-order}"]
-----
-public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
- throws IOException, ServletException
-----
-
-An implementation of this method can handle a request and pass the request onto another handler (or servlet), or it can modify and/or wrap the request before passing it on.
-This gives three styles of handler:
-
-* Coordinating Handlers - Handlers that route requests to other handlers (`HandlerCollection`, `ContextHandlerCollection`)
-* Filtering Handlers - Handlers that augment a request and pass it on to other handlers (`HandlerWrapper`, `ContextHandler`, `SessionHandler`)
-* Generating Handlers - Handlers that produce content (`ResourceHandler` and `ServletHandler`)
-
-[[target]]
-===== The Target
-
-The target of a handler is an identifier for the resource that should handle the passed request.
-This is normally the URI that is parsed from an HTTP Request.
-However, in two key circumstances the target may differ from the URI of the passed request:
-
-* If the request has been dispatched to a named resource, such as a named servlet, the target is the name of that resource.
-* If the request is being made by a call to link:http://docs.oracle.com/javaee/7/api/javax/servlet/RequestDispatcher.html[`RequestDispatcher`], the target is the URI of the included resource and is different to the URI of the actual request.
-
-[[request-and-response]]
-===== The Request and Response
-
-The request and response objects used in the signature of the handle method are
-link:http://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html[`ServletRequest`] and link:http://docs.oracle.com/javaee/7/api/javax/servlet/ServletResponse.html[`ServletResponse`].
-These are the standard APIs and are moderately restricted in what they can do to the request and response.
-More often than not, access to the Jetty implementations of these classes is required: link:{JDURL}/org/eclipse/jetty/server/Request.html[`Request`] and link:{JDURL}/org/eclipse/jetty/server/Response.html[`Response`].
-However, as the request and response may be wrapped by handlers, filters and servlets, it is not possible to pass the implementation directly.
-The following mantra retrieves the core implementation objects from under any wrappers:
-
-[source, java, subs="{sub-order}"]
-----
-Request base_request = request instanceof Request ? (Request)request : HttpConnection.getCurrentConnection().getHttpChannel().getRequest();
-Response base_response = response instanceof Response ? (Response)response : HttpConnection.getCurrentConnection().getHttpChannel().getResponse();
-----
-
-Notice that if the handler passes the request on to another handler, it should use the Request/Response objects passed in, and not the base objects.
-This is to preserve any wrapping done by up stream handlers.
-
-[[dispatch]]
-===== The Dispatch
-
-The dispatch argument indicates the state of the handling of the call and may be:
-
-* `REQUEST == 1` - An original request received from a connector.
-* `FORWARD == 2` - A request being forwarded by a RequestDispatcher.
-* `INCLUDE == 4` - A request being included by a RequestDispatcher.
-* `ERROR == 8` - A request being forwarded to a error handler by the container.
-
-These mostly have significance for servlet and related handlers.
-For example, the security handler only applies authentication and authorization to REQUEST dispatches.
-
-[[handling-requests]]
-==== Handling Requests
-
-A Handler may handle a request by:
-
-* xref:generating-response[]
-* xref:filtering-request-or-response[]
-* xref:passing-request-and-response[]
-
-[[generating-response]]
-===== Generating a Response
-
-The link:{JDURL}/org/eclipse/jetty/embedded/OneHandler.html[`OneHandler`] embedded example shows how a simple handler can generate a response.
-
-You can use the standard servlet response API, which will typically set some status, content headers and then write out the content:
-
-[source, java, subs="{sub-order}"]
-----
- response.setContentType("text/html");
- response.setStatus(HttpServletResponse.SC_OK);
- response.getWriter().println("
Hello OneHandler
");
-----
-
-It is also very important that a handler indicate that it has completed handling the request and that the request should not be passed to other handlers:
-
-[source, java, subs="{sub-order}"]
-----
- Request base_request = (request instanceof Request) ? (Request)request:HttpConnection.getCurrentConnection().getHttpChannel().getRequest();
- base_request.setHandled(true);
-----
-
-[[filtering-request-or-response]]
-===== Filtering the Request and/or Response
-
-Once the base request or response object is obtained, you can modify it.
-Typically you would make modifications to accomplish:
-
-* Breaking the URI into contextPath, servletPath and pathInfo components.
-* Associating a resource base with a request for static content.
-* Associating a session with a request.
-* Associating a security principal with a request.
-* Changing the URI and paths during a request dispatch forward to another resource.
-
-You can also update the context of the request:
-
-* Setting the current threads context classloader.
-* Setting thread locals to identify the current `ServletContext`.
-
-Typically Jetty passes a modified request to another handler and undoes modifications in a finally block afterwards:
-
-[source, java, subs="{sub-order}"]
-----
- try
- {
- base_request.setSession(a_session);
- next_handler.handle(target,request,response,dispatch);
- }
- finally
- {
- base_request.setSession(old_session);
- }
-----
-
-The classes that implement the link:{JDURL}/org/eclipse/jetty/server/handler/HandlerWrapper.html[`HandlerWrapper`] class are typically handler filters of this style.
-
-[[passing-request-and-response]]
-===== Passing the Request and Response to Another Handler
-
-A handler might simply inspect the request and use the target, request URI or other information to select another handler to pass the request to.
-These handlers typically implement the link:{JDURL}/org/eclipse/jetty/server/HandlerContainer.html[`HandlerContainer`] interface.
-
-Examples include:
-
-* link:{JDURL}/org/eclipse/jetty/server/handler/HandlerCollection.html[Class `HandlerCollection`] -
-A collection of handlers, where each handler is called regardless of the state of the request.
-This is typically used to pass a request to a link:{JDURL}/org/eclipse/jetty/server/handler/ContextHandlerCollection.html[`ContextHandlerCollection`,] and then the link:{JDURL}/org/eclipse/jetty/server/handler/RequestLogHandler.html[`RequestLogHandler`.]
-* link:{JDURL}/org/eclipse/jetty/server/handler/HandlerList.html[`HandlerList`] - A list of handlers that are called in turn until the request state is set as handled.
-* link:{JDURL}/org/eclipse/jetty/server/handler/ContextHandlerCollection.html[`ContextHandlerCollection`] - A collection of Handlers, of which one is selected by best match for the context path.
-
-[[more-about-handlers]]
-==== More About Handlers
-
-See the link:{JDURL}/[latest Jetty JavaDoc] for detailed information on each Jetty handler.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/server.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/server.adoc
index bfd8444bab5..d8ade48b4b4 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/server.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/old_docs/server.adoc
@@ -21,8 +21,6 @@
include::embedding/chapter.adoc[]
include::maven/chapter.adoc[]
-include::clients/http/chapter.adoc[]
-include::handlers/chapter.adoc[]
include::websockets/intro/chapter.adoc[]
include::websockets/jetty/chapter.adoc[]
include::ant/chapter.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-application.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-application.adoc
index c872ac8e57d..dcd6600898e 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-application.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-application.adoc
@@ -20,7 +20,7 @@
=== Writing HTTP Server Applications
Writing HTTP applications is typically simple, especially when using blocking APIs.
-However, there are more subtle cases where it is worth clarifying what a server application should do to obtain the desired results when deployed in Jetty.
+However, there are subtle cases where it is worth clarifying what a server application should do to obtain the desired results when run by Jetty.
[[eg-server-http-application-1xx]]
==== Managing 1xx Responses
@@ -44,12 +44,15 @@ include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=continue
----
[[jetty-102-processing]]
-==== 102 Processing
+===== 102 Processing
-link:https://tools.ietf.org/html/rfc2518#section-10.1[RFC 2518] defined the `102 Processing` status code that can be sent "when the server has a reasonable expectation that the request will take significant time to complete.
-As guidance, if a method is taking longer than 20 seconds (a reasonable, but arbitrary value) to process the server SHOULD return a `102 Processing` response".
+link:https://tools.ietf.org/html/rfc2518[RFC 2518] defined the `102 Processing` status code that can be sent:
-However a later update of RFC 2518, link:https://tools.ietf.org/html/rfc4918[RFC 4918], removed the `102 Processing` status code for link:https://tools.ietf.org/html/rfc4918#appendix-F.3["lack of implementation"].
+[quote,RFC 2518 section 10.1]
+when the server has a reasonable expectation that the request will take significant time to complete.
+As guidance, if a method is taking longer than 20 seconds (a reasonable, but arbitrary value) to process the server SHOULD return a `102 Processing` response.
+
+However, a later update of RFC 2518, link:https://tools.ietf.org/html/rfc4918[RFC 4918], removed the `102 Processing` status code for link:https://tools.ietf.org/html/rfc4918#appendix-F.3["lack of implementation"].
Jetty supports the `102 Processing` status code.
If a request is received with the `Expect: 102-processing` header, then a filter/servlet may send a `102 Processing` response (without terminating further processing) by calling `response.sendError(102)`.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler-implement.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler-implement.adoc
index 625d206f2e0..36dc9df088d 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler-implement.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/http/server-http-handler-implement.adoc
@@ -67,5 +67,5 @@ include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=handlerF
Note how a filtering `Handler` extends from `HandlerWrapper` and as such
needs another handler to forward the request processing to, and how the
-two``Handler``s needs to be linked together to work properly.
+two ``Handler``s needs to be linked together to work properly.
diff --git a/jetty-documentation/src/main/asciidoc/embedded-guide/server/server.adoc b/jetty-documentation/src/main/asciidoc/embedded-guide/server/server.adoc
index a8cc9ceffd2..79feeefb8da 100644
--- a/jetty-documentation/src/main/asciidoc/embedded-guide/server/server.adoc
+++ b/jetty-documentation/src/main/asciidoc/embedded-guide/server/server.adoc
@@ -45,6 +45,8 @@ xref:eg-server-http2[HTTP/2 libraries]
* WebSocket support, for applications that want to embed a WebSocket server,
via the xref:eg-server-websocket[WebSocket libraries]
+// TODO: add a section on lifecycle and the component tree.
+
include::http/server-http.adoc[]
include::http2/server-http2.adoc[]
include::websocket/server-websocket.adoc[]
From 4047e31ce76568e3f37e706c1f455cc70505b3b9 Mon Sep 17 00:00:00 2001
From: Lachlan Roberts
Date: Tue, 14 Apr 2020 21:15:50 +1000
Subject: [PATCH 085/101] Issue #4771 - cleanup and add tests for the different
ws message handlers
Signed-off-by: Lachlan Roberts
---
.../client/SessionAddMessageHandlerTest.java | 31 ++--
.../handlers/AbstractAnnotatedHandler.java | 69 +++++++
.../javax/tests/handlers/AbstractHandler.java | 68 +++++++
.../tests/handlers/BaseMessageHandler.java | 4 +-
.../javax/tests/handlers/BinaryHandlers.java | 169 ++++++++++++++++++
.../handlers/ByteArrayPartialHandler.java | 30 ----
.../tests/handlers/ByteArrayWholeHandler.java | 30 ----
.../handlers/ByteBufferPartialHandler.java | 30 ----
.../handlers/ByteBufferWholeHandler.java | 31 ----
.../tests/handlers/ComboMessageHandler.java | 6 +-
.../handlers/ExtendedMessageHandler.java | 2 +-
.../handlers/InputStreamWholeHandler.java | 31 ----
.../tests/handlers/LongMessageHandler.java | 3 +-
.../tests/handlers/MessageHandlerTest.java | 167 +++++++++++++++++
.../tests/handlers/ReaderWholeHandler.java | 31 ----
.../tests/handlers/StringPartialHandler.java | 30 ----
.../tests/handlers/StringWholeHandler.java | 30 ----
.../javax/tests/handlers/TextHandlers.java | 125 +++++++++++++
.../api/WebSocketConnectionListener.java | 12 +-
.../websocket/api/WebSocketListener.java | 8 +-
.../api/WebSocketPartialListener.java | 8 +-
.../api/WebSocketPingPongListener.java | 8 +-
.../api/annotations/OnWebSocketMessage.java | 10 +-
.../JettyWebSocketFrameHandlerFactory.java | 19 +-
.../tests/ConcurrentConnectTest.java | 6 +-
.../jetty/websocket/tests/EventSocket.java | 16 +-
.../websocket/tests/JettyOnCloseTest.java | 16 +-
.../JettyWebSocketExtensionConfigTest.java | 2 +-
.../tests/JettyWebSocketFilterTest.java | 2 +-
.../tests/JettyWebSocketServletTest.java | 2 +-
.../websocket/tests/SuspendResumeTest.java | 22 +--
.../tests/WebSocketOverHTTP2Test.java | 6 +-
.../tests/WebSocketServletExamplesTest.java | 6 +-
.../websocket/tests/WebSocketStopTest.java | 10 +-
.../tests/autobahn/JettyAutobahnClient.java | 2 +-
.../tests/client/ClientConfigTest.java | 8 +-
.../listeners/AbstractAnnotatedListener.java | 69 +++++++
.../tests/listeners/AbstractListener.java | 66 +++++++
.../tests/listeners/BinaryListeners.java | 129 +++++++++++++
.../tests/listeners/TextListeners.java | 105 +++++++++++
.../listeners/WebSocketListenerTest.java | 146 +++++++++++++++
.../tests/server/ServerConfigTest.java | 10 +-
.../util/messages/ByteArrayMessageSink.java | 3 +-
.../messages/PartialByteArrayMessageSink.java | 11 +-
.../PartialByteBufferMessageSink.java | 8 -
45 files changed, 1231 insertions(+), 366 deletions(-)
create mode 100644 jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/AbstractAnnotatedHandler.java
create mode 100644 jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/AbstractHandler.java
create mode 100644 jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/BinaryHandlers.java
delete mode 100644 jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ByteArrayPartialHandler.java
delete mode 100644 jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ByteArrayWholeHandler.java
delete mode 100644 jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ByteBufferPartialHandler.java
delete mode 100644 jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ByteBufferWholeHandler.java
delete mode 100644 jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/InputStreamWholeHandler.java
create mode 100644 jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/MessageHandlerTest.java
delete mode 100644 jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ReaderWholeHandler.java
delete mode 100644 jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/StringPartialHandler.java
delete mode 100644 jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/StringWholeHandler.java
create mode 100644 jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/TextHandlers.java
create mode 100644 jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/AbstractAnnotatedListener.java
create mode 100644 jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/AbstractListener.java
create mode 100644 jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/BinaryListeners.java
create mode 100644 jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/TextListeners.java
create mode 100644 jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/WebSocketListenerTest.java
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/client/SessionAddMessageHandlerTest.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/client/SessionAddMessageHandlerTest.java
index f980f1cd3e6..bc097cd5084 100644
--- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/client/SessionAddMessageHandlerTest.java
+++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/client/SessionAddMessageHandlerTest.java
@@ -41,10 +41,9 @@ import org.eclipse.jetty.websocket.javax.common.UpgradeRequest;
import org.eclipse.jetty.websocket.javax.common.UpgradeRequestAdapter;
import org.eclipse.jetty.websocket.javax.tests.MessageType;
import org.eclipse.jetty.websocket.javax.tests.SessionMatchers;
-import org.eclipse.jetty.websocket.javax.tests.handlers.ByteArrayWholeHandler;
-import org.eclipse.jetty.websocket.javax.tests.handlers.ByteBufferPartialHandler;
+import org.eclipse.jetty.websocket.javax.tests.handlers.BinaryHandlers;
import org.eclipse.jetty.websocket.javax.tests.handlers.LongMessageHandler;
-import org.eclipse.jetty.websocket.javax.tests.handlers.StringWholeHandler;
+import org.eclipse.jetty.websocket.javax.tests.handlers.TextHandlers;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -93,7 +92,7 @@ public class SessionAddMessageHandlerTest
@Test
public void testMessageHandlerBinary()
{
- session.addMessageHandler(new ByteBufferPartialHandler());
+ session.addMessageHandler(new BinaryHandlers.ByteBufferPartialHandler());
assertThat("session", session, SessionMatchers.isMessageHandlerTypeRegistered(MessageType.BINARY));
assertThat("session", session, Matchers.not(SessionMatchers.isMessageHandlerTypeRegistered(MessageType.TEXT)));
assertThat("session", session, Matchers.not(SessionMatchers.isMessageHandlerTypeRegistered(MessageType.PONG)));
@@ -102,7 +101,7 @@ public class SessionAddMessageHandlerTest
Matchers.hasItem(
Matchers.allOf(
SessionMatchers.isMessageHandlerType(session, MessageType.BINARY),
- instanceOf(ByteBufferPartialHandler.class)
+ instanceOf(BinaryHandlers.ByteBufferPartialHandler.class)
)
)
);
@@ -111,8 +110,8 @@ public class SessionAddMessageHandlerTest
@Test
public void testMessageHandlerBoth()
{
- session.addMessageHandler(new StringWholeHandler());
- session.addMessageHandler(new ByteArrayWholeHandler());
+ session.addMessageHandler(new TextHandlers.StringWholeHandler());
+ session.addMessageHandler(new BinaryHandlers.ByteArrayWholeHandler());
assertThat("session", session, SessionMatchers.isMessageHandlerTypeRegistered(MessageType.BINARY));
assertThat("session", session, SessionMatchers.isMessageHandlerTypeRegistered(MessageType.TEXT));
assertThat("session", session, Matchers.not(SessionMatchers.isMessageHandlerTypeRegistered(MessageType.PONG)));
@@ -121,7 +120,7 @@ public class SessionAddMessageHandlerTest
Matchers.hasItem(
Matchers.allOf(
SessionMatchers.isMessageHandlerType(session, MessageType.BINARY),
- instanceOf(ByteArrayWholeHandler.class)
+ instanceOf(BinaryHandlers.ByteArrayWholeHandler.class)
)
)
);
@@ -130,7 +129,7 @@ public class SessionAddMessageHandlerTest
Matchers.hasItem(
Matchers.allOf(
SessionMatchers.isMessageHandlerType(session, MessageType.TEXT),
- instanceOf(StringWholeHandler.class)
+ instanceOf(TextHandlers.StringWholeHandler.class)
)
)
);
@@ -139,9 +138,9 @@ public class SessionAddMessageHandlerTest
@Test
public void testMessageHandlerReplaceTextHandler()
{
- MessageHandler strHandler = new StringWholeHandler();
+ MessageHandler strHandler = new TextHandlers.StringWholeHandler();
session.addMessageHandler(strHandler); // add a TEXT handler
- session.addMessageHandler(new ByteArrayWholeHandler()); // add BINARY handler
+ session.addMessageHandler(new BinaryHandlers.ByteArrayWholeHandler()); // add BINARY handler
session.removeMessageHandler(strHandler); // remove original TEXT handler
session.addMessageHandler(new LongMessageHandler()); // add new TEXT handler
@@ -154,7 +153,7 @@ public class SessionAddMessageHandlerTest
Matchers.hasItem(
Matchers.allOf(
SessionMatchers.isMessageHandlerType(session, MessageType.BINARY),
- instanceOf(ByteArrayWholeHandler.class)
+ instanceOf(BinaryHandlers.ByteArrayWholeHandler.class)
)
)
);
@@ -177,7 +176,7 @@ public class SessionAddMessageHandlerTest
MessageHandler.Whole lamdaHandler = (msg) -> received.add(msg);
session.addMessageHandler(String.class, lamdaHandler); // add a TEXT handler lambda
- session.addMessageHandler(new ByteArrayWholeHandler()); // add BINARY handler
+ session.addMessageHandler(new BinaryHandlers.ByteArrayWholeHandler()); // add BINARY handler
session.removeMessageHandler(lamdaHandler); // remove original TEXT handler
assertThat("session", session, SessionMatchers.isMessageHandlerTypeRegistered(MessageType.BINARY));
@@ -189,7 +188,7 @@ public class SessionAddMessageHandlerTest
Matchers.hasItem(
Matchers.allOf(
SessionMatchers.isMessageHandlerType(session, MessageType.BINARY),
- instanceOf(ByteArrayWholeHandler.class)
+ instanceOf(BinaryHandlers.ByteArrayWholeHandler.class)
)
)
);
@@ -198,7 +197,7 @@ public class SessionAddMessageHandlerTest
@Test
public void testMessageHandlerText()
{
- session.addMessageHandler(new StringWholeHandler());
+ session.addMessageHandler(new TextHandlers.StringWholeHandler());
assertThat("session", session, Matchers.not(SessionMatchers.isMessageHandlerTypeRegistered(MessageType.BINARY)));
assertThat("session", session, SessionMatchers.isMessageHandlerTypeRegistered(MessageType.TEXT));
@@ -209,7 +208,7 @@ public class SessionAddMessageHandlerTest
Matchers.hasItem(
Matchers.allOf(
SessionMatchers.isMessageHandlerType(session, MessageType.TEXT),
- instanceOf(StringWholeHandler.class)
+ instanceOf(TextHandlers.StringWholeHandler.class)
)
)
);
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/AbstractAnnotatedHandler.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/AbstractAnnotatedHandler.java
new file mode 100644
index 00000000000..919461ccf22
--- /dev/null
+++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/AbstractAnnotatedHandler.java
@@ -0,0 +1,69 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.javax.tests.handlers;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import javax.websocket.EndpointConfig;
+import javax.websocket.OnError;
+import javax.websocket.OnOpen;
+import javax.websocket.Session;
+import javax.websocket.server.ServerEndpoint;
+
+@ServerEndpoint("/")
+public class AbstractAnnotatedHandler
+{
+ protected Session _session;
+
+ @OnOpen
+ public void onOpen(Session session, EndpointConfig config)
+ {
+ _session = session;
+ }
+
+ @OnError
+ public void onError(Session session, Throwable thr)
+ {
+ thr.printStackTrace();
+ }
+
+ public void sendText(String message, boolean last)
+ {
+ try
+ {
+ _session.getBasicRemote().sendText(message, last);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void sendBinary(ByteBuffer message, boolean last)
+ {
+ try
+ {
+ _session.getBasicRemote().sendBinary(message, last);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/AbstractHandler.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/AbstractHandler.java
new file mode 100644
index 00000000000..5b31c930106
--- /dev/null
+++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/AbstractHandler.java
@@ -0,0 +1,68 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.javax.tests.handlers;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import javax.websocket.Endpoint;
+import javax.websocket.EndpointConfig;
+import javax.websocket.MessageHandler;
+import javax.websocket.Session;
+
+public class AbstractHandler extends Endpoint implements MessageHandler
+{
+ protected Session _session;
+
+ @Override
+ public void onOpen(Session session, EndpointConfig config)
+ {
+ _session = session;
+ _session.addMessageHandler(this);
+ }
+
+ @Override
+ public void onError(Session session, Throwable thr)
+ {
+ thr.printStackTrace();
+ }
+
+ public void sendText(String message, boolean last)
+ {
+ try
+ {
+ _session.getBasicRemote().sendText(message, last);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void sendBinary(ByteBuffer message, boolean last)
+ {
+ try
+ {
+ _session.getBasicRemote().sendBinary(message, last);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/BaseMessageHandler.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/BaseMessageHandler.java
index 1df281c32f4..c4452ff38f0 100644
--- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/BaseMessageHandler.java
+++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/BaseMessageHandler.java
@@ -20,11 +20,11 @@ package org.eclipse.jetty.websocket.javax.tests.handlers;
import javax.websocket.MessageHandler;
-public class BaseMessageHandler implements MessageHandler.Whole
+public class BaseMessageHandler extends AbstractHandler implements MessageHandler.Whole
{
@Override
public void onMessage(String message)
{
- // TODO Auto-generated method stub
+ sendText(message, true);
}
}
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/BinaryHandlers.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/BinaryHandlers.java
new file mode 100644
index 00000000000..de3be71ced2
--- /dev/null
+++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/BinaryHandlers.java
@@ -0,0 +1,169 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.javax.tests.handlers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.stream.Stream;
+import javax.websocket.MessageHandler;
+import javax.websocket.OnMessage;
+import javax.websocket.Session;
+import javax.websocket.server.ServerEndpoint;
+
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.IO;
+import org.junit.jupiter.params.provider.Arguments;
+
+public class BinaryHandlers
+{
+ public static Stream getBinaryHandlers()
+ {
+ return Stream.of(
+ ByteArrayWholeHandler.class,
+ ByteArrayPartialHandler.class,
+ ByteBufferWholeHandler.class,
+ ByteBufferPartialHandler.class,
+ InputStreamWholeHandler.class,
+ AnnotatedByteBufferWholeHandler.class,
+ AnnotatedByteBufferPartialHandler.class,
+ AnnotatedByteArrayWholeHandler.class,
+ AnnotatedByteArrayPartialHandler.class,
+ AnnotatedInputStreamWholeHandler.class,
+ AnnotatedReverseArgumentPartialHandler.class
+ ).map(Arguments::of);
+ }
+
+ public static class ByteArrayWholeHandler extends AbstractHandler implements MessageHandler.Whole
+ {
+ @Override
+ public void onMessage(byte[] message)
+ {
+ sendBinary(BufferUtil.toBuffer(message), true);
+ }
+ }
+
+ public static class ByteArrayPartialHandler extends AbstractHandler implements MessageHandler.Partial
+ {
+ @Override
+ public void onMessage(byte[] partialMessage, boolean last)
+ {
+ sendBinary(BufferUtil.toBuffer(partialMessage), last);
+ }
+ }
+
+ public static class ByteBufferWholeHandler extends AbstractHandler implements MessageHandler.Whole
+ {
+ @Override
+ public void onMessage(ByteBuffer message)
+ {
+ sendBinary(message, true);
+ }
+ }
+
+ public static class ByteBufferPartialHandler extends AbstractHandler implements MessageHandler.Partial
+ {
+ @Override
+ public void onMessage(ByteBuffer partialMessage, boolean last)
+ {
+ sendBinary(partialMessage, last);
+ }
+ }
+
+ public static class InputStreamWholeHandler extends AbstractHandler implements MessageHandler.Whole
+ {
+ @Override
+ public void onMessage(InputStream stream)
+ {
+ sendBinary(readBytes(stream), true);
+ }
+ }
+
+ @ServerEndpoint("/")
+ public static class AnnotatedByteBufferWholeHandler extends AbstractAnnotatedHandler
+ {
+ @OnMessage
+ public void onMessage(ByteBuffer message)
+ {
+ sendBinary(message, true);
+ }
+ }
+
+ @ServerEndpoint("/")
+ public static class AnnotatedByteBufferPartialHandler extends AbstractAnnotatedHandler
+ {
+ @OnMessage
+ public void onMessage(ByteBuffer message, boolean last)
+ {
+ sendBinary(message, last);
+ }
+ }
+
+ @ServerEndpoint("/")
+ public static class AnnotatedByteArrayWholeHandler extends AbstractAnnotatedHandler
+ {
+ @OnMessage
+ public void onMessage(byte[] message)
+ {
+ sendBinary(BufferUtil.toBuffer(message), true);
+ }
+ }
+
+ @ServerEndpoint("/")
+ public static class AnnotatedByteArrayPartialHandler extends AbstractAnnotatedHandler
+ {
+ @OnMessage
+ public void onMessage(byte[] message, boolean last)
+ {
+ sendBinary(BufferUtil.toBuffer(message), last);
+ }
+ }
+
+ @ServerEndpoint("/")
+ public static class AnnotatedInputStreamWholeHandler extends AbstractAnnotatedHandler
+ {
+ @OnMessage
+ public void onMessage(InputStream stream)
+ {
+ sendBinary(readBytes(stream), true);
+ }
+ }
+
+ @ServerEndpoint("/")
+ public static class AnnotatedReverseArgumentPartialHandler extends AbstractAnnotatedHandler
+ {
+ @OnMessage
+ public void onMessage(boolean last, Session session, byte[] message)
+ {
+ sendBinary(BufferUtil.toBuffer(message), last);
+ }
+ }
+
+ private static ByteBuffer readBytes(InputStream stream)
+ {
+ try
+ {
+ return BufferUtil.toBuffer(IO.readBytes(stream));
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ByteArrayPartialHandler.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ByteArrayPartialHandler.java
deleted file mode 100644
index 32f9bc222e9..00000000000
--- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ByteArrayPartialHandler.java
+++ /dev/null
@@ -1,30 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-//
-// This program and the accompanying materials are made available under
-// the terms of the Eclipse Public License 2.0 which is available at
-// https://www.eclipse.org/legal/epl-2.0
-//
-// This Source Code may also be made available under the following
-// Secondary Licenses when the conditions for such availability set
-// forth in the Eclipse Public License, v. 2.0 are satisfied:
-// the Apache License v2.0 which is available at
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
-// ========================================================================
-//
-
-package org.eclipse.jetty.websocket.javax.tests.handlers;
-
-import javax.websocket.MessageHandler;
-
-public class ByteArrayPartialHandler implements MessageHandler.Partial
-{
- @Override
- public void onMessage(byte[] partialMessage, boolean last)
- {
- // TODO Auto-generated method stub
- }
-}
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ByteArrayWholeHandler.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ByteArrayWholeHandler.java
deleted file mode 100644
index 9c93050a373..00000000000
--- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ByteArrayWholeHandler.java
+++ /dev/null
@@ -1,30 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-//
-// This program and the accompanying materials are made available under
-// the terms of the Eclipse Public License 2.0 which is available at
-// https://www.eclipse.org/legal/epl-2.0
-//
-// This Source Code may also be made available under the following
-// Secondary Licenses when the conditions for such availability set
-// forth in the Eclipse Public License, v. 2.0 are satisfied:
-// the Apache License v2.0 which is available at
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
-// ========================================================================
-//
-
-package org.eclipse.jetty.websocket.javax.tests.handlers;
-
-import javax.websocket.MessageHandler;
-
-public class ByteArrayWholeHandler implements MessageHandler.Whole
-{
- @Override
- public void onMessage(byte[] message)
- {
- // TODO Auto-generated method stub
- }
-}
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ByteBufferPartialHandler.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ByteBufferPartialHandler.java
deleted file mode 100644
index f203e9a0cd9..00000000000
--- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ByteBufferPartialHandler.java
+++ /dev/null
@@ -1,30 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-//
-// This program and the accompanying materials are made available under
-// the terms of the Eclipse Public License 2.0 which is available at
-// https://www.eclipse.org/legal/epl-2.0
-//
-// This Source Code may also be made available under the following
-// Secondary Licenses when the conditions for such availability set
-// forth in the Eclipse Public License, v. 2.0 are satisfied:
-// the Apache License v2.0 which is available at
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
-// ========================================================================
-//
-
-package org.eclipse.jetty.websocket.javax.tests.handlers;
-
-import java.nio.ByteBuffer;
-import javax.websocket.MessageHandler;
-
-public class ByteBufferPartialHandler implements MessageHandler.Partial
-{
- @Override
- public void onMessage(ByteBuffer partialMessage, boolean last)
- {
- }
-}
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ByteBufferWholeHandler.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ByteBufferWholeHandler.java
deleted file mode 100644
index 4e350776b28..00000000000
--- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ByteBufferWholeHandler.java
+++ /dev/null
@@ -1,31 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-//
-// This program and the accompanying materials are made available under
-// the terms of the Eclipse Public License 2.0 which is available at
-// https://www.eclipse.org/legal/epl-2.0
-//
-// This Source Code may also be made available under the following
-// Secondary Licenses when the conditions for such availability set
-// forth in the Eclipse Public License, v. 2.0 are satisfied:
-// the Apache License v2.0 which is available at
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
-// ========================================================================
-//
-
-package org.eclipse.jetty.websocket.javax.tests.handlers;
-
-import java.nio.ByteBuffer;
-import javax.websocket.MessageHandler;
-
-public class ByteBufferWholeHandler implements MessageHandler.Whole
-{
- @Override
- public void onMessage(ByteBuffer message)
- {
- // TODO Auto-generated method stub
- }
-}
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ComboMessageHandler.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ComboMessageHandler.java
index a266e5a5c1d..9067fed437d 100644
--- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ComboMessageHandler.java
+++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ComboMessageHandler.java
@@ -24,17 +24,17 @@ import javax.websocket.MessageHandler;
/**
* A particularly annoying type of MessageHandler. One defining 2 implementations.
*/
-public class ComboMessageHandler implements MessageHandler.Whole, MessageHandler.Partial
+public class ComboMessageHandler extends AbstractHandler implements MessageHandler.Whole, MessageHandler.Partial
{
@Override
public void onMessage(ByteBuffer partialMessage, boolean last)
{
- // TODO Auto-generated method stub
+ sendBinary(partialMessage, last);
}
@Override
public void onMessage(String message)
{
- // TODO Auto-generated method stub
+ sendText(message, true);
}
}
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ExtendedMessageHandler.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ExtendedMessageHandler.java
index 9cb1f749d85..76c10824fc0 100644
--- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ExtendedMessageHandler.java
+++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ExtendedMessageHandler.java
@@ -26,6 +26,6 @@ public class ExtendedMessageHandler extends BaseMessageHandler implements Messag
@Override
public void onMessage(ByteBuffer partialMessage, boolean last)
{
- // TODO Auto-generated method stub
+ sendBinary(partialMessage, last);
}
}
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/InputStreamWholeHandler.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/InputStreamWholeHandler.java
deleted file mode 100644
index 34c5af063b8..00000000000
--- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/InputStreamWholeHandler.java
+++ /dev/null
@@ -1,31 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-//
-// This program and the accompanying materials are made available under
-// the terms of the Eclipse Public License 2.0 which is available at
-// https://www.eclipse.org/legal/epl-2.0
-//
-// This Source Code may also be made available under the following
-// Secondary Licenses when the conditions for such availability set
-// forth in the Eclipse Public License, v. 2.0 are satisfied:
-// the Apache License v2.0 which is available at
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
-// ========================================================================
-//
-
-package org.eclipse.jetty.websocket.javax.tests.handlers;
-
-import java.io.InputStream;
-import javax.websocket.MessageHandler;
-
-public class InputStreamWholeHandler implements MessageHandler.Whole
-{
- @Override
- public void onMessage(InputStream stream)
- {
- // TODO Auto-generated method stub
- }
-}
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/LongMessageHandler.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/LongMessageHandler.java
index f1155847855..46d1500997c 100644
--- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/LongMessageHandler.java
+++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/LongMessageHandler.java
@@ -20,10 +20,11 @@ package org.eclipse.jetty.websocket.javax.tests.handlers;
import javax.websocket.MessageHandler;
-public class LongMessageHandler implements MessageHandler.Whole
+public class LongMessageHandler extends AbstractHandler implements MessageHandler.Whole
{
@Override
public void onMessage(Long message)
{
+ sendText(message.toString(), true);
}
}
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/MessageHandlerTest.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/MessageHandlerTest.java
new file mode 100644
index 00000000000..92a4db28a11
--- /dev/null
+++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/MessageHandlerTest.java
@@ -0,0 +1,167 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.javax.tests.handlers;
+
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.websocket.CloseReason;
+import javax.websocket.ContainerProvider;
+import javax.websocket.Session;
+import javax.websocket.WebSocketContainer;
+import javax.websocket.server.ServerEndpointConfig;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
+import org.eclipse.jetty.websocket.javax.tests.EventSocket;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static javax.websocket.CloseReason.CloseCodes.NORMAL_CLOSURE;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class MessageHandlerTest
+{
+ private Server server;
+ private URI serverUri;
+ private WebSocketContainer client;
+
+ private static Stream getBinaryHandlers()
+ {
+ return Stream.concat(BinaryHandlers.getBinaryHandlers(),
+ Stream.of(ComboMessageHandler.class, ExtendedMessageHandler.class).map(Arguments::of));
+ }
+
+ private static Stream getTextHandlers()
+ {
+ return Stream.concat(TextHandlers.getTextHandlers(),
+ Stream.of(ComboMessageHandler.class, ExtendedMessageHandler.class).map(Arguments::of));
+ }
+
+ @BeforeEach
+ public void before() throws Exception
+ {
+ server = new Server();
+ ServerConnector connector = new ServerConnector(server);
+ server.addConnector(connector);
+
+ ServletContextHandler contextHandler = new ServletContextHandler();
+ contextHandler.setContextPath("/");
+ JavaxWebSocketServletContainerInitializer.configure(contextHandler, (context, container) ->
+ {
+ Stream argumentsStream = Stream.concat(getBinaryHandlers(), getTextHandlers());
+ for (Class> c : getClassListFromArguments(argumentsStream))
+ {
+ container.addEndpoint(ServerEndpointConfig.Builder.create(c, "/" + c.getSimpleName()).build());
+ }
+
+ container.addEndpoint(ServerEndpointConfig.Builder.create(LongMessageHandler.class,
+ "/" + LongMessageHandler.class.getSimpleName()).build());
+ });
+
+ server.setHandler(contextHandler);
+ server.start();
+ serverUri = URI.create("ws://localhost:" + connector.getLocalPort() + "/");
+ client = ContainerProvider.getWebSocketContainer();
+ }
+
+ @AfterEach
+ public void after() throws Exception
+ {
+ LifeCycle.stop(client);
+ server.stop();
+ }
+
+ @ParameterizedTest
+ @MethodSource("getBinaryHandlers")
+ public void testBinaryHandlers(Class> clazz) throws Exception
+ {
+ EventSocket clientEndpoint = new EventSocket();
+ Session session = client.connectToServer(clientEndpoint, serverUri.resolve(clazz.getSimpleName()));
+
+ // Send and receive echo on client.
+ ByteBuffer payload = BufferUtil.toBuffer("hello world");
+ session.getBasicRemote().sendBinary(payload);
+ ByteBuffer echoMessage = clientEndpoint.binaryMessages.poll(5, TimeUnit.SECONDS);
+ assertThat(echoMessage, is(payload));
+
+ // Close normally.
+ session.close(new CloseReason(NORMAL_CLOSURE, "standard close"));
+ assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
+ assertThat(clientEndpoint.closeReason.getCloseCode(), is(NORMAL_CLOSURE));
+ assertThat(clientEndpoint.closeReason.getReasonPhrase(), is("standard close"));
+ }
+
+ @ParameterizedTest
+ @MethodSource("getTextHandlers")
+ public void testTextHandlers(Class> clazz) throws Exception
+ {
+ EventSocket clientEndpoint = new EventSocket();
+ Session session = client.connectToServer(clientEndpoint, serverUri.resolve(clazz.getSimpleName()));
+
+ // Send and receive echo on client.
+ String payload = "hello world";
+ session.getBasicRemote().sendText(payload);
+ String echoMessage = clientEndpoint.textMessages.poll(5, TimeUnit.SECONDS);
+ assertThat(echoMessage, is(payload));
+
+ // Close normally.
+ session.close(new CloseReason(NORMAL_CLOSURE, "standard close"));
+ assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
+ assertThat(clientEndpoint.closeReason.getCloseCode(), is(NORMAL_CLOSURE));
+ assertThat(clientEndpoint.closeReason.getReasonPhrase(), is("standard close"));
+ }
+
+ @Test
+ public void testLongDecoderHandler() throws Exception
+ {
+ EventSocket clientEndpoint = new EventSocket();
+ Session session = client.connectToServer(clientEndpoint, serverUri.resolve(LongMessageHandler.class.getSimpleName()));
+
+ // Send and receive echo on client.
+ String payload = Long.toString(Long.MAX_VALUE);
+ session.getBasicRemote().sendText(payload);
+ String echoMessage = clientEndpoint.textMessages.poll(5, TimeUnit.SECONDS);
+ assertThat(echoMessage, is(payload));
+
+ // Close normally.
+ session.close(new CloseReason(NORMAL_CLOSURE, "standard close"));
+ assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
+ assertThat(clientEndpoint.closeReason.getCloseCode(), is(NORMAL_CLOSURE));
+ assertThat(clientEndpoint.closeReason.getReasonPhrase(), is("standard close"));
+ }
+
+ private List> getClassListFromArguments(Stream stream)
+ {
+ return stream.map(arguments -> (Class>)arguments.get()[0]).collect(Collectors.toList());
+ }
+}
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ReaderWholeHandler.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ReaderWholeHandler.java
deleted file mode 100644
index d8637aa7080..00000000000
--- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/ReaderWholeHandler.java
+++ /dev/null
@@ -1,31 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-//
-// This program and the accompanying materials are made available under
-// the terms of the Eclipse Public License 2.0 which is available at
-// https://www.eclipse.org/legal/epl-2.0
-//
-// This Source Code may also be made available under the following
-// Secondary Licenses when the conditions for such availability set
-// forth in the Eclipse Public License, v. 2.0 are satisfied:
-// the Apache License v2.0 which is available at
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
-// ========================================================================
-//
-
-package org.eclipse.jetty.websocket.javax.tests.handlers;
-
-import java.io.Reader;
-import javax.websocket.MessageHandler;
-
-public class ReaderWholeHandler implements MessageHandler.Whole
-{
- @Override
- public void onMessage(Reader reader)
- {
- // TODO Auto-generated method stub
- }
-}
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/StringPartialHandler.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/StringPartialHandler.java
deleted file mode 100644
index d614d282a1f..00000000000
--- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/StringPartialHandler.java
+++ /dev/null
@@ -1,30 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-//
-// This program and the accompanying materials are made available under
-// the terms of the Eclipse Public License 2.0 which is available at
-// https://www.eclipse.org/legal/epl-2.0
-//
-// This Source Code may also be made available under the following
-// Secondary Licenses when the conditions for such availability set
-// forth in the Eclipse Public License, v. 2.0 are satisfied:
-// the Apache License v2.0 which is available at
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
-// ========================================================================
-//
-
-package org.eclipse.jetty.websocket.javax.tests.handlers;
-
-import javax.websocket.MessageHandler;
-
-public class StringPartialHandler implements MessageHandler.Partial
-{
- @Override
- public void onMessage(String partialMessage, boolean last)
- {
- // TODO Auto-generated method stub
- }
-}
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/StringWholeHandler.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/StringWholeHandler.java
deleted file mode 100644
index 2f072ebc9bb..00000000000
--- a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/StringWholeHandler.java
+++ /dev/null
@@ -1,30 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-//
-// This program and the accompanying materials are made available under
-// the terms of the Eclipse Public License 2.0 which is available at
-// https://www.eclipse.org/legal/epl-2.0
-//
-// This Source Code may also be made available under the following
-// Secondary Licenses when the conditions for such availability set
-// forth in the Eclipse Public License, v. 2.0 are satisfied:
-// the Apache License v2.0 which is available at
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
-// ========================================================================
-//
-
-package org.eclipse.jetty.websocket.javax.tests.handlers;
-
-import javax.websocket.MessageHandler;
-
-public class StringWholeHandler implements MessageHandler.Whole
-{
- @Override
- public void onMessage(String message)
- {
- // TODO Auto-generated method stub
- }
-}
diff --git a/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/TextHandlers.java b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/TextHandlers.java
new file mode 100644
index 00000000000..b5badb5ec0a
--- /dev/null
+++ b/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/handlers/TextHandlers.java
@@ -0,0 +1,125 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.javax.tests.handlers;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.stream.Stream;
+import javax.websocket.MessageHandler;
+import javax.websocket.OnMessage;
+import javax.websocket.Session;
+import javax.websocket.server.ServerEndpoint;
+
+import org.eclipse.jetty.util.IO;
+import org.junit.jupiter.params.provider.Arguments;
+
+public class TextHandlers
+{
+ public static Stream getTextHandlers()
+ {
+ return Stream.of(
+ StringWholeHandler.class,
+ StringPartialHandler.class,
+ ReaderWholeHandler.class,
+ AnnotatedStringWholeHandler.class,
+ AnnotatedStringPartialHandler.class,
+ AnnotatedReaderHandler.class,
+ AnnotatedReverseArgumentsPartialHandler.class
+ ).map(Arguments::of);
+ }
+
+ public static class StringWholeHandler extends AbstractHandler implements MessageHandler.Whole
+ {
+ @Override
+ public void onMessage(String message)
+ {
+ sendText(message, true);
+ }
+ }
+
+ public static class StringPartialHandler extends AbstractHandler implements MessageHandler.Partial
+ {
+ @Override
+ public void onMessage(String partialMessage, boolean last)
+ {
+ sendText(partialMessage, last);
+ }
+ }
+
+ public static class ReaderWholeHandler extends AbstractHandler implements MessageHandler.Whole
+ {
+ @Override
+ public void onMessage(Reader reader)
+ {
+ sendText(readString(reader), true);
+ }
+ }
+
+ @ServerEndpoint("/")
+ public static class AnnotatedStringWholeHandler extends AbstractAnnotatedHandler
+ {
+ @OnMessage
+ public void onMessage(String message)
+ {
+ sendText(message, true);
+ }
+ }
+
+ @ServerEndpoint("/")
+ public static class AnnotatedStringPartialHandler extends AbstractAnnotatedHandler
+ {
+ @OnMessage
+ public void onMessage(String message, boolean last)
+ {
+ sendText(message, last);
+ }
+ }
+
+ @ServerEndpoint("/")
+ public static class AnnotatedReaderHandler extends AbstractAnnotatedHandler
+ {
+ @OnMessage
+ public void onMessage(Reader reader)
+ {
+ sendText(readString(reader), true);
+ }
+ }
+
+ @ServerEndpoint("/")
+ public static class AnnotatedReverseArgumentsPartialHandler extends AbstractAnnotatedHandler
+ {
+ @OnMessage
+ public void onMessage(boolean last, String message, Session session)
+ {
+ sendText(message, last);
+ }
+ }
+
+ private static String readString(Reader reader)
+ {
+ try
+ {
+ return IO.toString(reader);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketConnectionListener.java b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketConnectionListener.java
index 7dac7419859..fd98cae3cfe 100644
--- a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketConnectionListener.java
+++ b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketConnectionListener.java
@@ -31,7 +31,9 @@ public interface WebSocketConnectionListener
* @param statusCode the close status code. (See {@link StatusCode})
* @param reason the optional reason for the close.
*/
- void onWebSocketClose(int statusCode, String reason);
+ default void onWebSocketClose(int statusCode, String reason)
+ {
+ }
/**
* A WebSocket {@link Session} has connected successfully and is ready to be used.
@@ -40,7 +42,9 @@ public interface WebSocketConnectionListener
*
* @param session the websocket session.
*/
- void onWebSocketConnect(Session session);
+ default void onWebSocketConnect(Session session)
+ {
+ }
/**
* A WebSocket exception has occurred.
@@ -53,5 +57,7 @@ public interface WebSocketConnectionListener
*
* @param cause the error that occurred.
*/
- void onWebSocketError(Throwable cause);
+ default void onWebSocketError(Throwable cause)
+ {
+ }
}
diff --git a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketListener.java b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketListener.java
index 8f11b97c375..ec3b059ceac 100644
--- a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketListener.java
+++ b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketListener.java
@@ -30,12 +30,16 @@ public interface WebSocketListener extends WebSocketConnectionListener
* @param offset the offset in the payload array where the data starts
* @param len the length of bytes in the payload
*/
- void onWebSocketBinary(byte[] payload, int offset, int len);
+ default void onWebSocketBinary(byte[] payload, int offset, int len)
+ {
+ }
/**
* A WebSocket Text frame was received.
*
* @param message the message
*/
- void onWebSocketText(String message);
+ default void onWebSocketText(String message)
+ {
+ }
}
diff --git a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPartialListener.java b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPartialListener.java
index 26b4cc0a830..ec676b7387f 100644
--- a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPartialListener.java
+++ b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPartialListener.java
@@ -35,7 +35,9 @@ public interface WebSocketPartialListener extends WebSocketConnectionListener
* @param payload the binary message frame payload
* @param fin true if this is the final frame, false otherwise
*/
- void onWebSocketPartialBinary(ByteBuffer payload, boolean fin);
+ default void onWebSocketPartialBinary(ByteBuffer payload, boolean fin)
+ {
+ }
/**
* A WebSocket TEXT (or associated CONTINUATION) frame has been received.
@@ -50,5 +52,7 @@ public interface WebSocketPartialListener extends WebSocketConnectionListener
* will be held over until the next frame is received.
* @param fin true if this is the final frame, false otherwise
*/
- void onWebSocketPartialText(String payload, boolean fin);
+ default void onWebSocketPartialText(String payload, boolean fin)
+ {
+ }
}
diff --git a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPingPongListener.java b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPingPongListener.java
index dde0f7fb0ea..968ac27d937 100644
--- a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPingPongListener.java
+++ b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPingPongListener.java
@@ -30,12 +30,16 @@ public interface WebSocketPingPongListener extends WebSocketConnectionListener
*
* @param payload the ping payload
*/
- void onWebSocketPing(ByteBuffer payload);
+ default void onWebSocketPing(ByteBuffer payload)
+ {
+ }
/**
* A WebSocket PONG has been received.
*
* @param payload the pong payload
*/
- void onWebSocketPong(ByteBuffer payload);
+ default void onWebSocketPong(ByteBuffer payload)
+ {
+ }
}
diff --git a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketMessage.java b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketMessage.java
index acbee3fbd3b..dc58cf9973e 100644
--- a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketMessage.java
+++ b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketMessage.java
@@ -35,9 +35,9 @@ import org.eclipse.jetty.websocket.api.Session;
*
* Text Message Versions
*
- *
{@code public void methodName(String text)}
+ *
public void methodName(String text)
*
public void methodName({@link Session} session, String text)
- *
{@code public void methodName(Reader reader)}
+ *
public void methodName(Reader reader)
*
public void methodName({@link Session} session, Reader reader)
*
* Note: that the {@link Reader} in this case will always use UTF-8 encoding/charset (this is dictated by the RFC 6455 spec for Text Messages. If you need to
@@ -45,11 +45,11 @@ import org.eclipse.jetty.websocket.api.Session;
*
* Binary Message Versions
*
- *
{@code public void methodName(ByteBuffer message)}
+ *
public void methodName(ByteBuffer message)
*
public void methodName({@link Session} session, ByteBuffer message)
- *
{@code public void methodName(byte buf[], int offset, int length)}
+ *
public void methodName(byte buf[], int offset, int length)
*
public void methodName({@link Session} session, byte buf[], int offset, int length)
- *
{@code public void methodName(InputStream stream)}
+ *
public void methodName(InputStream stream)
*
public void methodName({@link Session} session, InputStream stream)
*
*/
diff --git a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java
index 13a22ab6d0f..0d08b373c4f 100644
--- a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java
+++ b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java
@@ -296,15 +296,9 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle
final InvokerUtils.Arg STATUS_CODE = new InvokerUtils.Arg(int.class);
final InvokerUtils.Arg REASON = new InvokerUtils.Arg(String.class);
MethodHandle methodHandle = InvokerUtils.mutatedInvoker(lookup, endpointClass, onmethod, SESSION, STATUS_CODE, REASON);
- // TODO: need mutation of args? ...
- // Session + CloseInfo ->
- // setOnClose((closeInfo) ->{
- // args[0] = getSession();
- // args[1] = closeInfo.getStatusCode();
- // args[2] = closeInfo.getReason();
- // invoker.apply(endpoint, args);
metadata.setCloseHandler(methodHandle, onmethod);
}
+
// OnWebSocketError [0..1]
onmethod = ReflectUtils.findAnnotatedMethod(endpointClass, OnWebSocketError.class);
if (onmethod != null)
@@ -360,7 +354,6 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle
new InvokerUtils.Arg(Reader.class).required()
};
- onmessageloop:
for (Method onMsg : onMessages)
{
assertSignatureValid(endpointClass, onMsg, OnWebSocketMessage.class);
@@ -371,7 +364,7 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle
// Normal Text Message
assertSignatureValid(endpointClass, onMsg, OnWebSocketMessage.class);
metadata.setTextHandler(StringMessageSink.class, methodHandle, onMsg);
- continue onmessageloop;
+ continue;
}
methodHandle = InvokerUtils.optionalMutatedInvoker(lookup, endpointClass, onMsg, binaryBufferCallingArgs);
@@ -380,7 +373,7 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle
// ByteBuffer Binary Message
assertSignatureValid(endpointClass, onMsg, OnWebSocketMessage.class);
metadata.setBinaryHandle(ByteBufferMessageSink.class, methodHandle, onMsg);
- continue onmessageloop;
+ continue;
}
methodHandle = InvokerUtils.optionalMutatedInvoker(lookup, endpointClass, onMsg, binaryArrayCallingArgs);
@@ -389,7 +382,7 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle
// byte[] Binary Message
assertSignatureValid(endpointClass, onMsg, OnWebSocketMessage.class);
metadata.setBinaryHandle(ByteArrayMessageSink.class, methodHandle, onMsg);
- continue onmessageloop;
+ continue;
}
methodHandle = InvokerUtils.optionalMutatedInvoker(lookup, endpointClass, onMsg, inputStreamCallingArgs);
@@ -398,7 +391,7 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle
// InputStream Binary Message
assertSignatureValid(endpointClass, onMsg, OnWebSocketMessage.class);
metadata.setBinaryHandle(InputStreamMessageSink.class, methodHandle, onMsg);
- continue onmessageloop;
+ continue;
}
methodHandle = InvokerUtils.optionalMutatedInvoker(lookup, endpointClass, onMsg, readerCallingArgs);
@@ -407,7 +400,7 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle
// Reader Text Message
assertSignatureValid(endpointClass, onMsg, OnWebSocketMessage.class);
metadata.setTextHandler(ReaderMessageSink.class, methodHandle, onMsg);
- continue onmessageloop;
+ continue;
}
else
{
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ConcurrentConnectTest.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ConcurrentConnectTest.java
index d0cb78f7884..4eddfa4c677 100644
--- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ConcurrentConnectTest.java
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ConcurrentConnectTest.java
@@ -122,15 +122,15 @@ public class ConcurrentConnectTest
for (EventSocket l : listeners)
{
l.session.getRemote().sendString("ping");
- assertThat(l.messageQueue.poll(5, TimeUnit.SECONDS), is("ping"));
+ assertThat(l.textMessages.poll(5, TimeUnit.SECONDS), is("ping"));
l.session.close(StatusCode.NORMAL, "close from client");
}
for (EventSocket l : listeners)
{
assertTrue(l.closeLatch.await(5, TimeUnit.SECONDS));
- assertThat(l.statusCode, is(StatusCode.NORMAL));
- assertThat(l.reason, is("close from client"));
+ assertThat(l.closeCode, is(StatusCode.NORMAL));
+ assertThat(l.closeReason, is("close from client"));
assertNull(l.error);
}
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/EventSocket.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/EventSocket.java
index 69d3fcb9e71..bffbe3825e5 100644
--- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/EventSocket.java
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/EventSocket.java
@@ -42,10 +42,10 @@ public class EventSocket
public Session session;
private String behavior;
- public BlockingQueue messageQueue = new BlockingArrayQueue<>();
- public BlockingQueue binaryMessageQueue = new BlockingArrayQueue<>();
- public volatile int statusCode = StatusCode.UNDEFINED;
- public volatile String reason;
+ public BlockingQueue textMessages = new BlockingArrayQueue<>();
+ public BlockingQueue binaryMessages = new BlockingArrayQueue<>();
+ public volatile int closeCode = StatusCode.UNDEFINED;
+ public volatile String closeReason;
public volatile Throwable error = null;
public CountDownLatch openLatch = new CountDownLatch(1);
@@ -67,7 +67,7 @@ public class EventSocket
{
if (LOG.isDebugEnabled())
LOG.debug("{} onMessage(): {}", toString(), message);
- messageQueue.offer(message);
+ textMessages.offer(message);
}
@OnWebSocketMessage
@@ -76,7 +76,7 @@ public class EventSocket
ByteBuffer message = ByteBuffer.wrap(buf, offset, len);
if (LOG.isDebugEnabled())
LOG.debug("{} onMessage(): {}", toString(), message);
- binaryMessageQueue.offer(message);
+ binaryMessages.offer(message);
}
@OnWebSocketClose
@@ -84,8 +84,8 @@ public class EventSocket
{
if (LOG.isDebugEnabled())
LOG.debug("{} onClose(): {}:{}", toString(), statusCode, reason);
- this.statusCode = statusCode;
- this.reason = reason;
+ this.closeCode = statusCode;
+ this.closeReason = reason;
closeLatch.countDown();
}
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyOnCloseTest.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyOnCloseTest.java
index 77b15837219..fb70a93f8ee 100644
--- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyOnCloseTest.java
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyOnCloseTest.java
@@ -130,8 +130,8 @@ public class JettyOnCloseTest
clientEndpoint.session.close();
assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
- assertThat(clientEndpoint.statusCode, is(StatusCode.SERVICE_RESTART));
- assertThat(clientEndpoint.reason, is("custom close reason"));
+ assertThat(clientEndpoint.closeCode, is(StatusCode.SERVICE_RESTART));
+ assertThat(clientEndpoint.closeReason, is("custom close reason"));
}
@Test
@@ -146,8 +146,8 @@ public class JettyOnCloseTest
serverEndpoint.session.close(StatusCode.NORMAL, "first close");
assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
- assertThat(clientEndpoint.statusCode, is(StatusCode.NORMAL));
- assertThat(clientEndpoint.reason, is("first close"));
+ assertThat(clientEndpoint.closeCode, is(StatusCode.NORMAL));
+ assertThat(clientEndpoint.closeReason, is("first close"));
}
@Test
@@ -166,8 +166,8 @@ public class JettyOnCloseTest
serverEndpoint.session.close(StatusCode.PROTOCOL, "abnormal close 1");
assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
- assertThat(clientEndpoint.statusCode, is(StatusCode.PROTOCOL));
- assertThat(clientEndpoint.reason, is("abnormal close 1"));
+ assertThat(clientEndpoint.closeCode, is(StatusCode.PROTOCOL));
+ assertThat(clientEndpoint.closeReason, is("abnormal close 1"));
}
@Test
@@ -185,8 +185,8 @@ public class JettyOnCloseTest
clientEndpoint.session.close();
assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
- assertThat(clientEndpoint.statusCode, is(StatusCode.SERVER_ERROR));
- assertThat(clientEndpoint.reason, containsString("trigger onError from onClose"));
+ assertThat(clientEndpoint.closeCode, is(StatusCode.SERVER_ERROR));
+ assertThat(clientEndpoint.closeReason, containsString("trigger onError from onClose"));
assertTrue(serverEndpoint.errorLatch.await(5, TimeUnit.SECONDS));
assertThat(serverEndpoint.error, instanceOf(RuntimeException.class));
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketExtensionConfigTest.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketExtensionConfigTest.java
index 620a59ee419..9b2a4cc74a0 100644
--- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketExtensionConfigTest.java
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketExtensionConfigTest.java
@@ -130,7 +130,7 @@ public class JettyWebSocketExtensionConfigTest
assertTrue(socket.closeLatch.await(5, TimeUnit.SECONDS));
assertTrue(correctResponseExtensions.await(5, TimeUnit.SECONDS));
- String msg = socket.messageQueue.poll();
+ String msg = socket.textMessages.poll();
assertThat(msg, is("hello world"));
}
}
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketFilterTest.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketFilterTest.java
index 2543e78f4c4..488eb796a9b 100644
--- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketFilterTest.java
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketFilterTest.java
@@ -80,7 +80,7 @@ public class JettyWebSocketFilterTest
}
assertTrue(socket.closeLatch.await(10, TimeUnit.SECONDS));
- String msg = socket.messageQueue.poll();
+ String msg = socket.textMessages.poll();
assertThat(msg, is("hello world"));
}
}
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketServletTest.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketServletTest.java
index 2bf60c8dd27..926ecd0bdd3 100644
--- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketServletTest.java
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketServletTest.java
@@ -92,7 +92,7 @@ public class JettyWebSocketServletTest
}
assertTrue(socket.closeLatch.await(10, TimeUnit.SECONDS));
- String msg = socket.messageQueue.poll();
+ String msg = socket.textMessages.poll();
assertThat(msg, is("hello world"));
}
}
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/SuspendResumeTest.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/SuspendResumeTest.java
index 90556e1823e..e919baada8f 100644
--- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/SuspendResumeTest.java
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/SuspendResumeTest.java
@@ -110,16 +110,16 @@ public class SuspendResumeTest
clientSocket.session.getRemote().sendString("suspend");
clientSocket.session.getRemote().sendString("hello world");
- assertThat(serverSocket.messageQueue.poll(5, TimeUnit.SECONDS), is("suspend"));
- assertNull(serverSocket.messageQueue.poll(1, TimeUnit.SECONDS));
+ assertThat(serverSocket.textMessages.poll(5, TimeUnit.SECONDS), is("suspend"));
+ assertNull(serverSocket.textMessages.poll(1, TimeUnit.SECONDS));
serverSocket.suspendToken.resume();
- assertThat(serverSocket.messageQueue.poll(5, TimeUnit.SECONDS), is("suspend"));
- assertNull(serverSocket.messageQueue.poll(1, TimeUnit.SECONDS));
+ assertThat(serverSocket.textMessages.poll(5, TimeUnit.SECONDS), is("suspend"));
+ assertNull(serverSocket.textMessages.poll(1, TimeUnit.SECONDS));
serverSocket.suspendToken.resume();
- assertThat(serverSocket.messageQueue.poll(5, TimeUnit.SECONDS), is("hello world"));
- assertNull(serverSocket.messageQueue.poll(1, TimeUnit.SECONDS));
+ assertThat(serverSocket.textMessages.poll(5, TimeUnit.SECONDS), is("hello world"));
+ assertNull(serverSocket.textMessages.poll(1, TimeUnit.SECONDS));
// make sure both sides are closed
clientSocket.session.close();
@@ -142,22 +142,22 @@ public class SuspendResumeTest
// verify connection by sending a message from server to client
assertTrue(serverSocket.openLatch.await(5, TimeUnit.SECONDS));
serverSocket.session.getRemote().sendString("verification");
- assertThat(clientSocket.messageQueue.poll(5, TimeUnit.SECONDS), is("verification"));
+ assertThat(clientSocket.textMessages.poll(5, TimeUnit.SECONDS), is("verification"));
// suspend the client so that no read events occur
SuspendToken suspendToken = clientSocket.session.suspend();
// verify client can still send messages
clientSocket.session.getRemote().sendString("message-from-client");
- assertThat(serverSocket.messageQueue.poll(5, TimeUnit.SECONDS), is("message-from-client"));
+ assertThat(serverSocket.textMessages.poll(5, TimeUnit.SECONDS), is("message-from-client"));
// the message is not received as it is suspended
serverSocket.session.getRemote().sendString("message-from-server");
- assertNull(clientSocket.messageQueue.poll(2, TimeUnit.SECONDS));
+ assertNull(clientSocket.textMessages.poll(2, TimeUnit.SECONDS));
// client should receive message after it resumes
suspendToken.resume();
- assertThat(clientSocket.messageQueue.poll(5, TimeUnit.SECONDS), is("message-from-server"));
+ assertThat(clientSocket.textMessages.poll(5, TimeUnit.SECONDS), is("message-from-server"));
// make sure both sides are closed
clientSocket.session.close();
@@ -180,7 +180,7 @@ public class SuspendResumeTest
// verify connection by sending a message from server to client
assertTrue(serverSocket.openLatch.await(5, TimeUnit.SECONDS));
serverSocket.session.getRemote().sendString("verification");
- assertThat(clientSocket.messageQueue.poll(5, TimeUnit.SECONDS), is("verification"));
+ assertThat(clientSocket.textMessages.poll(5, TimeUnit.SECONDS), is("verification"));
// make sure both sides are closed
clientSocket.session.close();
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketOverHTTP2Test.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketOverHTTP2Test.java
index cd590d1507f..aad53455047 100644
--- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketOverHTTP2Test.java
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketOverHTTP2Test.java
@@ -167,13 +167,13 @@ public class WebSocketOverHTTP2Test
String text = "websocket";
session.getRemote().sendString(text);
- String message = wsEndPoint.messageQueue.poll(5, TimeUnit.SECONDS);
+ String message = wsEndPoint.textMessages.poll(5, TimeUnit.SECONDS);
assertNotNull(message);
assertEquals(text, message);
session.close(StatusCode.NORMAL, null);
assertTrue(wsEndPoint.closeLatch.await(5, TimeUnit.SECONDS));
- assertEquals(StatusCode.NORMAL, wsEndPoint.statusCode);
+ assertEquals(StatusCode.NORMAL, wsEndPoint.closeCode);
assertNull(wsEndPoint.error);
}
@@ -230,7 +230,7 @@ public class WebSocketOverHTTP2Test
String text = "websocket";
session.getRemote().sendString(text);
- String message = wsEndPoint.messageQueue.poll(5, TimeUnit.SECONDS);
+ String message = wsEndPoint.textMessages.poll(5, TimeUnit.SECONDS);
assertNotNull(message);
assertEquals(text, message);
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketServletExamplesTest.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketServletExamplesTest.java
index 4f309ab5cf0..547c5c396ae 100644
--- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketServletExamplesTest.java
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketServletExamplesTest.java
@@ -123,7 +123,7 @@ public class WebSocketServletExamplesTest
String message = "hello world";
session.getRemote().sendString(message);
- String response = socket.messageQueue.poll(5, TimeUnit.SECONDS);
+ String response = socket.textMessages.poll(5, TimeUnit.SECONDS);
assertThat(response, is(message));
}
@@ -147,7 +147,7 @@ public class WebSocketServletExamplesTest
String message = "hello world";
session.getRemote().sendString(message);
- String response = socket.messageQueue.poll(5, TimeUnit.SECONDS);
+ String response = socket.textMessages.poll(5, TimeUnit.SECONDS);
assertThat(response, is(message));
}
@@ -173,7 +173,7 @@ public class WebSocketServletExamplesTest
String message = "hello world";
session.getRemote().sendString(message);
- String response = socket.messageQueue.poll(5, TimeUnit.SECONDS);
+ String response = socket.textMessages.poll(5, TimeUnit.SECONDS);
assertThat(response, is(message));
}
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketStopTest.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketStopTest.java
index 43ffc5e687f..914ddd1ae2b 100644
--- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketStopTest.java
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketStopTest.java
@@ -102,8 +102,8 @@ public class WebSocketStopTest
client.stop();
assertTrue(clientSocket1.closeLatch.await(5, TimeUnit.SECONDS));
assertTrue(clientSocket2.closeLatch.await(5, TimeUnit.SECONDS));
- assertThat(clientSocket1.statusCode, is(StatusCode.SHUTDOWN));
- assertThat(clientSocket2.statusCode, is(StatusCode.SHUTDOWN));
+ assertThat(clientSocket1.closeCode, is(StatusCode.SHUTDOWN));
+ assertThat(clientSocket2.closeCode, is(StatusCode.SHUTDOWN));
}
@Test
@@ -116,7 +116,7 @@ public class WebSocketStopTest
upgradeRequest.addExtensions("permessage-deflate");
Session session = client.connect(clientSocket, uri, upgradeRequest).get(5, TimeUnit.SECONDS);
clientSocket.session.getRemote().sendString("init deflater");
- assertThat(serverSocket.messageQueue.poll(5, TimeUnit.SECONDS), is("init deflater"));
+ assertThat(serverSocket.textMessages.poll(5, TimeUnit.SECONDS), is("init deflater"));
session.close(StatusCode.NORMAL, null);
// make sure both sides are closed
@@ -125,8 +125,8 @@ public class WebSocketStopTest
assertTrue(serverSocket.closeLatch.await(5, TimeUnit.SECONDS));
// check we closed normally
- assertThat(clientSocket.statusCode, is(StatusCode.NORMAL));
- assertThat(serverSocket.statusCode, is(StatusCode.NORMAL));
+ assertThat(clientSocket.closeCode, is(StatusCode.NORMAL));
+ assertThat(serverSocket.closeCode, is(StatusCode.NORMAL));
IOException error = assertThrows(IOException.class,
() -> session.getRemote().sendString("this should fail before ExtensionStack"));
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/autobahn/JettyAutobahnClient.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/autobahn/JettyAutobahnClient.java
index 662bbaa7bf5..916ab848b81 100644
--- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/autobahn/JettyAutobahnClient.java
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/autobahn/JettyAutobahnClient.java
@@ -161,7 +161,7 @@ public class JettyAutobahnClient
if (waitForUpgrade(wsUri, response))
{
- String msg = onCaseCount.messageQueue.poll(10, TimeUnit.SECONDS);
+ String msg = onCaseCount.textMessages.poll(10, TimeUnit.SECONDS);
onCaseCount.session.close(StatusCode.SHUTDOWN, null);
assertTrue(onCaseCount.closeLatch.await(2, TimeUnit.SECONDS));
assertNotNull(msg);
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConfigTest.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConfigTest.java
index 96d1e240536..ba3446931d6 100644
--- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConfigTest.java
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConfigTest.java
@@ -159,7 +159,7 @@ public class ClientConfigTest
assertNull(clientEndpoint.error);
assertTrue(serverSocket.closeLatch.await(5, TimeUnit.SECONDS));
- assertThat(serverSocket.statusCode, is(StatusCode.NO_CODE));
+ assertThat(serverSocket.closeCode, is(StatusCode.NO_CODE));
}
@ParameterizedTest
@@ -177,7 +177,7 @@ public class ClientConfigTest
assertThat(clientEndpoint.error, instanceOf(MessageTooLargeException.class));
assertTrue(serverSocket.closeLatch.await(5, TimeUnit.SECONDS));
- assertThat(serverSocket.statusCode, is(StatusCode.MESSAGE_TOO_LARGE));
+ assertThat(serverSocket.closeCode, is(StatusCode.MESSAGE_TOO_LARGE));
}
@ParameterizedTest
@@ -196,7 +196,7 @@ public class ClientConfigTest
assertThat(clientEndpoint.error, instanceOf(WebSocketTimeoutException.class));
assertTrue(serverSocket.closeLatch.await(5, TimeUnit.SECONDS));
- assertThat(serverSocket.statusCode, is(StatusCode.SHUTDOWN));
+ assertThat(serverSocket.closeCode, is(StatusCode.SHUTDOWN));
}
@ParameterizedTest
@@ -214,6 +214,6 @@ public class ClientConfigTest
assertThat(clientEndpoint.error, instanceOf(MessageTooLargeException.class));
assertTrue(serverSocket.closeLatch.await(5, TimeUnit.SECONDS));
- assertThat(serverSocket.statusCode, is(StatusCode.MESSAGE_TOO_LARGE));
+ assertThat(serverSocket.closeCode, is(StatusCode.MESSAGE_TOO_LARGE));
}
}
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/AbstractAnnotatedListener.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/AbstractAnnotatedListener.java
new file mode 100644
index 00000000000..c28200ac88e
--- /dev/null
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/AbstractAnnotatedListener.java
@@ -0,0 +1,69 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.tests.listeners;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
+import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
+import org.eclipse.jetty.websocket.api.annotations.WebSocket;
+
+@WebSocket
+public class AbstractAnnotatedListener
+{
+ protected Session _session;
+
+ @OnWebSocketConnect
+ public void onWebSocketConnect(Session session)
+ {
+ _session = session;
+ }
+
+ @OnWebSocketError
+ public void onWebSocketError(Throwable thr)
+ {
+ thr.printStackTrace();
+ }
+
+ public void sendText(String message, boolean last)
+ {
+ try
+ {
+ _session.getRemote().sendPartialString(message, last);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void sendBinary(ByteBuffer message, boolean last)
+ {
+ try
+ {
+ _session.getRemote().sendPartialBytes(message, last);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/AbstractListener.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/AbstractListener.java
new file mode 100644
index 00000000000..d6b3784e970
--- /dev/null
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/AbstractListener.java
@@ -0,0 +1,66 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.tests.listeners;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.WebSocketConnectionListener;
+
+public class AbstractListener implements WebSocketConnectionListener
+{
+ protected Session _session;
+
+ @Override
+ public void onWebSocketConnect(Session session)
+ {
+ _session = session;
+ }
+
+ @Override
+ public void onWebSocketError(Throwable thr)
+ {
+ thr.printStackTrace();
+ }
+
+ public void sendText(String message, boolean last)
+ {
+ try
+ {
+ _session.getRemote().sendPartialString(message, last);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void sendBinary(ByteBuffer message, boolean last)
+ {
+ try
+ {
+ _session.getRemote().sendPartialBytes(message, last);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/BinaryListeners.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/BinaryListeners.java
new file mode 100644
index 00000000000..7cc0a8850a7
--- /dev/null
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/BinaryListeners.java
@@ -0,0 +1,129 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.tests.listeners;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.stream.Stream;
+
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.WebSocketListener;
+import org.eclipse.jetty.websocket.api.WebSocketPartialListener;
+import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
+import org.eclipse.jetty.websocket.api.annotations.WebSocket;
+import org.junit.jupiter.params.provider.Arguments;
+
+public class BinaryListeners
+{
+ public static Stream getBinaryListeners()
+ {
+ return Stream.of(
+ OffsetByteArrayWholeListener.class,
+ OffsetByteBufferPartialListener.class,
+ AnnotatedByteBufferWholeListener.class,
+ AnnotatedByteArrayWholeListener.class,
+ AnnotatedOffsetByteArrayWholeListener.class,
+ AnnotatedInputStreamWholeListener.class,
+ AnnotatedReverseArgumentPartialListener.class
+ ).map(Arguments::of);
+ }
+
+ public static class OffsetByteArrayWholeListener extends AbstractListener implements WebSocketListener
+ {
+ @Override
+ public void onWebSocketBinary(byte[] payload, int offset, int len)
+ {
+ sendBinary(BufferUtil.toBuffer(payload, offset, len), true);
+ }
+ }
+
+ public static class OffsetByteBufferPartialListener extends AbstractListener implements WebSocketPartialListener
+ {
+ @Override
+ public void onWebSocketPartialBinary(ByteBuffer payload, boolean fin)
+ {
+ sendBinary(payload, fin);
+ }
+ }
+
+ @WebSocket
+ public static class AnnotatedByteBufferWholeListener extends AbstractAnnotatedListener
+ {
+ @OnWebSocketMessage
+ public void onMessage(ByteBuffer message)
+ {
+ sendBinary(message, true);
+ }
+ }
+
+ @WebSocket
+ public static class AnnotatedByteArrayWholeListener extends AbstractAnnotatedListener
+ {
+ @OnWebSocketMessage
+ public void onMessage(byte[] message)
+ {
+ sendBinary(BufferUtil.toBuffer(message), true);
+ }
+ }
+
+ @WebSocket
+ public static class AnnotatedOffsetByteArrayWholeListener extends AbstractAnnotatedListener
+ {
+ @OnWebSocketMessage
+ public void onMessage(byte[] message, int offset, int length)
+ {
+ sendBinary(BufferUtil.toBuffer(message, offset, length), true);
+ }
+ }
+
+ @WebSocket
+ public static class AnnotatedInputStreamWholeListener extends AbstractAnnotatedListener
+ {
+ @OnWebSocketMessage
+ public void onMessage(InputStream stream)
+ {
+ sendBinary(readBytes(stream), true);
+ }
+ }
+
+ @WebSocket
+ public static class AnnotatedReverseArgumentPartialListener extends AbstractAnnotatedListener
+ {
+ @OnWebSocketMessage
+ public void onMessage(Session session, ByteBuffer message)
+ {
+ sendBinary(message, true);
+ }
+ }
+
+ public static ByteBuffer readBytes(InputStream stream)
+ {
+ try
+ {
+ return BufferUtil.toBuffer(IO.readBytes(stream));
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/TextListeners.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/TextListeners.java
new file mode 100644
index 00000000000..e362c28aea0
--- /dev/null
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/TextListeners.java
@@ -0,0 +1,105 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.tests.listeners;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.stream.Stream;
+
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.WebSocketListener;
+import org.eclipse.jetty.websocket.api.WebSocketPartialListener;
+import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
+import org.eclipse.jetty.websocket.api.annotations.WebSocket;
+import org.junit.jupiter.params.provider.Arguments;
+
+public class TextListeners
+{
+ public static Stream getTextListeners()
+ {
+ return Stream.of(
+ StringWholeListener.class,
+ StringPartialListener.class,
+ AnnotatedStringWholeListener.class,
+ AnnotatedReaderWholeListener.class,
+ AnnotatedReverseArgumentPartialListener.class
+ ).map(Arguments::of);
+ }
+
+ public static class StringWholeListener extends AbstractListener implements WebSocketListener
+ {
+ @Override
+ public void onWebSocketText(String message)
+ {
+ sendText(message, true);
+ }
+ }
+
+ public static class StringPartialListener extends AbstractListener implements WebSocketPartialListener
+ {
+ @Override
+ public void onWebSocketPartialText(String message, boolean fin)
+ {
+ sendText(message, fin);
+ }
+ }
+
+ @WebSocket
+ public static class AnnotatedStringWholeListener extends AbstractAnnotatedListener
+ {
+ @OnWebSocketMessage
+ public void onMessage(String message)
+ {
+ sendText(message, true);
+ }
+ }
+
+ @WebSocket
+ public static class AnnotatedReaderWholeListener extends AbstractAnnotatedListener
+ {
+ @OnWebSocketMessage
+ public void onMessage(Reader reader)
+ {
+ sendText(readString(reader), true);
+ }
+ }
+
+ @WebSocket
+ public static class AnnotatedReverseArgumentPartialListener extends AbstractAnnotatedListener
+ {
+ @OnWebSocketMessage
+ public void onMessage(Session session, String message)
+ {
+ sendText(message, true);
+ }
+ }
+
+ public static String readString(Reader reader)
+ {
+ try
+ {
+ return IO.toString(reader);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/WebSocketListenerTest.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/WebSocketListenerTest.java
new file mode 100644
index 00000000000..fe290ccba82
--- /dev/null
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/listeners/WebSocketListenerTest.java
@@ -0,0 +1,146 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under
+// the terms of the Eclipse Public License 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0
+//
+// This Source Code may also be made available under the following
+// Secondary Licenses when the conditions for such availability set
+// forth in the Eclipse Public License, v. 2.0 are satisfied:
+// the Apache License v2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.tests.listeners;
+
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.websocket.api.StatusCode;
+import org.eclipse.jetty.websocket.client.WebSocketClient;
+import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
+import org.eclipse.jetty.websocket.tests.EventSocket;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class WebSocketListenerTest
+{
+ private Server server;
+ private URI serverUri;
+ private WebSocketClient client;
+
+ @BeforeEach
+ public void before() throws Exception
+ {
+ server = new Server();
+ ServerConnector connector = new ServerConnector(server);
+ server.addConnector(connector);
+
+ ServletContextHandler contextHandler = new ServletContextHandler();
+ contextHandler.setContextPath("/");
+ JettyWebSocketServletContainerInitializer.configure(contextHandler, (context, container) ->
+ {
+ for (Class> c : getClassListFromArguments(TextListeners.getTextListeners()))
+ {
+ container.addMapping("/text/" + c.getSimpleName(), (req, res) -> construct(c));
+ }
+
+ for (Class> c : getClassListFromArguments(BinaryListeners.getBinaryListeners()))
+ {
+ container.addMapping("/binary/" + c.getSimpleName(), (req, res) -> construct(c));
+ }
+ });
+
+ server.setHandler(contextHandler);
+ server.start();
+ serverUri = URI.create("ws://localhost:" + connector.getLocalPort() + "/");
+ client = new WebSocketClient();
+ client.start();
+ }
+
+ @AfterEach
+ public void after() throws Exception
+ {
+ client.stop();
+ server.stop();
+ }
+
+ @ParameterizedTest
+ @MethodSource("org.eclipse.jetty.websocket.tests.listeners.TextListeners#getTextListeners")
+ public void testTextListeners(Class> clazz) throws Exception
+ {
+ EventSocket clientEndpoint = new EventSocket();
+ client.connect(clientEndpoint, serverUri.resolve("/text/" + clazz.getSimpleName())).get(5, TimeUnit.SECONDS);
+
+ // Send and receive echo on client.
+ String payload = "hello world";
+ clientEndpoint.session.getRemote().sendString(payload);
+ String echoMessage = clientEndpoint.textMessages.poll(5, TimeUnit.SECONDS);
+ assertThat(echoMessage, is(payload));
+
+ // Close normally.
+ clientEndpoint.session.close(StatusCode.NORMAL, "standard close");
+ assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
+ assertThat(clientEndpoint.closeCode, is(StatusCode.NORMAL));
+ assertThat(clientEndpoint.closeReason, is("standard close"));
+ }
+
+ @ParameterizedTest
+ @MethodSource("org.eclipse.jetty.websocket.tests.listeners.BinaryListeners#getBinaryListeners")
+ public void testBinaryListeners(Class> clazz) throws Exception
+ {
+ EventSocket clientEndpoint = new EventSocket();
+ client.connect(clientEndpoint, serverUri.resolve("/binary/" + clazz.getSimpleName())).get(5, TimeUnit.SECONDS);
+
+ // Send and receive echo on client.
+ ByteBuffer payload = BufferUtil.toBuffer("hello world");
+ clientEndpoint.session.getRemote().sendBytes(payload);
+ ByteBuffer echoMessage = clientEndpoint.binaryMessages.poll(5, TimeUnit.SECONDS);
+ assertThat(echoMessage, is(payload));
+
+ // Close normally.
+ clientEndpoint.session.close(StatusCode.NORMAL, "standard close");
+ assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
+ assertThat(clientEndpoint.closeCode, is(StatusCode.NORMAL));
+ assertThat(clientEndpoint.closeReason, is("standard close"));
+ }
+
+ private List> getClassListFromArguments(Stream stream)
+ {
+ return stream.map(arguments -> (Class>)arguments.get()[0]).collect(Collectors.toList());
+ }
+
+ private T construct(Class clazz)
+ {
+ try
+ {
+ @SuppressWarnings("unchecked")
+ T instance = (T)clazz.getConstructors()[0].newInstance();
+ return instance;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ServerConfigTest.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ServerConfigTest.java
index 510774ed602..739877c084e 100644
--- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ServerConfigTest.java
+++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ServerConfigTest.java
@@ -230,7 +230,7 @@ public class ServerConfigTest
assertNull(serverEndpoint.error);
assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
- assertThat(clientEndpoint.statusCode, is(StatusCode.NO_CODE));
+ assertThat(clientEndpoint.closeCode, is(StatusCode.NO_CODE));
listener.assertClosed();
}
@@ -251,7 +251,7 @@ public class ServerConfigTest
assertThat(serverEndpoint.error, instanceOf(MessageTooLargeException.class));
assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
- assertThat(clientEndpoint.statusCode, is(StatusCode.MESSAGE_TOO_LARGE));
+ assertThat(clientEndpoint.closeCode, is(StatusCode.MESSAGE_TOO_LARGE));
listener.assertClosed();
}
@@ -267,7 +267,7 @@ public class ServerConfigTest
connect.get(5, TimeUnit.SECONDS);
clientEndpoint.session.getRemote().sendString("hello world");
- String msg = serverEndpoint.messageQueue.poll(500, TimeUnit.MILLISECONDS);
+ String msg = serverEndpoint.textMessages.poll(500, TimeUnit.MILLISECONDS);
assertThat(msg, is("hello world"));
Thread.sleep(idleTimeout + 500);
@@ -275,7 +275,7 @@ public class ServerConfigTest
assertThat(serverEndpoint.error, instanceOf(WebSocketTimeoutException.class));
assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
- assertThat(clientEndpoint.statusCode, is(StatusCode.SHUTDOWN));
+ assertThat(clientEndpoint.closeCode, is(StatusCode.SHUTDOWN));
listener.assertClosed();
}
@@ -296,7 +296,7 @@ public class ServerConfigTest
assertThat(serverEndpoint.error, instanceOf(MessageTooLargeException.class));
assertTrue(clientEndpoint.closeLatch.await(5, TimeUnit.SECONDS));
- assertThat(clientEndpoint.statusCode, is(StatusCode.MESSAGE_TOO_LARGE));
+ assertThat(clientEndpoint.closeCode, is(StatusCode.MESSAGE_TOO_LARGE));
listener.assertClosed();
}
diff --git a/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/messages/ByteArrayMessageSink.java b/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/messages/ByteArrayMessageSink.java
index 1921e36b704..4b84a54156d 100644
--- a/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/messages/ByteArrayMessageSink.java
+++ b/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/messages/ByteArrayMessageSink.java
@@ -42,7 +42,8 @@ public class ByteArrayMessageSink extends AbstractMessageSink
{
super(session, methodHandle);
- // byte[] buf
+ // This uses the offset length byte array signature not supported by javax websocket.
+ // The javax layer instead uses decoders for whole byte array messages instead of this message sink.
MethodType onMessageType = MethodType.methodType(Void.TYPE, byte[].class, int.class, int.class);
if (methodHandle.type().changeReturnType(void.class) != onMessageType.changeReturnType(void.class))
{
diff --git a/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/messages/PartialByteArrayMessageSink.java b/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/messages/PartialByteArrayMessageSink.java
index a42c3577837..bf7058cbe0c 100644
--- a/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/messages/PartialByteArrayMessageSink.java
+++ b/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/messages/PartialByteArrayMessageSink.java
@@ -19,13 +19,11 @@
package org.eclipse.jetty.websocket.util.messages;
import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodType;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.websocket.core.CoreSession;
import org.eclipse.jetty.websocket.core.Frame;
-import org.eclipse.jetty.websocket.util.InvalidSignatureException;
public class PartialByteArrayMessageSink extends AbstractMessageSink
{
@@ -34,13 +32,6 @@ public class PartialByteArrayMessageSink extends AbstractMessageSink
public PartialByteArrayMessageSink(CoreSession session, MethodHandle methodHandle)
{
super(session, methodHandle);
-
- // byte[] buf, int offset, int length
- MethodType onMessageType = MethodType.methodType(Void.TYPE, byte[].class, int.class, int.class, boolean.class);
- if (methodHandle.type() != onMessageType)
- {
- throw InvalidSignatureException.build(onMessageType, methodHandle.type());
- }
}
@Override
@@ -51,7 +42,7 @@ public class PartialByteArrayMessageSink extends AbstractMessageSink
if (frame.hasPayload() || frame.isFin())
{
byte[] buffer = frame.hasPayload() ? BufferUtil.toArray(frame.getPayload()) : EMPTY_BUFFER;
- methodHandle.invoke(buffer, 0, buffer.length, frame.isFin());
+ methodHandle.invoke(buffer, frame.isFin());
}
callback.succeeded();
diff --git a/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/messages/PartialByteBufferMessageSink.java b/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/messages/PartialByteBufferMessageSink.java
index ce00ee7acef..9336e93aa52 100644
--- a/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/messages/PartialByteBufferMessageSink.java
+++ b/jetty-websocket/websocket-util/src/main/java/org/eclipse/jetty/websocket/util/messages/PartialByteBufferMessageSink.java
@@ -29,14 +29,6 @@ public class PartialByteBufferMessageSink extends AbstractMessageSink
public PartialByteBufferMessageSink(CoreSession session, MethodHandle methodHandle)
{
super(session, methodHandle);
-
- /* TODO: Review
- MethodType onMessageType = MethodType.methodType(Void.TYPE, ByteBuffer.class, boolean.class);
- if (methodHandle.type() != onMessageType)
- {
- throw InvalidSignatureException.build(onMessageType, methodHandle.type());
- }
- */
}
@Override
From 2addb6a655ac40ac120be0e5cb33c59a91052024 Mon Sep 17 00:00:00 2001
From: Greg Wilkins
Date: Wed, 15 Apr 2020 18:42:28 +0200
Subject: [PATCH 086/101] Jetty 10.0.x use HandlerList instead of
HandlerCollection (#4757)
* Use HandlerList instead of HandlerCollection
Signed-off-by: Greg Wilkins
* Use HandlerList instead of HandlerCollection
Signed-off-by: Greg Wilkins