//
// ========================================================================
// 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
// ========================================================================
//

= WebSocket Client

Jetty's `WebSocketClient` is a more powerful alternative to the WebSocket client provided by the standard JSR 356 `javax.websocket` APIs.

Similarly to Jetty's xref:client/http.adoc[`HttpClient`], the `WebSocketClient` is non-blocking and asynchronous, making it very efficient in resource utilization.
A synchronous, blocking, API is also offered for simpler cases.

Since the first step of establishing a WebSocket communication is an HTTP request, `WebSocketClient` makes use of `HttpClient` and therefore depends on it.

The Maven artifact coordinates are the following:

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

[[start]]
== Starting WebSocketClient

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:

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

However, it is recommended that you explicitly pass an `HttpClient` instance to `WebSocketClient` so that you can have control over the HTTP configuration as well:

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

You may create multiple instances of `WebSocketClient`, but typically one instance is enough for most applications.
Creating multiple instances may be necessary for example when you need to specify different configuration parameters for different instances.
For example, you may need different instances when you need to configure the `HttpClient` differently: different transports, different proxies, different cookie stores, different authentications, etc.

The configuration that is not WebSocket specific (such as idle timeout, etc.) should be directly configured on the associated `HttpClient` instance.

The WebSocket specific configuration can be configured directly on the `WebSocketClient` instance.
Configuring the `WebSocketClient` allows to give default values to various parameters, whose values may be overridden more specifically, as described in <<session-configure,this section>>.

Refer to the  `WebSocketClient` link:{javadoc-url}/org/eclipse/jetty/websocket/client/WebSocketClient.html[javadocs] for the setter methods available to customize the WebSocket specific configuration.

[[stop]]
== Stopping WebSocketClient

It is recommended that when your application stops, you also stop the `WebSocketClient` instance (or instances) that you are using.

Similarly to xref:client/http.adoc#stop[stopping `HttpClient`], you want to stop `WebSocketClient` from a thread that is not owned by `WebSocketClient` itself, for example:

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

[[connect]]
== Connecting to a Remote Host

A WebSocket client may initiate the communication with the server either <<connect-http11,using HTTP/1.1>> or <<connect-http2,using HTTP/2>>.
The two mechanism are quite different and detailed in the following sections.

[[connect-http11]]
=== Using HTTP/1.1

Initiating a WebSocket communication with a server using HTTP/1.1 is detailed in https://tools.ietf.org/html/rfc6455#section-1.8[RFC 6455].

A WebSocket client first establishes a TCP connection to the server, then sends an HTTP/1.1 _upgrade_ request.

If the server supports upgrading to WebSocket, it responds with HTTP status code `101`, and then switches the communication over that connection, either incoming or outgoing, to happen using the WebSocket protocol.

When the client receives the HTTP status code `101`, it switches the communication over that connection, either incoming or outgoing, to happen using the WebSocket protocol.

[plantuml]
----
skinparam backgroundColor transparent
skinparam monochrome true
skinparam shadowing false

participant ClientEndPoint
participant WebSocketClient
participant HttpClient
participant Server
participant ServerEndPoint

WebSocketClient -> HttpClient : connect()
HttpClient -> Server : TCP/TLS connect
HttpClient -> Server : GET / HTTP/1.1\nUpgrade: websocket
Server -> ServerEndPoint ** : create
Server -> HttpClient : HTTP/1.1 101\nUpgrade: websocket
HttpClient -> WebSocketClient
WebSocketClient -> ClientEndPoint ** : create
ClientEndPoint -> WebSocketClient : WebSocket Frame A
WebSocketClient -> Server : WebSocket Frame A
Server -> ServerEndPoint : WebSocket Frame A
ServerEndPoint -> Server : WebSocket Frame B
Server -> WebSocketClient : WebSocket Frame B
WebSocketClient -> ClientEndPoint : WebSocket Frame B
----

In code:

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

`WebSocketClient.connect()` links the client-side WebSocket _endpoint_ to a specific server URI, and returns a `CompletableFuture` of an `org.eclipse.jetty.websocket.api.Session`.

The endpoint offers APIs to _receive_ WebSocket data (or errors) from the server, while the session offers APIs to _send_ WebSocket data to the server.

