-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rework client content listeners #8981
Rework client content listeners #8981
Conversation
3120423
to
646afcf
Compare
646afcf
to
b8a25b3
Compare
jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/api/Response.java
Outdated
Show resolved
Hide resolved
...re/jetty-client/src/main/java/org/eclipse/jetty/client/util/InputStreamResponseListener.java
Outdated
Show resolved
Hide resolved
...ion/jetty-documentation/src/main/asciidoc/programming-guide/client/http/client-http-api.adoc
Outdated
Show resolved
Hide resolved
...ion/jetty-documentation/src/main/asciidoc/programming-guide/client/http/client-http-api.adoc
Outdated
Show resolved
Hide resolved
...ion/jetty-documentation/src/main/asciidoc/programming-guide/client/http/client-http-api.adoc
Outdated
Show resolved
Hide resolved
...ransports/src/test/java/org/eclipse/jetty/ee10/test/client/transport/ServerTimeoutsTest.java
Show resolved
Hide resolved
jetty-ee9/jetty-ee9-proxy/src/main/java/org/eclipse/jetty/ee9/proxy/AsyncMiddleManServlet.java
Show resolved
Hide resolved
jetty-ee9/jetty-ee9-proxy/src/main/java/org/eclipse/jetty/ee9/proxy/ProxyServlet.java
Outdated
Show resolved
Hide resolved
...transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/ServerTimeoutsTest.java
Show resolved
Hide resolved
...transports/src/test/java/org/eclipse/jetty/ee9/test/client/transport/ServerTimeoutsTest.java
Outdated
Show resolved
Hide resolved
8e766e3
to
19bf6ee
Compare
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). | ||
You must provide a `org.eclipse.jetty.io.Content.Source` read/demand implementation that reads a `Content.Chunk` from the provided `Content.Source`; if the read chunk is `null`, `Content.Source.demand(Runnable)` should be called so that the demand callback is called back when more chunks are available; if the chunk is an instance of `Content.Chunk.Error` then the error should be processed; otherwise the chunk should be processed by consuming and then releasing the chunk's `ByteBuffer`; and finally either trying to read another chunk, or demanding for another chunk (unless the current chunk is the last). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You must provide a `org.eclipse.jetty.io.Content.Source` read/demand implementation that reads a `Content.Chunk` from the provided `Content.Source`; if the read chunk is `null`, `Content.Source.demand(Runnable)` should be called so that the demand callback is called back when more chunks are available; if the chunk is an instance of `Content.Chunk.Error` then the error should be processed; otherwise the chunk should be processed by consuming and then releasing the chunk's `ByteBuffer`; and finally either trying to read another chunk, or demanding for another chunk (unless the current chunk is the last). | |
You must provide a `ContentSourceListener` whose implementation reads a `Content.Chunk` from the provided `Content.Source`; if the read chunk is `null`, `Content.Source.demand(Runnable)` should be called so that the demand callback is called back when more chunks are available; if the chunk is an instance of `Content.Chunk.Error` then the error should be processed; otherwise the chunk should be processed by consuming and then releasing the chunk; and finally either trying to read another chunk, or demanding for another chunk (unless the current chunk is the last). |
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. | ||
IMPORTANT: Calling `Content.Chunk.release()` must be done only after the bytes in the `ByteBuffer` returned by `Content.Chunk.getByteBuffer()` have been consumed. | ||
When the `Content.Chunk` is released, the `HttpClient` implementation may reuse the `buffer` and overwrite the bytes with different bytes; if the application looks at the `buffer` _after_ having released the `Content.Chunk` is may see other, unrelated, bytes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the `Content.Chunk` is released, the `HttpClient` implementation may reuse the `buffer` and overwrite the bytes with different bytes; if the application looks at the `buffer` _after_ having released the `Content.Chunk` is may see other, unrelated, bytes. | |
When the `Content.Chunk` is released, the `HttpClient` implementation may reuse the `ByteBuffer` and overwrite the bytes with different bytes; if the application looks at the `ByteBuffer` _after_ having released the `Content.Chunk` is may see other, unrelated, bytes. |
|
||
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. | ||
An application can read, store aside the `Content.Chunk` objects without releasing them (to consume them later), and demand for more chunks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An application can read, store aside the `Content.Chunk` objects without releasing them (to consume them later), and demand for more chunks. | |
An application can read, store aside the `Content.Chunk` objects without releasing them (to consume them later), and demand for more chunks, but it must call `Chunk.retain()` on the stored chunks, and arrange to release them after they have been consumed later. |
|
||
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. | ||
An application can also read one chunk of content, consume it (by releasing it) and then _not_ demand for more content until a later time. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An application can also read one chunk of content, consume it (by releasing it) and then _not_ demand for more content until a later time. | |
An application can also read one chunk of content, consume it, release it, 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.AsyncContentListener` overrides the behavior of `Response.ContentSourceListener`; when an application implements `onContent(response, chunk, demander)`, it can control the disposing/recycling the `buffer` by releasing the `chunk` _and_ it can control when to demand one more chunk of content by calling `demander.run()`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Subclass `Response.AsyncContentListener` overrides the behavior of `Response.ContentSourceListener`; when an application implements `onContent(response, chunk, demander)`, it can control the disposing/recycling the `buffer` by releasing the `chunk` _and_ it can control when to demand one more chunk of content by calling `demander.run()`. | |
Subclass `Response.AsyncContentListener` overrides the behavior of `Response.ContentSourceListener`; when an application implements `AsyncContentListener.onContent(response, chunk, demander)`, it can control the disposing/recycling of the `ByteBuffer` by releasing the chunk _and_ it can control when to demand one more chunk by calling `demander.run()`. |
// When response content is received from server1, forward it to server2. | ||
content2.write(content, Callback.from(() -> | ||
content2.write(chunk.getByteBuffer(), Callback.from(() -> | ||
{ | ||
// When the request content to server2 is sent, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// When the request content to server2 is sent, | |
// When the request chunk is successfully sent to server2, |
@@ -73,13 +72,13 @@ | |||
public class InputStreamResponseListener extends Listener.Adapter | |||
{ | |||
private static final Logger LOG = LoggerFactory.getLogger(InputStreamResponseListener.class); | |||
private static final Chunk EOF = new Chunk(BufferUtil.EMPTY_BUFFER, Callback.NOOP); | |||
private static final ChunkCallback EOF = new ChunkCallback(Content.Chunk.EOF, () -> {}, (x) -> {}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the parens around x
.
{ | ||
if (LOG.isDebugEnabled()) | ||
LOG.debug("Skipped empty content {}", content); | ||
callback.succeeded(); | ||
LOG.debug("Skipped empty content {}", chunk); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LOG.debug("Skipped empty content {}", chunk); | |
LOG.debug("Skipped empty chunk {}", chunk); |
@@ -117,17 +117,18 @@ public void onContent(Response response, ByteBuffer content, Callback callback) | |||
if (!closed) | |||
{ | |||
if (LOG.isDebugEnabled()) | |||
LOG.debug("Queueing content {}", content); | |||
chunks.add(new Chunk(content, callback)); | |||
LOG.debug("Queueing content {}", chunk); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LOG.debug("Queueing content {}", chunk); | |
LOG.debug("Queueing chunk {}", chunk); |
l.signalAll(); | ||
} | ||
} | ||
|
||
if (closed) | ||
{ | ||
if (LOG.isDebugEnabled()) | ||
LOG.debug("InputStream closed, ignored content {}", content); | ||
callback.failed(new AsynchronousCloseException()); | ||
LOG.debug("InputStream closed, ignored content {}", chunk); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LOG.debug("InputStream closed, ignored content {}", chunk); | |
LOG.debug("InputStream closed, ignored chunk {}", chunk); |
@lorban approved, but please fix the 12 documentation/nits. Thanks! |
Signed-off-by: Ludovic Orban <[email protected]>
Signed-off-by: Ludovic Orban <[email protected]>
Signed-off-by: Ludovic Orban <[email protected]>
Signed-off-by: Simone Bordet <[email protected]>
Signed-off-by: Simone Bordet <[email protected]>
902a362
to
2cdf785
Compare
Proposal: get rid of
DemandedContentListener
and changeAsyncContentListener
API to adopt theSimpleContentListener
one.This depends on #8993