Updated the programming guide client documentation.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2023-07-16 15:40:58 +02:00
parent af90b35ee7
commit 0eb143634c
No known key found for this signature in database
GPG Key ID: 1677D141BCF3584D
10 changed files with 151 additions and 52 deletions

View File

@ -133,7 +133,7 @@ Real network protocols are of course more complicated and so is the implementati
The Jetty client implementation provides a number of `ClientConnectionFactory` implementations that can be composed to produce and interpret the network bytes. 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. 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 secure HTTP protocol.
The differences between the clear-text version and the TLS encrypted version are minimal: The differences between the clear-text version and the TLS encrypted version are minimal:

View File

@ -14,7 +14,7 @@
[[pg-client]] [[pg-client]]
== Client Libraries == Client Libraries
The Eclipse Jetty Project provides client-side libraries that allow you to embed a client in your applications. The Eclipse Jetty Project provides client-side libraries that allow you to embed an HTTP or WebSocket client in your applications.
A typical example is a client application that needs to contact a third party service via HTTP (for example a REST service). A typical example is a client 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 or HTTP/3. 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 or HTTP/3.
Yet another example is a client application that needs to receive events from a WebSocket server. Yet another example is a client application that needs to receive events from a WebSocket server.
@ -23,12 +23,12 @@ The client libraries are designed to be non-blocking and offer both synchronous
These are the available client libraries: These are the available client libraries:
* xref:pg-client-http[The High-Level HTTP Client Library] for HTTP/1.1, HTTP/2 and FastCGI * xref:pg-client-http[The High-Level HTTP Client Library] for HTTP/1.1, HTTP/2, HTTP/3 and FastCGI
* xref:pg-client-http2[The Low-Level HTTP/2 Client Library] for low-level HTTP/2 * xref:pg-client-http2[The Low-Level HTTP/2 Client Library] for low-level HTTP/2
* xref:pg-client-http3[The Low-Level HTTP/3 Client Library] for low-level HTTP/3 * xref:pg-client-http3[The Low-Level HTTP/3 Client Library] for low-level HTTP/3
* xref:pg-client-websocket[The WebSocket client library] * xref:pg-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 xref:pg-client-io-arch[Client I/O Architecture]. If you are interested in the low-level details of how the Jetty client libraries work, or are interested in writing a custom protocol, look at the xref:pg-client-io-arch[Client I/O Architecture].
include::client-io-arch.adoc[] include::client-io-arch.adoc[]
include::http/client-http.adoc[] include::http/client-http.adoc[]

View File

@ -26,7 +26,7 @@ The simpler way to perform a HTTP request is the following:
include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java[tags=simpleBlockingGet] include::../../{doc_code}/org/eclipse/jetty/docs/programming/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. 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 `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:pg-client-http-content-response[the section on response content handling]. The content length is limited by default to 2 MiB; for larger content see xref:pg-client-http-content-response[the section on response content handling].
@ -45,7 +45,7 @@ This is a shorthand for:
include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java[tags=headNonFluent] include::../../{doc_code}/org/eclipse/jetty/docs/programming/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). 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).
When the request object is customized, you call `request.send()` that produces the `ContentResponse` when the request/response conversation is complete. When the request object is customized, you call `request.send()` that produces the `ContentResponse` when the request/response conversation is complete.
Simple `POST` requests also have a shortcut method: Simple `POST` requests also have a shortcut method:
@ -67,7 +67,7 @@ File uploads also require one line, and make use of `java.nio.file` classes:
include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java[tags=fileFluent] include::../../{doc_code}/org/eclipse/jetty/docs/programming/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: It is possible to impose a total timeout for the request/response conversation using the `Request.timeout(\...)` method as follows:
[source,java,indent=0] [source,java,indent=0]
---- ----
@ -140,7 +140,7 @@ Have a look at the link:{javadoc-url}/org/eclipse/jetty/client/api/Request.Liste
Jetty's `HttpClient` 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`. You can provide request content as `String`, `byte[]`, `ByteBuffer`, `java.nio.file.Path`, `InputStream`, and provide your own implementation of `org.eclipse.jetty.client.Request.Content`.
Heres an example that provides the request content using `java.nio.file.Paths`: Heres an example that provides the request content using `java.nio.file.Paths`:
[source,java,indent=0] [source,java,indent=0]
@ -189,14 +189,14 @@ Jetty's `HttpClient` allows applications to handle response content in different
You can buffer the response content in memory; this is done when using the xref:pg-client-http-blocking[blocking APIs] 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:pg-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: 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.FutureResponseListener` in this way:
[source,java,indent=0] [source,java,indent=0]
---- ----
include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java[tags=futureResponseListener] include::../../{doc_code}/org/eclipse/jetty/docs/programming/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(\...)`.
You can buffer the response content in memory also using the xref:pg-client-http-non-blocking[non-blocking APIs], via the `BufferingResponseListener` utility class: You can buffer the response content in memory also using the xref:pg-client-http-non-blocking[non-blocking APIs], via the `BufferingResponseListener` utility class:
@ -221,22 +221,37 @@ The listener that follows this model is `Response.ContentSourceListener`.
After the response headers have been processed by the `HttpClient` implementation, `Response.ContentSourceListener.onContentSource(response, contentSource)` is invoked once and only once. After the response headers have been processed by the `HttpClient` implementation, `Response.ContentSourceListener.onContentSource(response, contentSource)` is invoked once and only once.
This allows the application to control precisely the read/demand loop: when to read a chunk, how to process it and when to demand the next one. This allows the application to control precisely the read/demand loop: when to read a chunk, how to process it and when to demand the next one.
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). You must provide a `ContentSourceListener` whose implementation reads a `Content.Chunk` from the provided `Content.Source`, as follows:
Then the following cases may happen:
* The read chunk ; you should process it by consuming its bytes and then releasing the chunk, then either trying to read another chunk, or demanding for another chunk (unless the current chunk is the last).
[source,java,indent=0]
----
include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java[tags=contentSourceListener]
----
<1> Using a `Runnable` anonymous class instead of a lambda in this example just for simplicity, and to be able to call `contentSource.demand(this)` passing `this` as parameter.
<2> Read in a loop
<3> Read a `Content.Chunk` from the `Content.Source`.
<4> The read chunk is `null`, call `Content.Source.demand(Runnable)` to be called back when more chunks are available.
<5> The read reported a failure, which may be transient or terminal; the application must handle them appropriately.
<6> The read chunk is a normal chunk of content; the application should consume its `ByteBuffer` bytes.
<7> The chunk must be released.
IMPORTANT: Calling `Content.Chunk.release()` must be done only after the bytes in the `ByteBuffer` returned by `Content.Chunk.getByteBuffer()` have been consumed. 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 `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. 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.
// TODO: add example code snippet
The invocation of `onContentSource(Request, Content.Source)` and of the demand callback passed to `contentSource.demand(Runnable)` are serialized with respect to asynchronous events such as timeouts or an asynchronous call to `Request.abort(Throwable)`. The invocation of `onContentSource(Request, Content.Source)` and of the demand callback passed to `contentSource.demand(Runnable)` are serialized with respect to asynchronous events such as timeouts or an asynchronous call to `Request.abort(Throwable)`.
This means that these asynchronous events are not processed until the invocation of `onContentSource(Request, Content.Source)` returns, or until the invocation of the demand callback returns. This means that these asynchronous events are not processed until the invocation of `onContentSource(Request, Content.Source)` returns, or until the invocation of the demand callback returns.
With this model, applications should not worry too much about concurrent asynchronous events happening during response content handling, because they will eventually see the events as failures while reading the response content.
Demanding for content and consuming the content are orthogonal activities. Demanding for content and consuming the content are orthogonal activities.
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 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.
If not done carefully, this may lead to excessive memory consumption, since the ``buffer``s are not consumed. If not done carefully, this may lead to excessive memory consumption, since the `ByteBuffer` bytes are not consumed.
Releasing the `Content.Chunk`s will result in the ``buffer``s to be disposed/recycled and may be performed at any time. Releasing the `Content.Chunk`s will result in the ``ByteBuffer``s to be disposed/recycled and may be performed at any 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. An application can also read one chunk of content, consume it, release it, and then _not_ demand for more content until a later time.
@ -244,11 +259,9 @@ Subclass `Response.AsyncContentListener` overrides the behavior of `Response.Con
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. 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 `ByteBuffer` bytes as in the following example:
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] [source,java,indent=0]
---- ----
include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java[tag=contentSourceListener] include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java[tag=forwardContent]
---- ----

View File

@ -69,11 +69,11 @@ The example above will retain only cookies that come from the `google.com` domai
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 `;`. 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: 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,screen] [source,screen]
---- ----
Set-Cookie: foo="bar;baz";Version=1;Path="/secur" Set-Cookie: foo="bar;baz";Version=1;Path="/secure"
---- ----
This was added to the HTTP Response as follows: This was added to the HTTP Response as follows:
@ -82,7 +82,7 @@ This was added to the HTTP Response as follows:
---- ----
protected void service(HttpServletRequest request, HttpServletResponse response) protected void service(HttpServletRequest request, HttpServletResponse response)
{ {
javax.servlet.http.Cookie cookie = new Cookie("foo", "bar;baz"); jakarta.servlet.http.Cookie cookie = new Cookie("foo", "bar;baz");
cookie.setPath("/secure"); cookie.setPath("/secure");
response.addCookie(cookie); response.addCookie(cookie);
} }
@ -93,7 +93,7 @@ This can be done utilizing `jakarta.servlet.http.Cookie` as follows:
[source,java] [source,java]
---- ----
javax.servlet.http.Cookie cookie = new Cookie("foo", URLEncoder.encode("bar;baz", "UTF-8")); jakarta.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. Jetty validates all cookie names and values being added to the `HttpServletResponse` via the `addCookie(Cookie)` method.

View File