[[connect-http2]]
=== Using HTTP/2

Initiating a WebSocket communication with a server using HTTP/1.1 is detailed in https://tools.ietf.org/html/rfc8441[RFC 8441].

A WebSocket client establishes a TCP connection to the server or reuses an existing one currently used for HTTP/2, then sends an HTTP/2 _CONNECT_ request over an HTTP/2 stream.

If the server supports upgrading to WebSocket, it responds with HTTP status code `200`, then switches the communication over that stream, either incoming or outgoing, to happen using HTTP/2 `DATA` frames wrapping WebSocket frames.

When the client receives the HTTP status code `200`, it switches the communication over that stream, either incoming or outgoing, to happen using HTTP/2 `DATA` frames wrapping WebSocket frames.

From an external point of view, it will look like client is sending chunks of an infinite HTTP/2 request upload, and the server is sending chunks of an infinite HTTP/2 response download, as they will exchange HTTP/2 `DATA` frames; but the HTTP/2 `DATA` frames will contain each one or more WebSocket frames that both client and server know how to deliver to the respective WebSocket endpoints.

When either WebSocket endpoint decides to terminate the communication, the HTTP/2 stream will be closed as well.

[plantuml]
----
skinparam backgroundColor transparent
skinparam monochrome true
skinparam shadowing false

participant ClientEndPoint
participant WebSocketClient
participant HttpClient
participant Server
participant ServerEndPoint

WebSocketClient -> HttpClient : connect()
HttpClient --> Server : TCP/TLS connect
HttpClient -> Server : HEADERS\n:method: CONNECT\n:protocol: websocket
Server -> ServerEndPoint ** : create
Server -> HttpClient : HEADERS\n:status: 200\n: websocket
HttpClient -> WebSocketClient
WebSocketClient -> ClientEndPoint ** : create
ClientEndPoint -> HttpClient : WebSocket Frame A
HttpClient -> Server : DATA\nWebSocket Frame A
Server -> ServerEndPoint : WebSocket Frame A
ServerEndPoint -> Server : WebSocket Frame B
Server -> HttpClient : DATA\nWebSocket Frame B
HttpClient -> ClientEndPoint : WebSocket Frame B
----

In code:

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

Alternatively, you can use the xref:client/http.adoc#transport-dynamic[dynamic `HttpClient` transport]:

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

[[connect-custom-http-request]]
=== Customizing the Initial HTTP Request

Sometimes you need to add custom cookies, or other HTTP headers, or specify a WebSocket sub-protocol to the HTTP request that initiates the WebSocket communication.

You can do this by using overloaded versions of the `WebSocketClient.connect(...)` method:

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

[[connect-inspect-http-response]]
=== Inspecting the Initial HTTP Response

If you want to inspect the HTTP response returned by the server as a reply to the  HTTP request that initiates the WebSocket communication, you may provide a `JettyUpgradeListener`:

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

[[architecture]]
== Jetty WebSocket Architecture

The Jetty WebSocket architecture is organized around the concept of a logical _connection_ between the client and the server.

The connection may be physical, when connecting to the server using HTTP/1.1, as the WebSocket bytes are carried directly by the TCP connection.

The connection may be virtual, when connecting to the server using HTTP/2, as the WebSocket bytes are wrapped into HTTP/2 `DATA` frames of an HTTP/2 stream.
In this case, a single TCP connection may carry several WebSocket virtual connections, each wrapped in its own HTTP/2 stream.

Each side of a WebSocket connection, either client or server, is made of two entities:

* A <<endpoints,WebSocket _endpoint_>>, the entity that _receives_ WebSocket events.
* A <<session,WebSocket _session_>>, the entity that offers an API to _send_ WebSocket data (and to close the WebSocket connection), as well as to configure WebSocket connection parameters.

[[endpoints]]
== WebSocket Endpoints

A WebSocket endpoint is the entity that receives WebSocket events.

The WebSocket events are the following:

