Play Framework - edit Sec-WebSocket-Protocol in Web socket Response Header - scala

Edit the Web socket header response sent from server to client.
I am creating a websocket server application using playframework. Right now the websocket response from the server is
taken care by Play. Following is the response header,
Request Header:
(UpgradeToWebSocket,),
(Host,localhost:8083),
(Connection,Upgrade),
(Upgrade,websocket),
(Sec-WebSocket-Version,13),
(Accept-Encoding,gzip, deflate, br),
(Accept-Language,en-US,en;q=0.9),
(Sec-WebSocket-Key,ZvfzpVo3EX4DFA4BRcgRIA==)
def chatSystem(): WebSocket = WebSocket.acceptOrResult[String, String] { request =>
Future.successful{
AuthenticationService.doBasicAuthentication(request.headers) match {
case Results.Ok => Right(ActorFlow.actorRef { out => ChatServiceActor.props(out) })
case _ => Left(Unauthorized)
}
}
}
I want to validate Sec-WebSocket-Protocol if it is present in the request header or add the same with value in the server response if it is not present.

I used the following code:
// Defined at http://tools.ietf.org/html/rfc6455#section-4.2.2
val MagicGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
def websocketAcceptForKey(key: String): String = {
val sha1 = MessageDigest.getInstance("sha1")
val salted = key + MagicGuid
val hash = sha1.digest(salted.asciiBytes)
val acceptKey: String = Base64.rfc2045().encodeToString(hash, false)
acceptKey
}
Use it like the following:
val wsKey: Optional[HttpHeader] = request.getHeader("Sec-WebSocket-Key")
val wsAccept = if (wsKey.isPresent) Some(RawHeader("Sec-WebSocket-Accept", websocketAcceptForKey(wsKey.get.value()))) else None

Related

How can I parameterise information in Gatling scenarios

I need to send specific parameters to a scenario that is being reused multiple times with different payloads depending on the workflows. The following is the code that is to be reused:
var reqName = ""
var payloadName = ""
lazy val sendInfo: ScenarioBuilder = scenario("Send info")
.exec(session => {
reqName = session("localReqName").as[String]
payloadName = session("localPayloadName").as[String]
session}
)
.exec(jms(s"$reqName")
.send
.queue(simQueue)
.textMessage(ElFileBody(s"$payloadName.json"))
)
.exec(session => {
val filePath = s"$payloadName"
val body = new String(Files.readAllBytes(Paths.get(ClassLoader.getSystemResource(filePath).toURI)))
logger.info("timestamp value: " + session("timestamp").as[String])
logger.debug("Template body:\n " + body)
session
})
I know that you can chain scenarios in Scala/Gatling but how can I pass in information like reqName and payloadName down the chain, where reqName is a parameter to indicate the name of the request where the info is being sent and payloadName is the name of the actual JSON payload for the related request:
lazy val randomInfoSend: ScenarioBuilder = scenario("Send random info payloads")
.feed(csv("IDfile.csv").circular)
.randomSwitch(
(randomInfoType1prob) -> exec(
feed(timeFeeder)
.exec(session => {
payloadName = "Info1payload.json"
reqName ="Info1Send"
session.set("localReqName", "Info1Send")
session.set("localPayloadName", "Info1payload.json")
session
})
.exec(sendInfo)
),
(100.0 - randomInfoType1prob) -> exec(
feed(timeFeeder)
.exec(session => {
payloadName = "Info2Payload.json"
reqName ="Info2Send"
session.set("localReqName", "Info2Send")
session.set("localPayloadName", "Info2Payload.json")
session
})
.exec(sendInfo)
)
I attempted the above but the values of that 2 specific parameters were not passed through correctly. (The IDs and timestamps were fed through correctly though) Any suggestions?
Please properly read the Session API documentation. Session is immutable so Session#set returns a new instance.

How to use http4s server and client library as a proxy?

I want use http4s as proxy(like nginx), how to forward all data from my http4s server to another http server?
What I really want do is append a verify function on every request before do forward function. Hopefully like this:
HttpService[IO] {
case request =>
val httpClient: Client[IO] = Http1Client[IO]().unsafeRunSync
if(verifySuccess(request)) { // forward all http data to host2 and
// get a http response.
val result = httpClient.forward(request, "http://host2")
result
} else {
Forbidden //403
}
}
How to do this with http4s and it's client?
Thanks
Updated
with the help of #TheInnerLight, I give it a try with the snippet code:
val httpClient = Http1Client[IO]()
val service: HttpService[IO] = HttpService[IO] {
case req =>
if(true) {
for {
client <- httpClient
newAuthority = req.uri.authority.map(_.copy(host = RegName("scala-lang.org"), port = Some(80)))
proxiedReq = req.withUri(req.uri.copy(authority = newAuthority))
response <- client.fetch(proxiedReq)(IO.pure(_))
} yield response
} else {
Forbidden("Some forbidden message...")
}
}
With a request: http://localhost:28080(http4s server listen at 28080):
but occurred a error:
[ERROR] org.http4s.client.PoolManager:102 - Error establishing client connection for key RequestKey(Scheme(http),localhost)
java.net.ConnectException: Connection refused
at sun.nio.ch.UnixAsynchronousSocketChannelImpl.checkConnect(Native Method)
at sun.nio.ch.UnixAsynchronousSocketChannelImpl.finishConnect(UnixAsynchronousSocketChannelImpl.java:252)
at sun.nio.ch.UnixAsynchronousSocketChannelImpl.finish(UnixAsynchronousSocketChannelImpl.java:198)
at sun.nio.ch.UnixAsynchronousSocketChannelImpl.onEvent(UnixAsynchronousSocketChannelImpl.java:213)
at sun.nio.ch.KQueuePort$EventHandlerTask.run(KQueuePort.java:301)
at java.lang.Thread.run(Thread.java:748)
[ERROR] org.http4s.server.service-errors:88 - Error servicing request: GET / from 0:0:0:0:0:0:0:1
java.net.ConnectException: Connection refused
at sun.nio.ch.UnixAsynchronousSocketChannelImpl.checkConnect(Native Method)
at sun.nio.ch.UnixAsynchronousSocketChannelImpl.finishConnect(UnixAsynchronousSocketChannelImpl.java:252)
at sun.nio.ch.UnixAsynchronousSocketChannelImpl.finish(UnixAsynchronousSocketChannelImpl.java:198)
at sun.nio.ch.UnixAsynchronousSocketChannelImpl.onEvent(UnixAsynchronousSocketChannelImpl.java:213)
at sun.nio.ch.KQueuePort$EventHandlerTask.run(KQueuePort.java:301)
at java.lang.Thread.run(Thread.java:748)
Latest Version
val httpClient: IO[Client[IO]] = Http1Client[IO]()
override val service: HttpService[IO] = HttpService[IO] {
case req =>
val hostName = "scala-lang.org"
val myPort = 80
if(true) {
val newHeaders = {
val filterHeader = req.headers.filterNot{h =>
h.name == CaseInsensitiveString("Connection") ||
h.name == CaseInsensitiveString("Keep-Alive") ||
h.name == CaseInsensitiveString("Proxy-Authenticate") ||
h.name == CaseInsensitiveString("Proxy-Authorization") ||
h.name == CaseInsensitiveString("TE") ||
h.name == CaseInsensitiveString("Trailer") ||
h.name == CaseInsensitiveString("Transfer-Encoding") ||
h.name == CaseInsensitiveString("Upgrade")
}
filterHeader.put(Header("host", hostName))
}
for {
client <- httpClient
newAuthority = req.uri.authority
.map(_.copy(host = RegName(hostName), port = Some(myPort)))
.getOrElse( Authority(host = RegName(hostName), port = Some(myPort)))
proxiedReq = req.withUri(req.uri.copy(authority = Some(newAuthority)))
.withHeaders(newHeaders)
response <- client.fetch(proxiedReq)(x => IO.pure(x))
} yield {
val rst = response
rst
}
} else {
Forbidden("Some forbidden message...")
}
}
It works fine enough for my REST API web server.
There are some error when proxy scala-lang.org for test:
[ERROR] org.http4s.blaze.pipeline.Stage:226 - Error writing body
org.http4s.InvalidBodyException: Received premature EOF.
How about something like this:
HttpService[IO] {
case req =>
if(verifyRequest(req)) {
for {
client <- Http1Client[IO]()
newHost = "host2"
newAuthority = Authority(host = RegName("host2"), port = Some(80))
proxiedReq =
req.withUri(req.uri.copy(authority = Some(newAuthority)))
.withHeaders(req.headers.put(Header("host", newHost)))
response <- client.fetch(proxiedReq)(IO.pure(_))
} yield response
} else {
Forbidden("Some forbidden message...")
}
}
Note that you should definitely avoid littering your code with calls tounsafeRunSync. You should generally be using it at most once in your program (in Main). In other circumstances, you should focus on lifting the effects into the monad you're working in.

Scala/Spray/Akka unable to leverage mapRequest

I am new to Scala/Spray/AKKA so please excuse this dumb requests.
I have the following Directive and it is being called as the first
logger line ("inside") is showing up in logs.
However, anything inside mapRequest{} is skipped over. The logging line ("headers:") isn't showing up
private def directiveToGetHeaders(input: String) : Directive0 = {
logger.info("inside")
mapRequest { request =>
val headList: List[HttpHeader] = request.headers
logger.info("headers: " + headList.size)
request
}
}
I am not sure what I did wrong. My goal is to pull out all the HTTP headers. Any tip/pointer much appreciated. Thanks
-v
You can use extractRequest directive for getting headers.
private def directiveToGetHeaders(input: String) : Directive0 = {
logger.info("inside")
extractRequest { request =>
val headList: Seq[HttpHeader] = request.headers
logger.info("headers: " + headList.size)
complete(HttpResponse())
}
}

How to check for 200 OK response status using Scala and Play Framework

I have a following Actor class that is responsible for sending a JSON message to a URL using POST.
import play.api.libs.ws._
class Worker extends Actor {
val logger: Logger = Logger("superman")
val supermanURL = "http://localhost:9000/superman/send"
def receive = {
case message: JsValue => {
val transactionID = (message \ "transactionID").get
println("Received JSON Object =>" + message)
val responseFromSuperman = WS.url(supermanURL).withHeaders("Content-Type" -> "application/json").post(message)
responseFromSuperman.map(
result => {
//TODO: Make sure to only log if response status is 200 OK
logger.info("""Message="ACK received from Superman" for transactionID=""" + transactionID)}
).recover { case error: Throwable =>
logger.error("""Message="NACK received from Superman" for transactionID=""" + transactionID) + " errorMessage:" + error.getLocalizedMessage()
}
}
}
}
So, if you look into my TODO above, I would like to add a check for a response type 200 OK. The current implementation is not doing that and it logs the message even if I manually send in a BadRequest. I tried checking for result.allHeaders which returns:
Map(Date -> Buffer(Wed, 27 Jan 2016 21:45:31 GMT), Content-Type -> Buffer(text/plain; charset=utf-8), Content-Length -> Buffer(7))
but no information about response status 200 OK
Simply:
import play.api.http.Status
if(result.status == Status.OK) {
// ...
}
Maybe I am missing here something but you have "status" on the response.
So you can do:
WS.url(url).withHeaders("Content-Type" -> "application/json").post(message).map{
case response if ( response.status == OK) => //DO SOMETHING?
}

Scala with Play Framework 2.3.6: sending a message to all socket clients

I'm new to Scala and the Play Framework, so I'm experimenting a bit.
I've successfully created websockets, but I'd like to be able to send a message to multiple socket clients from a simple POST request.
For instance, I have 10 different random browsers connected to my socket (ws:// ... /websocket), and I can myself send a "POST: HELLO" to /newMessage. How do I make so this "HELLO" gets sent to each of the 10 clients ?
Here's the controller receiving the HELLO. Works fine and prints "Got: AnyContentAsText(HELLO)" :
def newMessage = Action { implicit request =>
println("Got: " + request.body)
/* add something here to send "request.body" to every socket client */
Ok("Got: " + request.body)
}
And here's my simple "Socket" controller that sends "Welcome" to connected clients :
object Socket extends Controller {
def txSocket = WebSocket.using[String] { request =>
// Log events to the console
val in = Iteratee.foreach[String](println).map { _ =>
println("Disconnected")
}
// Send a single 'Welcome!' message
val out = Enumerator("Welcome!")
(in, out)
}
}
How can I, from my "Message" controller, send request.body to the websocket ?
Thank you for your time !
Each websocket connection makes a new actor. You need to select the actors and send the message to them.
Like so.
object Application extends Controller {
def connect = WebSocket.acceptWithActor[JsValue, JsValue] { request => out =>
ClientActor.props(out)
}
def broadcast = Action { _ =>
system.actorSelection("akka://application/system/websockets/*/handler") ! "msg"
Ok
}
def system = play.api.libs.concurrent.Akka.system(play.api.Play.current)
}