@ -38,7 +38,7 @@ 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. * 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. * Cookies support -- cookies sent by servers are stored and sent back to servers in matching requests.
* Authentication support -- HTTP "Basic", "Digest" and "SPNEGO" authentications are supported, others are pluggable. * Authentication support -- HTTP "Basic", "Digest" and "SPNEGO" authentications are supported, others are pluggable.
* Forward proxy support -- HTTP proxying and SOCKS4 proxying. * Forward proxy support -- HTTP proxying, SOCKS4 and SOCKS5 proxying.
[[pg-client-http-start]] [[pg-client-http-start]]
==== Starting HttpClient ==== Starting HttpClient
@ -58,7 +58,7 @@ The Maven artifact coordinates are the following:
The main class is named `org.eclipse.jetty.client.HttpClient`. The main class is named `org.eclipse.jetty.client.HttpClient`.
You can think of a `HttpClient` instance as a browser instance. 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. Like a browser it can make requests to different domains, it manages redirects, cookies and authentication, you can configure it with a forward 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: In order to use `HttpClient`, you must instantiate it, configure it, and then start it:
@ -87,7 +87,7 @@ It is recommended that when your application stops, you also stop the `HttpClien
include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java[tags=stop] include::../../{doc_code}/org/eclipse/jetty/docs/programming/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. Stopping `HttpClient` makes sure that the memory it holds (for example, `ByteBuffer` pools, authentication credentials, cookies, etc.) is released, and that the thread pool and scheduler are properly stopped allowing all threads used by `HttpClient` to exit.
[NOTE] [NOTE]
==== ====
@ -108,9 +108,9 @@ A `HttpClient` instance can be thought as a browser instance, and it manages the
* a `CookieStore` (see xref:pg-client-http-cookie[this section]). * a `CookieStore` (see xref:pg-client-http-cookie[this section]).
* a `AuthenticationStore` (see xref:pg-client-http-authentication[this section]). * a `AuthenticationStore` (see xref:pg-client-http-authentication[this section]).
* a `ProxyConfiguration` (see xref:pg-client-http-proxy[this section]). * a `ProxyConfiguration` (see xref:pg-client-http-proxy[this section]).
* a set of _destinations_. * a set of ``Destination``s
A _destination_ is the client-side component that represents an _origin_ server, and manages a queue of requests for that origin, and a xref:pg-client-http-connection-pool[pool of TCP connections] to that origin. A `Destination` is the client-side component that represents an _origin_ server, and manages a queue of requests for that origin, and a xref:pg-client-http-connection-pool[pool of TCP 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. 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. However, this is not enough.
@ -132,10 +132,10 @@ Therefore an origin is identified by the tuple `(scheme, host, port, tag, protoc
[[pg-client-http-connection-pool]] [[pg-client-http-connection-pool]]
==== HttpClient Connection Pooling ==== HttpClient Connection Pooling
A destination manages a `org.eclipse.jetty.client.ConnectionPool`, where connections to a particular origin are pooled for performance reasons: 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. 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`. 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 ``Destination``s for the same `host` and `port`, and therefore multiple ``ConnectionPool``s
You can access the `ConnectionPool` in this way: You can access the `ConnectionPool` in this way:
@ -149,6 +149,7 @@ Jetty's client library provides the following `ConnectionPool` implementations:
* `DuplexConnectionPool`, historically the first implementation, only used by the HTTP/1.1 transport. * `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). * `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. * `RoundRobinConnectionPool`, similar to `MultiplexConnectionPool` but where connections are reused with a round-robin algorithm.
* `RandomRobinConnectionPool`, similar to `MultiplexConnectionPool` but where connections are reused with an algorithm that chooses them randomly.
The `ConnectionPool` implementation can be customized for each destination in by setting a `ConnectionPool.Factory` on the `HttpClientTransport`: The `ConnectionPool` implementation can be customized for each destination in by setting a `ConnectionPool.Factory` on the `HttpClientTransport`:

View File

@ -81,13 +81,13 @@ The following example connects to the server on an encrypted port:
include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=encryptedConnect] include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=encryptedConnect]
---- ----
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. 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.
[[pg-client-http2-configure]] [[pg-client-http2-configure]]
==== Configuring the Session ==== Configuring the Session
The `connect(...)` method takes a `Session.Listener` parameter. The `connect(\...)` method takes a `Session.Listener` parameter.
This listener's `onPreface(...)` method is invoked just before establishing the connection to the server to gather the client configuration to send to the server. This listener's `onPreface(\...)` method is invoked just before establishing the connection to the server to gather the client configuration to send to the server.
Client applications can override this method to change the default configuration: Client applications can override this method to change the default configuration:
[source,java,indent=0] [source,java,indent=0]
@ -114,7 +114,7 @@ Sending the `HEADERS` frame opens the `Stream`:
include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=newStream] include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=newStream]
---- ----
Note how `Session.newStream(...)` takes a `Stream.Listener` parameter. 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 xref:pg-client-http2-response[section below]. 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 xref:pg-client-http2-response[section below].
Please refer to the `Stream.Listener` link:{javadoc-url}/org/eclipse/jetty/http2/api/Stream.Listener.html[javadocs] for the complete list of events. Please refer to the `Stream.Listener` link:{javadoc-url}/org/eclipse/jetty/http2/api/Stream.Listener.html[javadocs] for the complete list of events.
@ -125,13 +125,13 @@ HTTP requests may have content, which is sent using the `Stream` APIs:
include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=newStreamWithData] include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=newStreamWithData]
---- ----
IMPORTANT: When sending two `DATA` frames consecutively, the second call to `Stream.data(...)` must be done only when the first is completed, or a `WritePendingException` will be thrown. IMPORTANT: When sending two `DATA` frames consecutively, the second call to `Stream.data(\...)` must be done only when the first is completed, or a `WritePendingException` will be thrown.
Use the `Callback` APIs or `CompletableFuture` APIs to ensure that the second `Stream.data(...)` call is performed when the first completed successfully. Use the `Callback` APIs or `CompletableFuture` APIs to ensure that the second `Stream.data(\...)` call is performed when the first completed successfully.
[[pg-client-http2-response]] [[pg-client-http2-response]]
==== Receiving a Response ==== Receiving a Response
Response events are delivered to the `Stream.Listener` passed to `Session.newStream(...)`. Response events are delivered to the `Stream.Listener` passed to `Session.newStream(\...)`.
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. 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.

View File

@ -78,8 +78,8 @@ include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http3/HTTP3C
[[pg-client-http3-configure]] [[pg-client-http3-configure]]
==== Configuring the Session ==== Configuring the Session
The `connect(...)` method takes a `Session.Client.Listener` parameter. The `connect(\...)` method takes a `Session.Client.Listener` parameter.
This listener's `onPreface(...)` method is invoked just before establishing the connection to the server to gather the client configuration to send to the server. This listener's `onPreface(\...)` method is invoked just before establishing the connection to the server to gather the client configuration to send to the server.
Client applications can override this method to change the default configuration: Client applications can override this method to change the default configuration:
[source,java,indent=0] [source,java,indent=0]
@ -106,7 +106,7 @@ Sending the `HEADERS` frame opens the `Stream`:
include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java[tags=newStream] include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java[tags=newStream]
---- ----
Note how `Session.newRequest(...)` takes a `Stream.Client.Listener` parameter. Note how `Session.newRequest(\...)` takes a `Stream.Client.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 xref:pg-client-http3-response[section below]. 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 xref:pg-client-http3-response[section below].
Please refer to the `Stream.Client.Listener` link:{javadoc-url}/org/eclipse/jetty/http3/api/Stream.Client.Listener.html[javadocs] for the complete list of events. Please refer to the `Stream.Client.Listener` link:{javadoc-url}/org/eclipse/jetty/http3/api/Stream.Client.Listener.html[javadocs] for the complete list of events.
@ -117,13 +117,13 @@ HTTP requests may have content, which is sent using the `Stream` APIs:
include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java[tags=newStreamWithData] include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/http3/HTTP3ClientDocs.java[tags=newStreamWithData]
---- ----
IMPORTANT: When sending two `DATA` frames consecutively, the second call to `Stream.data(...)` must be done only when the first is completed, or a `WritePendingException` will be thrown. IMPORTANT: When sending two `DATA` frames consecutively, the second call to `Stream.data(\...)` must be done only when the first is completed, or a `WritePendingException` will be thrown.
Use the `CompletableFuture` APIs to ensure that the second `Stream.data(...)` call is performed when the first completed successfully. Use the `CompletableFuture` APIs to ensure that the second `Stream.data(\...)` call is performed when the first completed successfully.
[[pg-client-http3-response]] [[pg-client-http3-response]]
==== Receiving a Response ==== Receiving a Response
Response events are delivered to the `Stream.Client.Listener` passed to `Session.newRequest(...)`. Response events are delivered to the `Stream.Client.Listener` passed to `Session.newRequest(\...)`.
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. 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.