* The _open_ event.
This event is emitted when the WebSocket communication has been successfully established.
Applications interested in the open event receive the WebSocket _session_ so that they can use it to send data to the remote peer.
* The _close_ event.
This event is emitted when the WebSocket communication has been closed.
Applications interested in the close event receive a WebSocket status code and an optional close reason message.
* The _error_ event.
This event is emitted when the WebSocket communication encounters a fatal error, such as an I/O error (for example, the network connection has been broken), or a protocol error (for example, the remote peer sends an invalid WebSocket frame).
Applications interested in the error event receive a `Throwable` that represent the error.
* The _frame_ events.
The frame events are emitted when a WebSocket frame is received, either a control frame such as PING, PONG or CLOSE, or a data frame such as BINARY or TEXT.
One or more data frames of the same type define a _message_.
* The _message_ events.
The message event are emitted when a WebSocket message is received.
The message event can be of two types:
** TEXT.
Applications interested in this type of messages receive a `String` representing the UTF-8 bytes received.
** BINARY.
Applications interested in this type of messages receive a `ByteBuffer` representing the raw bytes received.

<<endpoints-listener,Listener endpoints>> are notified of events by invoking the correspondent method defined by the `org.eclipse.jetty.websocket.api.Session.Listener` interface.

<<endpoints-annotated,Annotated endpoints>> are notified of events by invoking the correspondent method annotated with the correspondent annotation from the `+org.eclipse.jetty.websocket.api.annotations.*+` package.

Jetty uses ``MethodHandle``s to instantiate WebSocket endpoints and invoke WebSocket event methods, so WebSocket endpoint classes and WebSocket event methods must be `public`.

When using JPMS, your classes must be `public` and must be exported using the `exports` directive in your `module-info.java`.
It is not recommended to use the `opens` directive in your `module-info.java` for your classes, as it would expose your classes to deep reflection, which is unnecessary, as the `exports` directive is sufficient.

This guarantees that WebSocket endpoints can be accessed by the Jetty implementation without additional configuration, no matter whether you are using only the class-path, or the module-path.

For both types of WebSocket endpoints, only one thread at a time will be delivering frame or message events to the corresponding methods; the next frame or message event will not be delivered until the previous call to the corresponding method has exited, and if there is <<endpoints-demand,demand>> for it.
Endpoints will always be notified of message events in the same order they were received over the network.

[[endpoints-demand]]
=== WebSocket Events Demand

In order to receive WebSocket events, you must _demand_ for them; the only exception is the _open_ event, because it is the initial event that applications can interact with.

When a WebSocket event is received by an endpoint, the demand for WebSocket events (for that endpoint) is reset, so that no more WebSocket events will be received by the endpoint.
It is responsibility of the endpoint to demand to receive more WebSocket events.

For simple cases, you can just annotate your WebSocket endpoint with `@WebSocket(autoDemand = true)`, or implement `Session.Listener.AutoDemanding`.
In these two cases, when a method that receives a WebSocket event returns, the Jetty implementation automatically demands for another WebSocket event.

For example:

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

While auto-demand works for simple cases, it may not work in all cases, especially those where the method that receives the WebSocket event performs asynchronous operations.

The following example shows the problem:

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

Note how, in the example above, auto-demanding has the problem that receiving WebSocket text messages may happen faster than echoing them back, because the call to `sendText(\...)` may return almost immediately but be slow to complete because it is asynchronous.

In the example above, if another WebSocket text message arrives, and the `sendText(\...)` operation is not complete, a `WritePendingException` will be thrown.

In other cases, this may lead to infinite buffering of data, eventually causing ``OutOfMemoryError``s, and in general excessive resource consumption that may be difficult to diagnose and troubleshoot.

For more information, see also the <<session-send,section about sending data>>.

[CAUTION]
====
Always be careful when using auto-demand.

Analyze the operations that your endpoint performs and make sure they complete synchronously within the method.
====

To solve the problem outlined above, you must explicitly demand for the next WebSocket event, only when the processing of the previous events is complete.

For example:

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

Note how it is necessary to invoke `Session.demand()` from the _open_ event, in order to receive _message_ events.

Furthermore, note how every time a text message is received, a possibly slow asynchronous operation is initiated (which returns almost immediately, although it may not be completed yet) and then the method returns.

Because there is no demand when the method returns (because the asynchronous operation is not completed yet), the implementation will not notify any other WebSocket event (not even _frame_, _close_ or _error_ events).

