Merge pull request #19543 from akka/wip-19315-document-websocket-client-johanandren
=doc #19315 websocket client docs
This commit is contained in:
commit
1822108175
7 changed files with 469 additions and 9 deletions
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package docs.http.javadsl;
|
||||
|
||||
import akka.Done;
|
||||
import akka.NotUsed;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.http.javadsl.Http;
|
||||
import akka.http.javadsl.model.StatusCodes;
|
||||
import akka.http.javadsl.model.headers.Authorization;
|
||||
import akka.http.javadsl.model.ws.Message;
|
||||
import akka.http.javadsl.model.ws.TextMessage;
|
||||
import akka.http.javadsl.model.ws.WebSocketRequest;
|
||||
import akka.http.javadsl.model.ws.WebSocketUpgradeResponse;
|
||||
import akka.japi.Pair;
|
||||
import akka.japi.function.Procedure;
|
||||
import akka.stream.ActorMaterializer;
|
||||
import akka.stream.Materializer;
|
||||
import akka.stream.javadsl.Flow;
|
||||
import akka.stream.javadsl.Keep;
|
||||
import akka.stream.javadsl.Sink;
|
||||
import akka.stream.javadsl.Source;
|
||||
|
||||
import java.util.concurrent.CompletionStage;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class WebSocketClientExampleTest {
|
||||
|
||||
// compile only test
|
||||
public void testSingleWebSocketRequest() {
|
||||
//#single-WebSocket-request
|
||||
ActorSystem system = ActorSystem.create();
|
||||
Materializer materializer = ActorMaterializer.create(system);
|
||||
Http http = Http.get(system);
|
||||
|
||||
// print each incoming text message
|
||||
// would throw exception on non strict or binary message
|
||||
Sink<Message, CompletionStage<Done>> printSink =
|
||||
Sink.foreach((message) ->
|
||||
System.out.println("Got message: " + message.asTextMessage().getStrictText())
|
||||
);
|
||||
|
||||
// send this as a message over the WebSocket
|
||||
Source<Message, NotUsed> helloSource =
|
||||
Source.single(TextMessage.create("hello world"));
|
||||
|
||||
// the CompletionStage<Done> is the materialized value of Sink.foreach
|
||||
// and it is completed when the stream completes
|
||||
Flow<Message, Message, CompletionStage<Done>> flow =
|
||||
Flow.fromSinkAndSourceMat(printSink, helloSource, Keep.left());
|
||||
|
||||
Pair<CompletionStage<WebSocketUpgradeResponse>, CompletionStage<Done>> pair =
|
||||
http.singleWebSocketRequest(
|
||||
WebSocketRequest.create("ws://echo.websocket.org"),
|
||||
flow,
|
||||
materializer
|
||||
);
|
||||
|
||||
// The first value in the pair is a CompletionStage<WebSocketUpgradeResponse> that
|
||||
// completes when the WebSocket request has connected successfully (or failed)
|
||||
CompletionStage<Done> connected = pair.first().thenApply(upgrade -> {
|
||||
// just like a regular http request we can get 404 NotFound,
|
||||
// with a response body, that will be available from upgrade.response
|
||||
if (upgrade.response().status().equals(StatusCodes.OK)) {
|
||||
return Done.getInstance();
|
||||
} else {
|
||||
throw new RuntimeException("Connection failed: " + upgrade.response().status());
|
||||
}
|
||||
});
|
||||
|
||||
// the second value is the completion of the sink from above
|
||||
// in other words, it completes when the WebSocket disconnects
|
||||
CompletionStage<Done> closed = pair.second();
|
||||
|
||||
// in a real application you would not side effect here
|
||||
// and handle errors more carefully
|
||||
connected.thenAccept(done -> System.out.println("Connected"));
|
||||
closed.thenAccept(done -> System.out.println("Connection closed"));
|
||||
|
||||
//#single-WebSocket-request
|
||||
}
|
||||
|
||||
// compile time only test
|
||||
public void testAuthorizedSingleWebSocketRequest() {
|
||||
Materializer materializer = null;
|
||||
Http http = null;
|
||||
|
||||
Flow<Message, Message, NotUsed> flow = null;
|
||||
|
||||
//#authorized-single-WebSocket-request
|
||||
http.singleWebSocketRequest(
|
||||
WebSocketRequest.create("ws://example.com:8080/some/path")
|
||||
.addHeader(Authorization.basic("johan", "correcthorsebatterystaple")),
|
||||
flow,
|
||||
materializer);
|
||||
//#authorized-single-WebSocket-request
|
||||
}
|
||||
|
||||
// compile time only test
|
||||
public void testWebSocketClientFlow() {
|
||||
//#WebSocket-client-flow
|
||||
ActorSystem system = ActorSystem.create();
|
||||
Materializer materializer = ActorMaterializer.create(system);
|
||||
Http http = Http.get(system);
|
||||
|
||||
// print each incoming text message
|
||||
// would throw exception on non strict or binary message
|
||||
Sink<Message, CompletionStage<Done>> printSink =
|
||||
Sink.foreach((message) ->
|
||||
System.out.println("Got message: " + message.asTextMessage().getStrictText())
|
||||
);
|
||||
|
||||
// send this as a message over the WebSocket
|
||||
Source<Message, NotUsed> helloSource =
|
||||
Source.single(TextMessage.create("hello world"));
|
||||
|
||||
|
||||
Flow<Message, Message, CompletionStage<WebSocketUpgradeResponse>> webSocketFlow =
|
||||
http.webSocketClientFlow(WebSocketRequest.create("ws://echo.websocket.org"));
|
||||
|
||||
|
||||
Pair<CompletionStage<WebSocketUpgradeResponse>, CompletionStage<Done>> pair =
|
||||
helloSource.viaMat(webSocketFlow, Keep.right())
|
||||
.toMat(printSink, Keep.both())
|
||||
.run(materializer);
|
||||
|
||||
|
||||
// The first value in the pair is a CompletionStage<WebSocketUpgradeResponse> that
|
||||
// completes when the WebSocket request has connected successfully (or failed)
|
||||
CompletionStage<WebSocketUpgradeResponse> upgradeCompletion = pair.first();
|
||||
|
||||
// the second value is the completion of the sink from above
|
||||
// in other words, it completes when the WebSocket disconnects
|
||||
CompletionStage<Done> closed = pair.second();
|
||||
|
||||
CompletionStage<Done> connected = upgradeCompletion.thenApply(upgrade->
|
||||
{
|
||||
// just like a regular http request we can get 404 NotFound,
|
||||
// with a response body, that will be available from upgrade.response
|
||||
if (upgrade.response().status().equals(StatusCodes.OK)) {
|
||||
return Done.getInstance();
|
||||
} else {
|
||||
throw new RuntimeException(("Connection failed: " + upgrade.response().status()));
|
||||
}
|
||||
});
|
||||
|
||||
// in a real application you would not side effect here
|
||||
// and handle errors more carefully
|
||||
connected.thenAccept(done -> System.out.println("Connected"));
|
||||
closed.thenAccept(done -> System.out.println("Connection closed"));
|
||||
|
||||
//#WebSocket-client-flow
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -3,6 +3,84 @@
|
|||
Client-Side WebSocket Support
|
||||
=============================
|
||||
|
||||
Not yet implemented see 17275_.
|
||||
Client side WebSocket support is available through ``Http.singleWebSocketRequest`` ,
|
||||
``Http.webSocketClientFlow`` and ``Http.webSocketClientLayer``.
|
||||
|
||||
A WebSocket consists of two streams of messages, incoming messages (a :class:`Sink`) and outgoing messages
|
||||
(a :class:`Source`) where either may be signalled first; or even be the only direction in which messages flow
|
||||
during the lifetime of the connection. Therefore a WebSocket connection is modelled as either something you connect a
|
||||
``Flow<Message, Message, Mat>`` to or a ``Flow<Message, Message, Mat>`` that you connect a ``Source<Message, Mat>``
|
||||
and a ``Sink<Message, Mat>`` to.
|
||||
|
||||
A WebSocket request starts with a regular HTTP request which contains an ``Upgrade`` header (and possibly
|
||||
other regular HTTP request properties), so in addition to the flow of messages there also is an initial response
|
||||
from the server, this is modelled with :class:`WebSocketUpgradeResponse`.
|
||||
|
||||
The methods of the WebSocket client API handle the upgrade to WebSocket on connection success and materializes
|
||||
the connected WebSocket stream. If the connection fails, for example with a ``404 NotFound`` error, this regular
|
||||
HTTP result can be found in ``WebSocketUpgradeResponse.response``
|
||||
|
||||
|
||||
Message
|
||||
-------
|
||||
Messages sent and received over a WebSocket can be either :class:`TextMessage` s or :class:`BinaryMessage` s and each
|
||||
of those can be either strict (all data in one chunk) or streaming. In typical applications messages will be strict as
|
||||
WebSockets are usually deployed to communicate using small messages not stream data, the protocol does however
|
||||
allow this (by not marking the first fragment as final, as described in `rfc 6455 section 5.2`__).
|
||||
|
||||
__ https://tools.ietf.org/html/rfc6455#section-5.2
|
||||
|
||||
The strict text is available from ``TextMessage.getStrictText`` and strict binary data from
|
||||
``BinaryMessage.getStrictData``.
|
||||
|
||||
For streaming messages ``BinaryMessage.getStreamedData`` and ``TextMessage.getStreamedText`` is used to access the data.
|
||||
In these cases the data is provided as a ``Source<ByteString, NotUsed>`` for binary and ``Source<String, NotUsed>``
|
||||
for text messages.
|
||||
|
||||
|
||||
singleWebSocketRequest
|
||||
----------------------
|
||||
``singleWebSocketRequest`` takes a :class:`WebSocketRequest` and a flow it will connect to the source and
|
||||
sink of the WebSocket connection. It will trigger the request right away and returns a tuple containing a
|
||||
``CompletionStage<WebSocketUpgradeResponse>`` and the materialized value from the flow passed to the method.
|
||||
|
||||
The future will succeed when the WebSocket connection has been established or the server returned a regular
|
||||
HTTP response, or fail if the connection fails with an exception.
|
||||
|
||||
Simple example sending a message and printing any incoming message:
|
||||
|
||||
.. includecode:: ../../code/docs/http/javadsl/WebSocketClientExampleTest.java
|
||||
:include: single-WebSocket-request
|
||||
|
||||
The websocket request may also include additional headers, like in this example, HTTP Basic Auth:
|
||||
|
||||
.. includecode:: ../../code/docs/http/javadsl/WebSocketClientExampleTest.java
|
||||
:include: authorized-single-WebSocket-request
|
||||
|
||||
|
||||
webSocketClientFlow
|
||||
-------------------
|
||||
``webSocketClientFlow`` takes a request, and returns a ``Flow<Message, Message, Future<WebSocketUpgradeResponse>>``.
|
||||
|
||||
The future that is materialized from the flow will succeed when the WebSocket connection has been established or
|
||||
the server returned a regular HTTP response, or fail if the connection fails with an exception.
|
||||
|
||||
.. note::
|
||||
The :class:`Flow` that is returned by this method can only be materialized once. For each request a new
|
||||
flow must be acquired by calling the method again.
|
||||
|
||||
Simple example sending a message and printing any incoming message:
|
||||
|
||||
|
||||
.. includecode:: ../../code/docs/http/javadsl/WebSocketClientExampleTest.java
|
||||
:include: WebSocket-client-flow
|
||||
|
||||
|
||||
webSocketClientLayer
|
||||
--------------------
|
||||
Just like the :ref:`http-client-layer-java` for regular HTTP requests, the WebSocket layer can be used fully detached from the
|
||||
underlying TCP interface. The same scenarios as described for regular HTTP requests apply here.
|
||||
|
||||
The returned layer forms a ``BidiFlow<Message, SslTlsOutbound, SslTlsInbound, Message, CompletionStage<WebSocketUpgradeResponse>>``.
|
||||
|
||||
|
||||
.. _17275: https://github.com/akka/akka/issues/17275
|
||||
|
|
@ -22,9 +22,9 @@ the ``PatternCS`` class that provide the ability to interact between Actors and
|
|||
|
||||
Should you have the need to use Scala Futures with these new Java APIs please use
|
||||
the ``scala-java8-compat`` library that comes as a dependency of Akka. For more
|
||||
information see `the documentation``_.
|
||||
information see `the documentation`__.
|
||||
|
||||
.. _`the documentation`:: https://github.com/scala/scala-java8-compat
|
||||
__ https://github.com/scala/scala-java8-compat
|
||||
|
||||
akka.Done and akka.NotUsed replacing Unit and BoxedUnit
|
||||
-------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue