=doc add more documentation for WebsocketDirectives and testing
This commit is contained in:
parent
060ea707c9
commit
9399455601
8 changed files with 171 additions and 14 deletions
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
package docs.http.scaladsl.server
|
||||
|
||||
import akka.http.scaladsl.model.ws.BinaryMessage
|
||||
import akka.stream.scaladsl.Sink
|
||||
import org.scalatest.{ Matchers, WordSpec }
|
||||
|
||||
class WebsocketExampleSpec extends WordSpec with Matchers {
|
||||
|
|
@ -27,13 +29,16 @@ class WebsocketExampleSpec extends WordSpec with Matchers {
|
|||
// returns a greeting message for that name
|
||||
val greeterWebsocketService =
|
||||
Flow[Message]
|
||||
.collect {
|
||||
.mapConcat {
|
||||
// we match but don't actually consume the text message here,
|
||||
// rather we simply stream it back as the tail of the response
|
||||
// this means we might start sending the response even before the
|
||||
// end of the incoming message has been received
|
||||
case tm: TextMessage ⇒ TextMessage(Source.single("Hello ") ++ tm.textStream)
|
||||
// ignore binary messages
|
||||
case tm: TextMessage ⇒ TextMessage(Source.single("Hello ") ++ tm.textStream) :: Nil
|
||||
case bm: BinaryMessage =>
|
||||
// ignore binary messages but drain content to avoid the stream being clogged
|
||||
bm.dataStream.runWith(Sink.ignore)
|
||||
Nil
|
||||
}
|
||||
//#websocket-handler
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server.directives
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import akka.util.ByteString
|
||||
|
||||
import akka.stream.OverflowStrategy
|
||||
import akka.stream.scaladsl.{ Sink, Source, Flow }
|
||||
|
||||
import docs.http.scaladsl.server.RoutingSpec
|
||||
|
||||
import akka.http.scaladsl.model.ws.{ TextMessage, Message, BinaryMessage }
|
||||
import akka.http.scaladsl.testkit.WSProbe
|
||||
|
||||
class WebsocketDirectivesExamplesSpec extends RoutingSpec {
|
||||
"greeter-service" in {
|
||||
def greeter: Flow[Message, Message, Any] =
|
||||
Flow[Message].mapConcat {
|
||||
case tm: TextMessage ⇒
|
||||
TextMessage(Source.single("Hello ") ++ tm.textStream ++ Source.single("!")) :: Nil
|
||||
case bm: BinaryMessage ⇒
|
||||
// ignore binary messages but drain content to avoid the stream being clogged
|
||||
bm.dataStream.runWith(Sink.ignore)
|
||||
Nil
|
||||
}
|
||||
val websocketRoute =
|
||||
path("greeter") {
|
||||
handleWebsocketMessages(greeter)
|
||||
}
|
||||
|
||||
// create a testing probe representing the client-side
|
||||
val wsClient = WSProbe()
|
||||
|
||||
// WS creates a Websocket request for testing
|
||||
WS("/greeter", wsClient.flow) ~> websocketRoute ~>
|
||||
check {
|
||||
// check response for WS Upgrade headers
|
||||
isWebsocketUpgrade shouldEqual true
|
||||
|
||||
// manually run a WS conversation
|
||||
wsClient.sendMessage("Peter")
|
||||
wsClient.expectMessage("Hello Peter!")
|
||||
|
||||
wsClient.sendMessage(BinaryMessage(ByteString("abcdef")))
|
||||
wsClient.expectNoMessage(100.millis)
|
||||
|
||||
wsClient.sendMessage("John")
|
||||
wsClient.expectMessage("Hello John!")
|
||||
|
||||
wsClient.sendCompletion()
|
||||
wsClient.expectCompletion()
|
||||
}
|
||||
}
|
||||
|
||||
"handle-multiple-protocols" in {
|
||||
def greeterService: Flow[Message, Message, Any] =
|
||||
Flow[Message].mapConcat {
|
||||
case tm: TextMessage ⇒
|
||||
TextMessage(Source.single("Hello ") ++ tm.textStream ++ Source.single("!")) :: Nil
|
||||
case bm: BinaryMessage ⇒
|
||||
// ignore binary messages but drain content to avoid the stream being clogged
|
||||
bm.dataStream.runWith(Sink.ignore)
|
||||
Nil
|
||||
}
|
||||
|
||||
def echoService: Flow[Message, Message, Any] =
|
||||
Flow[Message]
|
||||
// needed because a noop flow hasn't any buffer that would start processing in tests
|
||||
.buffer(1, OverflowStrategy.backpressure)
|
||||
|
||||
def websocketMultipleProtocolRoute =
|
||||
path("services") {
|
||||
handleWebsocketMessagesForProtocol(greeterService, "greeter") ~
|
||||
handleWebsocketMessagesForProtocol(echoService, "echo")
|
||||
}
|
||||
|
||||
val wsClient = WSProbe()
|
||||
|
||||
// WS creates a Websocket request for testing
|
||||
WS("/services", wsClient.flow, List("other", "echo")) ~>
|
||||
websocketMultipleProtocolRoute ~>
|
||||
check {
|
||||
expectWebsocketUpgradeWithProtocol { protocol ⇒
|
||||
protocol shouldEqual "echo"
|
||||
|
||||
wsClient.sendMessage("Peter")
|
||||
wsClient.expectMessage("Peter")
|
||||
|
||||
wsClient.sendMessage(BinaryMessage(ByteString("abcdef")))
|
||||
wsClient.expectMessage(ByteString("abcdef"))
|
||||
|
||||
wsClient.sendMessage("John")
|
||||
wsClient.expectMessage("John")
|
||||
|
||||
wsClient.sendCompletion()
|
||||
wsClient.expectCompletion()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -72,7 +72,10 @@ Directive Description
|
|||
:ref:`-handleRejections-` Transforms rejections produced by the inner route using the given
|
||||
``RejectionHandler``
|
||||
:ref:`-handleWebsocketMessages-` Handles websocket requests with the given handler and rejects other requests
|
||||
with a ``ExpectedWebsocketRequestRejection``
|
||||
with an ``ExpectedWebsocketRequestRejection``
|
||||
:ref:`-handleWebsocketMessagesForProtocol-` Handles websocket requests with the given handler if the subprotocol matches
|
||||
and rejects other requests with an ``ExpectedWebsocketRequestRejection`` or
|
||||
an ``UnsupportedWebsocketSubprotocolRejection``.
|
||||
:ref:`-handleWith-` Completes the request using a given function
|
||||
:ref:`-head-` Rejects all non-HEAD requests
|
||||
:ref:`-headerValue-` Extracts an HTTP header value using a given ``HttpHeader ⇒ Option[T]``
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
handleWebsocketMessages
|
||||
=======================
|
||||
|
||||
...
|
||||
Handles Websocket requests with the given handler and rejects other requests with an
|
||||
``ExpectedWebsocketRequestRejection``.
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
|
@ -14,10 +15,16 @@ Signature
|
|||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
The directive first checks if the request was a valid Websocket handshake request and if yes, it completes the request
|
||||
with the passed handler. Otherwise, the request is rejected with an ``ExpectedWebsocketRequestRejection``.
|
||||
|
||||
Websocket subprotocols offered in the ``Sec-Websocket-Protocol`` header of the request are ignored. If you want to
|
||||
support several protocols use the :ref:`-handleWebsocketMessagesForProtocol-` directive, instead.
|
||||
|
||||
For more information about the Websocket support, see :ref:`server-side-websocket-support-scala`.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/WebsocketDirectivesExamplesSpec.scala
|
||||
:snippet: 0handleWebsocketMessages
|
||||
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/WebsocketDirectivesExamplesSpec.scala
|
||||
:snippet: greeter-service
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
.. _-handleWebsocketMessagesForProtocol-:
|
||||
|
||||
handleWebsocketMessagesForProtocol
|
||||
==================================
|
||||
|
||||
Handles Websocket requests with the given handler if the given subprotocol is offered in the ``Sec-Websocket-Protocol``
|
||||
header of the request and rejects other requests with an ``ExpectedWebsocketRequestRejection`` or an
|
||||
``UnsupportedWebsocketSubprotocolRejection``.
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/WebsocketDirectives.scala
|
||||
:snippet: handleWebsocketMessagesForProtocol
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The directive first checks if the request was a valid Websocket handshake request and if the request offers the passed
|
||||
subprotocol name. If yes, the directive completes the request with the passed handler. Otherwise, the request is
|
||||
either rejected with an ``ExpectedWebsocketRequestRejection`` or an ``UnsupportedWebsocketSubprotocolRejection``.
|
||||
|
||||
To support several subprotocols, for example at the same path, several instances of ``handleWebsocketMessagesForProtocol`` can
|
||||
be chained using ``~`` as you can see in the below example.
|
||||
|
||||
For more information about the Websocket support, see :ref:`server-side-websocket-support-scala`.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/WebsocketDirectivesExamplesSpec.scala
|
||||
:snippet: handle-multiple-protocols
|
||||
|
|
@ -7,3 +7,4 @@ WebsocketDirectives
|
|||
:maxdepth: 1
|
||||
|
||||
handleWebsocketMessages
|
||||
handleWebsocketMessagesForProtocol
|
||||
|
|
@ -103,7 +103,12 @@ was a WebSocket request. Otherwise, the directive rejects the request.
|
|||
|
||||
Here's the above simple request handler rewritten as a route:
|
||||
|
||||
.. includecode:: ../../code/docs/http/scaladsl/server/WebsocketExampleSpec.scala
|
||||
:include: websocket-routing
|
||||
.. 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
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ trait WebsocketDirectives {
|
|||
def extractOfferedWsProtocols: Directive1[immutable.Seq[String]] = extractUpgradeToWebsocket.map(_.requestedProtocols)
|
||||
|
||||
/**
|
||||
* Handles Websocket requests with the given handler and rejects other requests with a
|
||||
* Handles Websocket requests with the given handler and rejects other requests with an
|
||||
* [[ExpectedWebsocketRequestRejection]].
|
||||
*/
|
||||
def handleWebsocketMessages(handler: Flow[Message, Message, Any]): Route =
|
||||
|
|
@ -39,19 +39,19 @@ trait WebsocketDirectives {
|
|||
|
||||
/**
|
||||
* Handles Websocket requests with the given handler if the given subprotocol is offered in the request and
|
||||
* rejects other requests with a [[ExpectedWebsocketRequestRejection]] or a [[UnsupportedWebsocketSubprotocolRejection]].
|
||||
* rejects other requests with an [[ExpectedWebsocketRequestRejection]] or an [[UnsupportedWebsocketSubprotocolRejection]].
|
||||
*/
|
||||
def handleWebsocketMessagesForProtocol(handler: Flow[Message, Message, Any], subprotocol: String): Route =
|
||||
handleWebsocketMessagesForOptionalProtocol(handler, Some(subprotocol))
|
||||
|
||||
/**
|
||||
* Handles Websocket requests with the given handler and rejects other requests with a
|
||||
* Handles Websocket requests with the given handler and rejects other requests with an
|
||||
* [[ExpectedWebsocketRequestRejection]].
|
||||
*
|
||||
* If the `subprotocol` parameter is None any Websocket request is accepted. If the `subprotocol` parameter is
|
||||
* `Some(protocol)` a Websocket request is only accepted if the list of subprotocols supported by the client (as
|
||||
* announced in the Websocket request) contains `protocol`. If the client did not offer the protocol in question
|
||||
* the request is rejected with a [[UnsupportedWebsocketSubprotocolRejection]] rejection.
|
||||
* the request is rejected with an [[UnsupportedWebsocketSubprotocolRejection]] rejection.
|
||||
*
|
||||
* To support several subprotocols you may chain several `handleWebsocketMessage` Routes.
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue