I am trying to write a websocket client application where I got to subscribe for an websocket URL i am using play-ws for the same. But getting the exception like below.
Exception in thread "main" java.io.IOException: WebSocket method must
be a GET
Dependency used:
"com.typesafe.play" %% "play-ws" % "2.4.0-M1"
Piece of code I used to get the websocket client is below,
trait PlayHelper {
val config = new NingAsyncHttpClientConfigBuilder(DefaultWSClientConfig()).build()
val builder = new AsyncHttpClientConfig.Builder(config)
val wsClient = new NingWSClient(builder.build())
def getBody(future: Future[WSResponse]) = {
val response = Await.result(future, Duration.Inf);
if (response.status != 200)
throw new Exception(response.statusText);
response.body
}
}
object Client extends PlayHelper with App{
def subscribe()={
val url = "ws://localhost:8080"
val body = getBody(wsClient.url(url).get())
Thread.sleep(1000)
println(s"body: $body")
}
subscribe()
}
Exception screen shot is below:
Looking for the help for this issue.
I don't think that play-ws supports websockets, you may want to use the AsyncHttpClient directly: https://github.com/AsyncHttpClient/async-http-client#websocket
Related
I have an application running a gRPC service alongside a simple Akka HTTP endpoint. I am following this guide: https://doc.akka.io/docs/akka-grpc/current/server/akka-http.html. The problem: when curling the HTTP endpoint I get 404 not found. I know it found the server because Akka-HTTP/10.2.5 is the server of the response header.
Some code:
object Server extends App {
val conf = ConfigFactory
.parseString("akka.http.server.preview.enable-http2 = on")
.withFallback(ConfigFactory.defaultApplication())
val system = ActorSystem("Interop", conf)
new Server(system).run()
}
class Server(system: ActorSystem) {
def run() = {
// implicit sys, ec...
val grpcService: HttpRequest => Future[HttpResponse] = ServiceHandler(new Service())
val greeter = get {
pathEndOrSingleSlash {
complete("I am alive")
}
}
// lifted this line straight out of the guide
val grpcRoute = { ctx => grpcService(ctx.request).map(RouteResult.Complete) }
val route = concat(greeter, grpcRoute)
val binding = Http().newServerAt("127.0.0.1", 8080).bind(route)
binding
}
}
When I take out the gRPC route, the greeter endpoint works as intended. Otherwise, when I curl http://localhost:8080, I get
*Mark bundle as not supporting multiuser
<HTTP/1.1 404 Not Found
<Server: akka-http/10.2.5
<other-stuff
I am using akka-gRPC 2.0.0
What should I do to ensure interop between the two routes?
gRPC uses HTTP/2 so try
curl --http2 http://localhost:8080
It should also get rid of the initial "Mark bundle..." message.
I have created WS client on play framework2.6 which connects to REST API. This rest API is continuously sending chunks/stream of data. But then for few seconds REST API server goes down and is again restarted.
I have written client to restart stream but it seems its not able to restart it.
def employesCompany() = Action.async { implicit request: Request[AnyContent] =>
val client = ws.underlying
val wsrequest: WSRequest = ws.url(playConfiguration.get[String]("employes.service.url"))
val futureResponse: Future[WSResponse] = wsrequest.stream()
futureResponse.flatMap { response =>
val source: Source[ByteString, Any] = response.bodyAsSource
val publisher = source.toMat(Sink.asPublisher(true))(Keep.right).run()
val flow = Flow[ByteString].map(res => res.utf8String)
val ns = Source.fromPublisher(publisher).via(flow)
val restartSource = RestartSource.withBackoff(Duration.apply(1, "sec"), Duration.apply(3, "sec"), 0.2) { () =>
ns.map {
elem =>
println(elem)
elem
}
}
Future.successful(Ok.chunked(restartSource via EventSource.flow).as(ContentTypes.EVENT_STREAM))
}
}
I am facing below error:
ERROR] [12/20/2020 18:35:27.585] [play-dev-mode-akka.actor.default-dispatcher-8] [RestartWithBackoffSource(akka://play-dev-mode)] **Restarting graph due to failure
java.io.IOException: An existing connection was forcibly closed by the remote host**
at sun.nio.ch.SocketDispatcher.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)
at sun.nio.ch.IOUtil.read(IOUtil.java:192)
at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:380)
at play.shaded.ahc.io.netty.buffer.UnpooledUnsafeDirectByteBuf.setBytes(UnpooledUnsafeDirectByteBuf.java:368)
at play.shaded.ahc.io.netty.buffer.AbstractByteBuf.writeBytes(AbstractByteBuf.java:891)
at play.shaded.ahc.io.netty.channel.socket.nio.NioSocketChannel.doReadBytes(NioSocketChannel.java:277)
at play.shaded.ahc.io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:119)
at play.shaded.ahc.io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:646)
at play.shaded.ahc.io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:581)
at play.shaded.ahc.io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:498)
at play.shaded.ahc.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:460)
at play.shaded.ahc.io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:131)
at play.shaded.ahc.io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
at java.lang.Thread.run(Thread.java:748)
The problem is that the request is already performed when you call wsrequest.stream(), but you want it to be performed later when the Source is materialized. You therefore need to delay the creation of the Future by not calling wsrequest.stream yourself but having Akka Stream do it.
I haven't worked with Akka Stream in a while, but something like this should work:
val responseStream = Source.lazyFuture(() => wsrequest.stream())
responseStream.flatMapConcat { response =>
// the rest like before
Any ideas on how to best test an Akka Stream containing an Akka Http Flow? I'm struggling with the following method in particular:
def akkaHttpFlow(server: String)(implicit actorSystem: ActorSystem, actorMaterializer: ActorMaterializer) = {
val uri = new java.net.URI(server)
val port: Int = if( uri.getPort != -1) { uri.getPort } else { 80 }
Http().cachedHostConnectionPool[Seq[String]](uri.getHost, port)
.withAttributes(ActorAttributes.supervisionStrategy(decider))
}
This is the test code
val emails = Set("tonymurphy#example.com")
val source: Source[String, NotUsed] = Source(emails)
val f = source
.grouped(10)
.via(requestBuilderFlow)
.via(akkaHttpFlow)
.map(responseHandler)
.runForeach(println)
f.futureValue.shouldBe(Done)
It fails with the following error (not unexpected tbh) >>>
The future returned an exception of type: akka.stream.StreamTcpException, with message: Tcp command [Connect(localhost:9001,None,List(),Some(10 seconds),true)] failed because of Connection refused.
Would it be possible to embed akka http server in the test? Or how best could I structure the code to be able to mock it?
The supporting code
object MyOperations extends StrictLogging {
val requestBuilderFunc : Seq[String] => (HttpRequest, Seq[String]) = { emails : Seq[String] =>
HttpRequest(method = HttpMethods.POST, uri = "/subscribers").withEntity(ContentTypes.`application/json`, ByteString(Json.toJson(emails).toString())) -> emails.toVector
}
val requestBuilderFlow : Flow[Seq[String],(HttpRequest, Seq[String]),NotUsed] = Flow[Seq[String]] map requestBuilderFunc
val responseHandler: ((Try[HttpResponse], Seq[String])) => (HttpResponse, Seq[String]) = {
case (responseTry, context) =>
logger.debug(s"Response: $responseTry")
(responseTry.get, context.asInstanceOf[Seq[String]])
}
}
I have to admit I'm struggling with how to organise my scala applications into objects, traits, classes, higher order functions etc and test them
What you'll want to do is use something like dependency injection to inject a Flow[(HttpRequest, Seq[String]), (Try[HttpResponse], Seq[String]), Any].
In production that flow will be from akka http, but in test you can mock it yourself to return whatever you need.
I am working on Play framework 2.5.3 and Akka 2.4.7 using Scala. I have an application made in play based webSockets using Akka. I am using the project as a jar in another project.
My code in new project looks like this:
For accepting connections:
val postActor = actorSystem.actorOf(Props[PostActorClass])
def wsSocket = WebSocket.accept[JsValue,JsValue]{ request =>
ActorFlow.actorRef(out => wsConnObj.HandlerClass.props(out,postActor))
}
For getting references of all connections stored in my jar project:
def getConnReferences:Set[ActorRef] = {
implicit val timeout = Timeout(5 seconds)
val future = postActor ? GetReferences
val result = Await.result(future, timeout.duration).asInstanceOf[Set[ActorRef]]
(result)
}
For sending messages to all the references:
def sendMsg = {
val msg = Json.parse("""{"username":"Server","message":"Reached finally.","pub_key":"empty","target":"user"}""")
val refs = getConnReferences
refs.foreach( _ ! msg)
}
I am trying to use these methods in my new project's HomeController. But unable to work them together, like wsSocket should be executed , then sendMsg (which itself calls getConnReferences also).
How will I achieve that? If am calling all these blocks in single action method, I can't establish connection even.
Code for WebSocketConnect class:
class WebSocketConnect #Inject() (cache:CacheApi) (implicit actorSystem: ActorSystem,materializer: Materializer){
object HandlerClass {
homelogger.info(logMessages.passingActorRef)
def props(out: ActorRef, postActor: ActorRef) = {
Props(new SocketHandlerClass(out, postActor, cache, postActorToUsernameMap))
}
}
}
Im trying to parse some data from an API
I have a recursion method that calling to this method
def getJsonValue( url: (String)): JsValue = {
val builder = new com.ning.http.client.AsyncHttpClientConfig.Builder()
val client = new play.api.libs.ws.ning.NingWSClient(builder.build())
val newUrl = url.replace("\"", "").replace("|", "%7C").trim
val response: Future[WSResponse] = client.url(newUrl).get()
Await.result(response, Duration.create(10, "seconds")).json
}
Everything is working well but after 128 method calls i'm getting this warning
WARNING: You are creating too many HashedWheelTimer instances. HashedWheelTimer is a shared resource that must be reused across the application, so that only a few instances are created.
After about 20 More calls im getting this exception
23:24:57.425 [main] ERROR com.ning.http.client.AsyncHttpClient - Unable to instantiate provider com.ning.http.client.providers.netty.NettyAsyncHttpProvider. Trying other providers.
23:24:57.438 [main] ERROR com.ning.http.client.AsyncHttpClient - org.jboss.netty.channel.ChannelException: Failed to create a selector.
Questions
1.Im assuming that the connections didnt closed ?? and therefore i can't create new connections.
2.What will be the correct and the safe way to create those HTTP calls
Had the same problem.
Found 2 interesting solutions:
make sure you are not creating tons of clients with closing them
the threadPool you are using may be causing this.
My piece of code (commenting that line of code solved, I'm now testing several configurations):
private[this] def withClient(block: NingWSClient => WSResponse): Try[WSResponse] = {
val config = new NingAsyncHttpClientConfigBuilder().build()
val clientConfig = new AsyncHttpClientConfig.Builder(config)
// .setExecutorService(new ThreadPoolExecutor(5, 15, 30L, TimeUnit.SECONDS, new SynchronousQueue[Runnable]))
.build()
val client = new NingWSClient(clientConfig)
val result = Try(block(client))
client.close()
result
}
for avoiding this you can use different provider.
private AsyncHttpProvider httpProvider =new ApacheAsyncHttpProvider(config);
private AsyncHttpClient asyncHttpClient = new AsyncHttpClient(httpProvider,config);
I ran into this same problem. Before you call your recursive method, you should create builder and client and pass client to the recursive method, as well as getJsonValue. This is what getJsonValue should look like:
def getJsonValue(url: String, client: NingWSClient): JsValue = {
val builder = new com.ning.http.client.AsyncHttpClientConfig.Builder()
val client = new play.api.libs.ws.ning.NingWSClient(builder.build())
val newUrl = url.replace("\"", "").replace("|", "%7C").trim
val response: Future[WSResponse] = client.url(newUrl).get()
Await.result(response, Duration.create(10, "seconds")).json
}