akka/akka-http
Documentation examples for request-level client APIs are misleading
Open
#2,805 opened on Nov 7, 2019
1 - triagedhelp wantedt:docs
Repository metrics
- Stars
- (1,311 stars)
- PR merge metrics
- (Avg merge 1d 10h) (2 merged PRs in 30d)
Description
The first example of using the request-level API has a few issues when you try to run it:
- It requests from http://akka.io, which redirects to https://akka.io, but it doesn't follow the redirect.
- It never terminates the connection pool or the
ActorSystem, so the program doesn't exit. - It doesn't consume the response, so it prints a warning and an error: (see also #2595)
[2019-11-08 09:12:47,184] [WARN] [akka.http.impl.engine.client.PoolGateway] [default-akka.actor.default-dispatcher-6] [default/Pool(shared->http://akka.io:80)] - [0 (WaitingForResponseEntitySubscription)] Response entity was not subscribed after 1 second. Make sure to read the response entity body or call `discardBytes()` on it. GET / Empty -> 301 Moved Permanently Chunked [2019-11-08 09:12:47,204] [ERROR] [akka.actor.ActorSystemImpl] [default-akka.actor.default-dispatcher-6] [akka.actor.ActorSystemImpl(default)] - Outgoing request stream error java.util.concurrent.TimeoutException: Response entity was not subscribed after 1 second. Make sure to read the response entity body or call `discardBytes()` on it. GET / Empty -> 301 Moved Permanently Chunked at akka.http.impl.engine.client.pool.SlotState$WaitingForResponseEntitySubscription.onTimeout(SlotState.scala:317) at akka.http.impl.engine.client.pool.NewHostConnectionPool$HostConnectionPoolStage$$anon$1$Event$.$anonfun$onTimeout$1(NewHostConnectionPool.scala:169) at akka.http.impl.engine.client.pool.NewHostConnectionPool$HostConnectionPoolStage$$anon$1$Event$.$anonfun$event0$1(NewHostConnectionPool.scala:171) at akka.http.impl.engine.client.pool.NewHostConnectionPool$HostConnectionPoolStage$$anon$1$Slot.runOneTransition$1(NewHostConnectionPool.scala:253) at akka.http.impl.engine.client.pool.NewHostConnectionPool$HostConnectionPoolStage$$anon$1$Slot.loop$1(NewHostConnectionPool.scala:335) at akka.http.impl.engine.client.pool.NewHostConnectionPool$HostConnectionPoolStage$$anon$1$Slot.updateState(NewHostConnectionPool.scala:344) at akka.http.impl.engine.client.pool.NewHostConnectionPool$HostConnectionPoolStage$$anon$1$Slot.updateState(NewHostConnectionPool.scala:243) at akka.http.impl.engine.client.pool.NewHostConnectionPool$HostConnectionPoolStage$$anon$1$Slot.$anonfun$updateState$1(NewHostConnectionPool.scala:264) at akka.http.impl.engine.client.pool.NewHostConnectionPool$HostConnectionPoolStage$$anon$1.$anonfun$safeCallback$1(NewHostConnectionPool.scala:591) at akka.http.impl.engine.client.pool.NewHostConnectionPool$HostConnectionPoolStage$$anon$1.$anonfun$safeCallback$1$adapted(NewHostConnectionPool.scala:591) at akka.stream.impl.fusing.GraphInterpreter.runAsyncInput(GraphInterpreter.scala:466) at akka.stream.impl.fusing.GraphInterpreterShell$AsyncInput.execute(ActorGraphInterpreter.scala:497) at akka.stream.impl.fusing.GraphInterpreterShell.processEvent(ActorGraphInterpreter.scala:599) at akka.stream.impl.fusing.ActorGraphInterpreter.akka$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:768) at akka.stream.impl.fusing.ActorGraphInterpreter$$anonfun$receive$1.applyOrElse(ActorGraphInterpreter.scala:783) at akka.actor.Actor.aroundReceive(Actor.scala:532) at akka.actor.Actor.aroundReceive$(Actor.scala:530) at akka.stream.impl.fusing.ActorGraphInterpreter.aroundReceive(ActorGraphInterpreter.scala:690) at akka.actor.ActorCell.receiveMessage(ActorCell.scala:573) at akka.actor.ActorCell.invoke(ActorCell.scala:543) at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:269) at akka.dispatch.Mailbox.run(Mailbox.scala:230) at akka.dispatch.Mailbox.exec(Mailbox.scala:242) at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289) at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056) at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692) at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Other examples on this page also have some of these problems.
I got this code to work as expected (but maybe it's not he best way to write it):
def main(args: Array[String]): Unit = {
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
// needed for the future flatMap/onComplete in the end
implicit val executionContext = system.dispatcher
val responseFuture: Future[Terminated] =
Http()
.singleRequest(HttpRequest(uri = "https://akka.io"))
.flatMap(res => res.entity.toStrict(5.seconds))
.andThen {
case Success(entity) => println(entity)
case Failure(e) => println("something went wrong: " + e)
}
.flatMap(_ => Http().shutdownAllConnectionPools())
.flatMap(_ => system.terminate())
Await.ready(responseFuture, 60.seconds)
}
There was still one error printed at the end of the output that I don't know how to prevent:
[2019-11-08 09:03:20,729] [INFO] [akka.http.impl.engine.client.PoolGateway] [default-akka.actor.default-dispatcher-5] [default/Pool(shared->https://akka.io:443)] - Pool is now shutting down as requested.
[2019-11-08 09:03:20,738] [ERROR] [akka.actor.ActorSystemImpl] [default-akka.actor.default-dispatcher-5] [akka.actor.ActorSystemImpl(default)] - Outgoing request stream error
akka.http.impl.engine.client.pool.NewHostConnectionPool$HostConnectionPoolStage$$anon$1$Slot$$anon$2: Pool slot was shut down
Reference
Reported in Gitter: https://gitter.im/akka/akka?at=5dc492d4a3f0b17849063bc2