+doc #19429 initial merge of docs-dev and docs
This commit is contained in:
parent
be0c8af4c0
commit
5a18d43435
501 changed files with 9876 additions and 3681 deletions
114
akka-docs/rst/scala/http/routing-dsl/websocket-support.rst
Normal file
114
akka-docs/rst/scala/http/routing-dsl/websocket-support.rst
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
.. _server-side-websocket-support-scala:
|
||||
|
||||
Server-Side WebSocket Support
|
||||
=============================
|
||||
|
||||
WebSocket is a protocol that provides a bi-directional channel between browser and webserver usually run over an
|
||||
upgraded HTTP(S) connection. Data is exchanged in messages whereby a message can either be binary data or unicode text.
|
||||
|
||||
Akka HTTP provides a stream-based implementation of the WebSocket protocol that hides the low-level details of the
|
||||
underlying binary framing wire-protocol and provides a simple API to implement services using WebSocket.
|
||||
|
||||
|
||||
Model
|
||||
-----
|
||||
|
||||
The basic unit of data exchange in the WebSocket protocol is a message. A message can either be binary message,
|
||||
i.e. a sequence of octets or a text message, i.e. a sequence of unicode code points.
|
||||
|
||||
Akka HTTP provides a straight-forward model for this abstraction:
|
||||
|
||||
.. includecode:: /../../akka-http-core/src/main/scala/akka/http/scaladsl/model/ws/Message.scala
|
||||
:include: message-model
|
||||
|
||||
The data of a message is provided as a stream because WebSocket messages do not have a predefined size and could
|
||||
(in theory) be infinitely long. However, only one message can be open per direction of the WebSocket connection,
|
||||
so that many application level protocols will want to make use of the delineation into (small) messages to transport
|
||||
single application-level data units like "one event" or "one chat message".
|
||||
|
||||
Many messages are small enough to be sent or received in one go. As an opportunity for optimization, the model provides
|
||||
a ``Strict`` subclass for each kind of message which contains data as a strict, i.e. non-streamed, ``ByteString`` or
|
||||
``String``.
|
||||
|
||||
When receiving data from the network connection the WebSocket implementation tries to create a ``Strict`` message whenever
|
||||
possible, i.e. when the complete data was received in one chunk. However, the actual chunking of messages over a network
|
||||
connection and through the various streaming abstraction layers is not deterministic from the perspective of the
|
||||
application. Therefore, application code must be able to handle both streaming and strict messages and not expect
|
||||
certain messages to be strict. (Particularly, note that tests against ``localhost`` will behave differently than tests
|
||||
against remote peers where data is received over a physical network connection.)
|
||||
|
||||
For sending data, use ``TextMessage.apply(text: String)`` to create a ``Strict`` message which is often the natural
|
||||
choice when the complete message has already been assembled. Otherwise, use ``TextMessage.apply(textStream: Source[String, Any])``
|
||||
to create a streaming message from an Akka Stream source.
|
||||
|
||||
Server API
|
||||
----------
|
||||
|
||||
The entrypoint for the Websocket API is the synthetic ``UpgradeToWebsocket`` header which is added to a request
|
||||
if Akka HTTP encounters a Websocket upgrade request.
|
||||
|
||||
The Websocket specification mandates that details of the Websocket connection are negotiated by placing special-purpose
|
||||
HTTP-headers into request and response of the HTTP upgrade. In Akka HTTP these HTTP-level details of the WebSocket
|
||||
handshake are hidden from the application and don't need to be managed manually.
|
||||
|
||||
Instead, the synthetic ``UpgradeToWebsocket`` represents a valid Websocket upgrade request. An application can detect
|
||||
a Websocket upgrade request by looking for the ``UpgradeToWebsocket`` header. It can choose to accept the upgrade and
|
||||
start a Websocket connection by responding to that request with an ``HttpResponse`` generated by one of the
|
||||
``UpgradeToWebsocket.handleMessagesWith`` methods. In its most general form this method expects two arguments:
|
||||
first, a handler ``Flow[Message, Message, Any]`` that will be used to handle Websocket messages on this connection.
|
||||
Second, the application can optionally choose one of the proposed application-level sub-protocols by inspecting the
|
||||
values of ``UpgradeToWebsocket.requestedProtocols`` and pass the chosen protocol value to ``handleMessages``.
|
||||
|
||||
Handling Messages
|
||||
+++++++++++++++++
|
||||
|
||||
A message handler is expected to be implemented as a ``Flow[Message, Message, Any]``. For typical request-response
|
||||
scenarios this fits very well and such a ``Flow`` can be constructed from a simple function by using
|
||||
``Flow[Message].map`` or ``Flow[Message].mapAsync``.
|
||||
|
||||
There are other use-cases, e.g. in a server-push model, where a server message is sent spontaneously, or in a
|
||||
true bi-directional scenario where input and output aren't logically connected. Providing the handler as a ``Flow`` in
|
||||
these cases may not fit. Another method, ``UpgradeToWebsocket.handleMessagesWithSinkSource``, is provided
|
||||
which allows to pass an output-generating ``Source[Message, Any]`` and an input-receiving ``Sink[Message, Any]`` independently.
|
||||
|
||||
Note that a handler is required to consume the data stream of each message to make place for new messages. Otherwise,
|
||||
subsequent messages may be stuck and message traffic in this direction will stall.
|
||||
|
||||
Example
|
||||
+++++++
|
||||
|
||||
Let's look at an example_.
|
||||
|
||||
Websocket requests come in like any other requests. In the example, requests to ``/greeter`` are expected to be
|
||||
Websocket requests:
|
||||
|
||||
.. includecode:: ../../code/docs/http/scaladsl/server/WebsocketExampleSpec.scala
|
||||
:include: websocket-request-handling
|
||||
|
||||
It uses pattern matching on the path and then inspects the request to query for the ``UpgradeToWebsocket`` header. If
|
||||
such a header is found, it is used to generate a response by passing a handler for Websocket messages to the
|
||||
``handleMessages`` method. If no such header is found a "400 Bad Request" response is generated.
|
||||
|
||||
The passed handler expects text messages where each message is expected to contain (a person's) name
|
||||
and then responds with another text message that contains a greeting:
|
||||
|
||||
.. includecode:: ../../code/docs/http/scaladsl/server/WebsocketExampleSpec.scala
|
||||
:include: websocket-handler
|
||||
|
||||
Routing support
|
||||
---------------
|
||||
|
||||
The routing DSL provides the :ref:`-handleWebsocketMessages-` directive to install a WebSocket handler if the request
|
||||
was a WebSocket request. Otherwise, the directive rejects the request.
|
||||
|
||||
Here's the above simple request handler rewritten as a route:
|
||||
|
||||
.. includecode2:: ../../code/docs/http/scaladsl/server/directives/WebsocketDirectivesExamplesSpec.scala
|
||||
:snippet: greeter-service
|
||||
|
||||
The example also includes code demonstrating the testkit support for Websocket services. It allows to create Websocket
|
||||
requests to run against a route using `WS` which can be used to provide a mock Websocket probe that allows manual
|
||||
testing of the Websocket handler's behavior if the request was accepted.
|
||||
|
||||
|
||||
.. _example: @github@/akka-docs-dev/rst/scala/code/docs/http/scaladsl/server/WebsocketExampleSpec.scala
|
||||
Loading…
Add table
Add a link
Reference in a new issue