When the asynchronous operation completes successfully the callback is notified; this, in turn, invokes `Session.demand()`, and the implementation may notify another WebSocket event (if any) to the WebSocket endpoint.

[[endpoints-listener]]
=== Listener Endpoints

A WebSocket endpoint may implement the `org.eclipse.jetty.websocket.api.Session.Listener` interface to receive WebSocket events:

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

==== Message Streaming Reads

If you need to deal with large WebSocket messages, you may reduce the memory usage by streaming the message content.
For large WebSocket messages, the memory usage may be large due to the fact that the text or the bytes must be accumulated until the message is complete before delivering the message event.

To stream textual or binary messages, you override either `org.eclipse.jetty.websocket.api.Session.Listener.onWebSocketPartialText(\...)` or `org.eclipse.jetty.websocket.api.Session.Listener.onWebSocketPartialBinary(\...)`.

These methods receive _chunks_ of, respectively, text and bytes that form the whole WebSocket message.

You may accumulate the chunks yourself, or process each chunk as it arrives, or stream the chunks elsewhere, for example:

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

[[endpoints-annotated]]
=== Annotated Endpoints

A WebSocket endpoint may annotate methods with `+org.eclipse.jetty.websocket.api.annotations.*+` annotations to receive WebSocket events.

Each annotated event method may take an optional `Session` argument as its first parameter:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/WebSocketDocs.java[tags=annotatedEndpoint]
----
<1> Use the `@WebSocket` annotation at the class level to make it a WebSocket endpoint, and disable auto-demand.
<2> Use the `@OnWebSocketOpen` annotation for the _open_ event.
As this is the first event notified to the endpoint, you can configure the `Session` object.
<3> Use the `@OnWebSocketMessage` annotation for the _message_ event, both for textual and binary messages.
<4> Use the `@OnWebSocketError` annotation for the _error_ event.
<5> Use the `@OnWebSocketClose` annotation for the _close_ event.

[[endpoints-annotated-streaming]]
==== Message Streaming Reads

If you need to deal with large WebSocket messages, you may reduce the memory usage by streaming the message content.

To stream textual or binary messages, you still use the `@OnWebSocketMessage` annotation, but you change the signature of the method to take an additional `boolean` parameter:

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

Alternatively, but less efficiently, you can use the `@OnWebSocketMessage` annotation, but you change the signature of the method to take, respectively, a `Reader` and an `InputStream`:

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

[CAUTION]
====
`Reader` or `InputStream` only offer blocking APIs, so if the remote peers are slow in sending the large WebSocket messages, reading threads may be blocked in `Reader.read(char[])` or `InputStream.read(byte[])`, possibly exhausting the thread pool.
====

Note that when you use blocking APIs, the invocations to `Session.demand()` are now performed by the `Reader` or `InputStream` implementations (as well as the `ByteBuffer` lifecycle management).
You indirectly control the demand by deciding when to read from `Reader` or `InputStream`.

[[session]]
== WebSocket Session

A WebSocket session is the entity that offers an API to send data to the remote peer, to close the WebSocket connection, and to configure WebSocket connection parameters.

[[session-configure]]
=== Configuring the Session

You may configure the WebSocket session behavior using the `org.eclipse.jetty.websocket.api.Session` APIs.
You want to do this as soon as you have access to the `Session` object, typically from the <<endpoints,_open_ event>> handler:

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

The settings that can be configured include:

maxBinaryMessageSize::
the maximum size in bytes of a binary message (which may be composed of multiple frames) that can be received.

maxTextMessageSize::
the maximum size in bytes of a text message (which may be composed of multiple frames) that can be received.

maxFrameSize::
the maximum payload size in bytes of any WebSocket frame that can be received.

inputBufferSize::
the input (read from network/transport layer) buffer size in bytes; it has no relationship with the WebSocket frame size or message size.

outputBufferSize::
the output (write to network/transport layer) buffer size in bytes; it has no relationship to the WebSocket frame size or message size.

autoFragment::
whether WebSocket frames are automatically fragmented to respect the maximum frame size.

idleTimeout::
the duration that a WebSocket connection may remain idle (that is, there is no network traffic, neither in read nor in write) before being closed by the implementation.

