File: http2.adoc

package info (click to toggle)
jetty12 12.0.33-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 87,784 kB
  • sloc: java: 735,235; xml: 58,955; javascript: 1,041; sh: 873; jsp: 391; sql: 40; makefile: 9
file content (287 lines) | stat: -rw-r--r-- 16,319 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

= HTTP/2 Client Library

In the vast majority of cases, client applications should use the generic, high-level, xref:client/http.adoc[HTTP client library] that also provides HTTP/2 support via the pluggable xref:client/http.adoc#transport-http2[HTTP/2 transport] or the xref:client/http.adoc#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 low-level HTTP/2 library.

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:server/http2.adoc[HTTP/2 server library].

[[intro]]
== Introducing HTTP2Client

The Maven artifact coordinates for the HTTP/2 client library are the following:

[,xml,subs=attributes+]
----
<dependency>
  <groupId>org.eclipse.jetty.http2</groupId>
  <artifactId>jetty-http2-client</artifactId>
  <version>{jetty-version}</version>
</dependency>
----

The main class is named `org.eclipse.jetty.http2.client.HTTP2Client`, and must be created, configured and started before use:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=start]
----

When your application stops, or otherwise does not need `HTTP2Client` anymore, it should stop the `HTTP2Client` instance (or instances) that were started:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=stop]
----

`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, or _session_.
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.

[[flow-control]]
== HTTP/2 Flow Control

The HTTP/2 protocol is _flow controlled_ (see 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 the implementation 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.
The sender may resume sending data bytes when it receives a message from the receiver that the data bytes sent previously have been consumed.
This message enlarges the sender 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`, nor on other streams, despite all the streams having 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`.

How a client application should handle HTTP/2 flow control is discussed in details in <<response,this section>>.

[[connect]]
== Connecting to the Server

The first thing an application should do is to connect to the server and obtain a `Session`.
The following example connects to the server on a clear-text port:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=clearTextConnect]
----

The following example connects to the server on an encrypted port:

[,java,indent=0]
----
include::code:example$src/main/java/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.

[[configure]]
== Configuring the Session

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.
Client applications can override this method to change the default configuration:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=configure]
----

The `Session.Listener` is notified of session events originated by the server such as receiving a `SETTINGS` frame from the server, or the server closing the connection, or the client timing out the connection due to idleness.
Please refer to the `Session.Listener` link:{javadoc-url}/org/eclipse/jetty/http2/api/Session.Listener.html[javadocs] for the complete list of events.

Once a `Session` has been established, the communication with the server happens by exchanging _frames_, as specified in the https://tools.ietf.org/html/rfc7540#section-4[HTTP/2 specification].

[[request]]
== Sending a Request

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 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.
Sending the `HEADERS` frame opens the `Stream`:

[,java,indent=0,subs=attributes+]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=newStream]
----

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 <<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.

HTTP requests may have content, which is sent using the `Stream` APIs:

[,java,indent=0]
----
include::code:example$src/main/java/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.
Use the `Callback` APIs or `CompletableFuture` APIs to ensure that the second `Stream.data(\...)` call is performed when the first completed successfully.

[[response]]
== Receiving a Response

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.

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 client application can therefore receive the HTTP/2 frames sent by the server by implementing the relevant methods in `Stream.Listener`:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=responseListener]
----

[NOTE]
====
When `onDataAvailable(Stream stream)` is invoked, the demand is implicitly cancelled.

Just returning from the `onDataAvailable(Stream stream)` method does _not_ implicitly demand for more `DATA` frames.

Applications must call `Stream.demand()` to explicitly require that `onDataAvailable(Stream stream)` is invoked again when more `DATA` frames are available.
====

Applications that consume the content buffer within `onDataAvailable(Stream stream)` (for example, writing it to a file, or copying the bytes to another storage) should call `Data.release()` 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, an application may store away the `Data` object to consume the buffer bytes later, or pass the `Data` object to another asynchronous API (this is typical in proxy applications).

[IMPORTANT]
====
The call to `Stream.readData()` 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 unwrap the `Data` object into some other object that may be used later, provided that the _release_ semantic is maintained:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/HTTP2Docs.java[tags=dataUnwrap]
----

[IMPORTANT]
====
Applications that implement `onDataAvailable(Stream stream)` must remember to call `Stream.demand()` eventually.

If they do not call `Stream.demand()`, the implementation will not invoke `onDataAvailable(Stream stream)` to deliver more `DATA` frames and the application will stall threadlessly until an idle timeout fires to close the stream or the session.
====

[[reset]]
== Resetting a Request or Response

In HTTP/2, clients and servers have the ability to tell to the other peer that they are not interested anymore in either the request or the response, using a `RST_STREAM` frame.

The `HTTP2Client` APIs allow client applications to send and receive this "reset" frame:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=reset]
----

[[push]]
== Receiving HTTP/2 Pushes

HTTP/2 servers have the ability to push resources related to a primary 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 <<configure,this section>>.

Client applications can listen to the push events, and act accordingly:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=push]
----

If a client application does not want to handle a particular HTTP/2 push, it can just reset the pushed stream to tell the server to stop sending bytes for the pushed stream:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=pushReset]
----

[[goaway]]
== Session Closing

Once a `Session` has been established (see <<connect,here>>), it may be closed in the following way, providing an error code and a short textual reason:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=close]
----

Closing a `Session` will send a `GOAWAY` frame to the server.

When the server closes the `Session`, the `GOAWAY` frame received by the client carries the identifier of the last stream that was processed by the server.
Streams that have an identifier higher than the one specified in the `GOAWAY` frame are not processed by the server.

A client application may be sending requests while the server is closing the `Session`, so it is possible that the client opens a stream that will not be processed by the server, because it has an identifier that is greater than the one specified in the `GOAWAY` frame sent by the server.

In this case the stream is failed on the client with a `RetryableStreamException`, and client applications may decide whether to retry the request (if the request can be retried):

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=retryStream]
----

[[listeners]]
== Listening to HTTP/2 Events

The HTTP/2 low-level APIs allow you to listen and react to semantic events such as HTTP requests and HTTP responses.

You can use `HTTP2Session.LifeCycleListener` to be notified of lifecycle events of a `Session` (when it is opened and when it is closed), and `HTTP2Session.FrameListener` to be notified of what HTTP/2 frames have been sent and received.

These listeners are useful to implement cross-cutting concerns that are not related to the handling of HTTP requests and responses.

For example, you can use a `HTTP2Session.LifeCycleListener` to record and log how long sessions remain open:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=listenerLifeCycle]
----

Another example is to use a `HTTP2Session.FrameListener` to log HTTP/2 frames for HTTP/2 protocol troubleshooting:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/http2/HTTP2ClientDocs.java[tags=listenerLogging]
----

In limited cases, you can use these listeners to access the low-level HTTP/2 APIs even if your application uses the xref:client/http.adoc[high-level APIs] with the xref:client/http.adoc#transport-http2[HTTP/2 transport].