From 78dc11b6484f016b1b651d6daa1d21710630e3da Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Thu, 22 Apr 2021 21:42:14 +0200 Subject: [PATCH] Documented WebSocketClient. Updates after review. Signed-off-by: Simone Bordet --- .../client/websocket/client-websocket.adoc | 3 ++ .../asciidoc/programming-guide/websocket.adoc | 45 ++++++++++++++++--- .../jetty/docs/programming/WebSocketDocs.java | 18 ++++---- .../client/websocket/WebSocketClientDocs.java | 4 +- 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/client/websocket/client-websocket.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/client/websocket/client-websocket.adoc index 415ce1df27a..ecd516ea592 100644 --- a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/client/websocket/client-websocket.adoc +++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/client/websocket/client-websocket.adoc @@ -52,6 +52,9 @@ include::../../{doc_code}/org/eclipse/jetty/docs/programming/client/websocket/We 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. + +WebSocket specific configuration may be typically be given a default value in `WebSocketClient` and then overridden more specifically, see for example xref:pg-websocket-session-configure[this section]. [[pg-client-websocket-stop]] ==== Stopping WebSocketClient diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/websocket.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/websocket.adoc index 4d3b94b2e9b..4181873eea6 100644 --- a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/websocket.adoc +++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/websocket.adoc @@ -40,7 +40,6 @@ This event is emitted when the WebSocket communication has been successfully est Applications interested in the connect 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. -// TODO: is this event emitted when the application calls Session.close()? 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). @@ -83,15 +82,41 @@ include::{doc_code}/org/eclipse/jetty/docs/programming/WebSocketDocs.java[tags=s [[pg-websocket-endpoints-annotated]] ===== Annotated Endpoints -A WebSocket endpoint may annotate methods with `org.eclipse.jetty.websocket.api.annotations.*` annotations to receive WebSocket events: +A WebSocket endpoint may annotate methods with `org.eclipse.jetty.websocket.api.annotations.*` annotations to receive WebSocket events. +Each annotated method may take an optional `Session` argument as its first parameter: [source,java,indent=0] ---- include::{doc_code}/org/eclipse/jetty/docs/programming/WebSocketDocs.java[tags=annotatedEndpoint] ---- -<1> You must annotate the class with the `@WebSocket` annotation to make it an endpoint. -<2> For each event you are interested in, use the correspondent annotation. -<3> Note how methods annotated with `@OnWebSocketMessage` may optionally declare `Session` as first parameter. +<1> Use the `@WebSocket` annotation at the class level to make it a WebSocket endpoint. +<2> Use the `@OnWebSocketConnect` annotation for the _connect_ event. +As this is the first event notified to the endpoint, you can configure the `Session` object. +<3> Use the `@OnWebSocketClose` annotation for the _close_ event. +The method may take an optional `Session` as first parameter. +<4> Use the `@OnWebSocketError` annotation for the _error_ event. +The method may take an optional `Session` as first parameter. +<5> Use the `@OnWebSocketMessage` annotation for the _message_ event, both for textual and binary messages. +The method may take an optional `Session` as first parameter. + +[NOTE] +==== +For binary messages, you may declare the annotated method with either or these two signatures: + +[source,java] +---- +@OnWebSocketMessage +public void methodName(byte[] bytes, int offset, int length) { ... } +---- + +or + +[source,java] +---- +@OnWebSocketMessage +public void methodName(ByteBuffer buffer) { ... } +---- +==== ====== Message Streaming Reads @@ -181,6 +206,16 @@ Furthermore, if you have initiated a non-blocking send, you cannot initiate a bl 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 buffered messages 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. [[pg-websocket-session-send-stream]] diff --git a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/WebSocketDocs.java b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/WebSocketDocs.java index 25698506ba2..11958c6f36f 100644 --- a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/WebSocketDocs.java +++ b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/WebSocketDocs.java @@ -90,7 +90,7 @@ public class WebSocketDocs } @Override - public void onWebSocketBinary(byte[] payload, int offset, int len) + public void onWebSocketBinary(byte[] payload, int offset, int length) { // A WebSocket binary message is received. @@ -101,7 +101,7 @@ public class WebSocketDocs if (pngBytes[i] != payload[offset + i]) return; } - savePNGImage(payload, offset, len); + savePNGImage(payload, offset, length); } } // end::listenerEndpoint[] @@ -150,7 +150,7 @@ public class WebSocketDocs session.getRemote().sendString("connected", WriteCallback.NOOP); } - @OnWebSocketClose // <2> + @OnWebSocketClose // <3> public void onClose(int statusCode, String reason) { // The WebSocket connection is closed. @@ -159,7 +159,7 @@ public class WebSocketDocs disposeResources(); } - @OnWebSocketError // <2> + @OnWebSocketError // <4> public void onError(Throwable cause) { // The WebSocket connection failed. @@ -171,7 +171,7 @@ public class WebSocketDocs disposeResources(); } - @OnWebSocketMessage // <2> + @OnWebSocketMessage // <5> public void onTextMessage(Session session, String message) // <3> { // A WebSocket textual message is received. @@ -181,8 +181,8 @@ public class WebSocketDocs session.getRemote().sendString(message.substring("echo:".length()), WriteCallback.NOOP); } - @OnWebSocketMessage // <2> - public void onBinaryMessage(byte[] payload, int offset, int len) + @OnWebSocketMessage // <5> + public void onBinaryMessage(byte[] payload, int offset, int length) { // A WebSocket binary message is received. @@ -193,7 +193,7 @@ public class WebSocketDocs if (pngBytes[i] != payload[offset + i]) return; } - savePNGImage(payload, offset, len); + savePNGImage(payload, offset, length); } } // end::annotatedEndpoint[] @@ -475,7 +475,7 @@ public class WebSocketDocs { } - private static void savePNGImage(byte[] payload, int offset, int len) + private static void savePNGImage(byte[] payload, int offset, int length) { } diff --git a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/websocket/WebSocketClientDocs.java b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/websocket/WebSocketClientDocs.java index 8e5ae17eaa3..5221588a647 100644 --- a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/websocket/WebSocketClientDocs.java +++ b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/client/websocket/WebSocketClientDocs.java @@ -113,7 +113,7 @@ public class WebSocketClientDocs // receives WebSocket messages from the server. ClientEndPoint clientEndPoint = new ClientEndPoint(); // The server URI to connect to. - URI serverURI = URI.create("ws://domain.com/path"); + URI serverURI = URI.create("wss://domain.com/path"); // Connect the client EndPoint to the server. CompletableFuture clientSessionPromise = webSocketClient.connect(clientEndPoint, serverURI); @@ -132,7 +132,7 @@ public class WebSocketClientDocs webSocketClient.start(); ClientEndPoint clientEndPoint = new ClientEndPoint(); - URI serverURI = URI.create("ws://domain.com/path"); + URI serverURI = URI.create("wss://domain.com/path"); // Connect the client EndPoint to the server. CompletableFuture clientSessionPromise = webSocketClient.connect(clientEndPoint, serverURI);