Please refer to the `Session` link:{javadoc-url}/org/eclipse/jetty/websocket/api/Session.html[javadocs] for the complete list of configuration APIs.

[[session-send]]
=== Sending Data

To send data to the remote peer, you can use the non-blocking APIs offered by `Session`.

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/WebSocketDocs.java[tags=sendNonBlocking]
----
<1> Non-blocking APIs require a `Callback` parameter.
<2> Note how the second send must be performed from inside the callback.
<3> Sequential sends may throw `WritePendingException`.

[IMPORTANT]
====
Non-blocking APIs are more difficult to use since you are required to meet the following condition:

* You cannot initiate another send of any kind until the previous send is completed.

For example, if you have initiated a text send, you cannot initiate another text or binary send, until the previous send has completed.
====

This requirement is necessary to avoid unbounded buffering that could lead to ``OutOfMemoryError``s.

[CAUTION]
====
We strongly recommend that you follow the condition above.

However, there may be cases where you want to explicitly control the number of outgoing buffered messages using `RemoteEndpoint.setMaxOutgoingFrames(int)`.

Remember that trying to control the number of outgoing frames is very difficult and tricky; you may set `maxOutgoingFrames=4` and have a situation where 6 threads try to concurrently send messages: threads 1 to 4 will be able to successfully buffer their messages, thread 5 may fail, but thread 6 may succeed because one of the previous threads completed its send.
At this point you have an out-of-order message delivery that could be unexpected and very difficult to troubleshoot because it will happen non-deterministically.
====

While non-blocking APIs are more difficult to use, they don't block the sender thread and therefore use less resources, which in turn typically allows for greater scalability under load: with respect to blocking APIs, non-blocking APIs need less resources to cope with the same load.

[[session-send-stream]]
==== Streaming Send APIs

If you need to send large WebSocket messages, you may reduce the memory usage by streaming the message content.

The Jetty WebSocket APIs offer `sendPartial*(\...)` methods that allow you to send a chunk of the whole message at a time, therefore reducing the memory usage since it is not necessary to have the whole message `String` or `ByteBuffer` in memory to send it.

The Jetty WebSocket APIs for streaming the message content are non-blocking and therefore you should wait (without blocking!) for the callbacks to complete.

Fortunately, Jetty provides the `IteratingCallback` utility class (described in more details xref:arch/io.adoc#echo[in this section]) which greatly simplify the use of non-blocking APIs:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/WebSocketDocs.java[tags=streamSendNonBlocking]
----
<1> Implementing `Callback` allows to pass `this` to `sendPartialBinary(\...)`.
<2> The `process()` method is called iteratively when each `sendPartialBinary(\...)` is completed.
<3> Sends the message chunks.
<4> When the last chunk as been sent, complete successfully the `IteratingCallback`.
<5> Only when the `IteratingCallback` is completed successfully, demand for more WebSocket events.

[[session-ping]]
=== Sending Ping/Pong

The WebSocket protocol defines two special frame, named `PING` and `PONG` that may be interesting to applications for these use cases:

* Calculate the round-trip time with the remote peer.
* Keep the connection from being closed due to idle timeout -- a heartbeat-like mechanism.

To handle `PING`/`PONG` events, you may implement methods `Session.Listener.onWebSocketPing(ByteBuffer)` and/or `Session.Listener.onWebSocketPong(ByteBuffer)`.

[NOTE]
====
`PING`/`PONG` events are also supported when using annotations via the `OnWebSocketFrame` annotation.
====

`PING` frames may contain opaque application bytes, and the WebSocket implementation replies to them with a `PONG` frame containing the same bytes:

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

[[session-close]]
=== Closing the Session

When you want to terminate the communication with the remote peer, you close the `Session`:

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

Closing a WebSocket `Session` carries a status code and a reason message that the remote peer can inspect in the _close_ event handler (see <<endpoints,this section>>).

[NOTE]
====
The reason message is optional, and may be truncated to fit into the WebSocket frame sent to the client.
It is best to use short tokens such as `"shutdown"`, or `"idle_timeout"`, etc. or even application specific codes such as `"0001"` or `"00AF"` that can be converted by the application into more meaningful messages.
====