View File

@ -27,7 +27,7 @@ The Maven artifact coordinates are the following:
---- ----
<dependency> <dependency>
<groupId>org.eclipse.jetty.websocket</groupId> <groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-jetty-client</artifactId> <artifactId>jetty-websocket-jetty-client</artifactId>
<version>{version}</version> <version>{version}</version>
</dependency> </dependency>
---- ----
@ -35,7 +35,7 @@ The Maven artifact coordinates are the following:
[[pg-client-websocket-start]] [[pg-client-websocket-start]]
==== Starting WebSocketClient ==== Starting WebSocketClient
The main class is `org.eclipse.jetty.websocket.client.WebSocketClient`; you instantiate it, configure it, and then start it like may other Jetty components. The main class is `org.eclipse.jetty.websocket.client.WebSocketClient`; you instantiate it, configure it, and then start it like many other Jetty components.
This is a minimal example: This is a minimal example:
[source,java,indent=0] [source,java,indent=0]

View File

@ -13,12 +13,12 @@
== Eclipse Jetty Programming Guide == Eclipse Jetty Programming Guide
The Eclipse Jetty Programming Guide targets developers who want to use the Eclipse Jetty libraries in their applications. The Eclipse Jetty Programming Guide targets developers who want to use the Jetty libraries in their applications.
The Eclipse Jetty libraries provide the client-side and server-side APIs to work with various web protocols such as HTTP/1.1, HTTP/2, HTTP/3, WebSocket and FastCGI. The Jetty libraries provide the client-side and server-side APIs to work with various web protocols such as HTTP/1.1, HTTP/2, HTTP/3, WebSocket and FastCGI.
You may use the xref:pg-client[Eclipse Jetty client-side library] in your application to make calls to third party REST services, or to other REST microservices in your system. You may use the xref:pg-client[Jetty client-side library] in your application to make calls to third party REST services, or to other REST microservices in your system.
Likewise, you may use the xref:pg-server[Eclipse Jetty server-side library] to quickly create an HTTP or REST service without having to create a web application archive file (a `+*.war+` file) and without having to deploy it a Jetty standalone server that you would have to download and install. Likewise, you may use the xref:pg-server[Jetty server-side library] to quickly create an HTTP or REST service without having to create a web application archive file (a `+*.war+` file) and without having to deploy it to a Jetty standalone server that you would have to download and install.
This guide will walk you through the design of the Eclipse Jetty libraries and how to use its classes to write your applications. This guide will walk you through the design of the Jetty libraries and how to use its classes to write your applications.

View File

@ -72,6 +72,7 @@ import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.ssl.SslContextFactory;
import static java.lang.System.Logger.Level.ERROR;
import static java.lang.System.Logger.Level.INFO; import static java.lang.System.Logger.Level.INFO;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -453,11 +454,95 @@ public class HTTPClientDocs
HttpClient httpClient = new HttpClient(); HttpClient httpClient = new HttpClient();
httpClient.start(); httpClient.start();
// tag::contentSourceListener[]
httpClient.newRequest("http://domain.com/path")
.onResponseContentSource(((response, contentSource) ->
{
// The function (as a Runnable) that reads the response content.
Runnable demander = new Runnable() // <1>
{
@Override
public void run()
{
while (true) // <2>
{
Content.Chunk chunk = contentSource.read(); // <3>
// No chunk of content, demand again and return.
if (chunk == null)
{
contentSource.demand(this); // <4>
return;
}
// A failure happened.
if (Content.Chunk.isFailure(chunk)) // <5>
{
Throwable failure = chunk.getFailure();
if (chunk.isLast())
{
// A terminal failure, such as a network failure.
// Your logic to handle terminal failures here.
System.getLogger("failure").log(ERROR, "Unexpected terminal failure", failure);
return;
}
else
{
// A transient failure such as a read timeout.
// Your logic to handle transient failures here.
if (ignoreTransientFailure(response, failure))
{
// Try to read again.
continue;
}
else
{
// The transient failure is treated as a terminal failure.
System.getLogger("failure").log(ERROR, "Unexpected transient failure", failure);
return;
}
}
}
// A normal chunk of content.
consumeResponseContentChunk(response, chunk); // <6>
// Release the chunk.
chunk.release(); // <7>
// Loop around to read another response chunk.
}
}
};
// Initiate the reads.
demander.run();
}))
.send(result ->
{
});
// end::contentSourceListener[]
}
private boolean ignoreTransientFailure(Response response, Throwable failure)
{
return false;
}
private void consumeResponseContentChunk(Response response, Content.Chunk chunk)
{
}
public void forwardContent() throws Exception
{
HttpClient httpClient = new HttpClient();
httpClient.start();
String host1 = "localhost"; String host1 = "localhost";
String host2 = "localhost"; String host2 = "localhost";
int port1 = 8080; int port1 = 8080;
int port2 = 8080; int port2 = 8080;
// tag::contentSourceListener[] // tag::forwardContent[]
// Prepare a request to server1, the source. // Prepare a request to server1, the source.
Request request1 = httpClient.newRequest(host1, port1) Request request1 = httpClient.newRequest(host1, port1)
.path("/source"); .path("/source");
@ -533,7 +618,7 @@ public class HTTPClientDocs
// Send the request to server1. // Send the request to server1.
request1.send(result -> System.getLogger("forwarder").log(INFO, "Sourcing from server1 complete")); request1.send(result -> System.getLogger("forwarder").log(INFO, "Sourcing from server1 complete"));
// end::contentSourceListener[] // end::forwardContent[]
} }
public void getCookies() throws Exception public void getCookies() throws Exception