akka/akka-http

Documentation examples for request-level client APIs are misleading

Open

#2,805 opened on Nov 7, 2019

 (5 comments) (0 reactions) (0 assignees)Scala (598 forks)batch import
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:

  1. It requests from http://akka.io, which redirects to https://akka.io, but it doesn't follow the redirect.
  2. It never terminates the connection pool or the ActorSystem, so the program doesn't exit.
  3. 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

Contributor guide