This is an automated email from the ASF dual-hosted git repository.

He-Pin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko-http.git


The following commit(s) were added to refs/heads/main by this push:
     new 454e08d3b refactor: replace deprecated extends App with explicit main 
method (#1131)
454e08d3b is described below

commit 454e08d3b51474d0b9e0b479c98adb0af83ca7a7
Author: He-Pin(kerr) <[email protected]>
AuthorDate: Tue Jul 7 00:40:42 2026 +0800

    refactor: replace deprecated extends App with explicit main method (#1131)
    
    * refactor: replace deprecated `extends App` with explicit main method
    
    Motivation:
    `extends App` is deprecated in Scala 3. Replace with explicit `def main`
    method for forward compatibility.
    
    Modification:
    Replace `object X extends App { ... }` with
    `object X { def main(args: Array[String]): Unit = { ... } }`
    in 14 test and example files.
    
    Result:
    No more usage of deprecated `scala.App` trait. Code is compatible with 
Scala 3.
    
    Tests:
    Not run - docs only
    
    References:
    None - Scala 3 compatibility
    
    * fix: fix forward reference errors in Http2ServerTest and TestServer
    
    Motivation:
    Moving lazy vals from object-level members into def main() body
    introduced forward reference compilation errors since local vals
    cannot reference later definitions in the same block.
    
    Modification:
    Move lazy val index/imagePage and def imagesBlock definitions before
    the code that references them (syncHandler/routes).
    
    Result:
    No more forward reference compilation errors.
    
    Tests:
    sbt "http2-tests / Test / compile" "http-tests / Test / compile" - success
    
    References:
    Fixes CI failures in PR #1131
    
    * fix: avoid forward reference after removing extends App
    
    Motivation:
    PR 1131 replaces deprecated Scala extends App usage with explicit main 
methods, but moving Http2ClientApp logic into main turned the singleRequest 
helper into a local method used before its definition.
    
    Modification:
    Move singleRequest back to object scope as a private helper while keeping 
the explicit main method.
    
    Result:
    Http2ClientApp keeps the PR's explicit main entry point and compiles 
without the local forward reference error.
    
    Tests:
    - rtk sbt "++2.13.18!" "docs / Test / compile" - passed
    - rtk sbt "++3.3.8!" "docs / Test / compile" - passed
    - rtk sbt "++3.8.4!" "docs / Test / compile" - passed
    - rtk scalafmt --mode diff-ref=origin/main - passed
    - rtk scalafmt --list --mode diff-ref=origin/main - passed
    - rtk git diff --check - passed
    - rtk sbt "docs / Test / headerCheck" - passed
    
    References:
    Refs #1131
---
 .../scala/docs/http/scaladsl/Http2ClientApp.scala  |  79 +++---
 .../server/ExceptionHandlerExamplesSpec.scala      |  40 +--
 .../server/RejectionHandlerExamplesSpec.scala      |  58 ++--
 .../directives/JsonStreamingFullExamples.scala     |  54 ++--
 .../engine/parsing/HttpHeaderParserTestBed.scala   |  56 ++--
 .../http/impl/engine/ws/EchoTestClientApp.scala    |  78 +++---
 .../http/impl/engine/ws/WSClientAutobahnTest.scala | 306 +++++++++++----------
 .../http/impl/engine/ws/WSServerAutobahnTest.scala |  56 ++--
 .../apache/pekko/http/scaladsl/TestClient.scala    | 168 +++++------
 .../apache/pekko/http/scaladsl/TestServer.scala    | 116 ++++----
 .../pekko/http/scaladsl/TestSingleRequest.scala    |  34 +--
 .../pekko/http/scaladsl/server/TcpLeakApp.scala    |  73 ++---
 .../pekko/http/scaladsl/server/TestServer.scala    | 178 ++++++------
 .../pekko/http/scaladsl/Http2ServerTest.scala      | 202 +++++++-------
 14 files changed, 766 insertions(+), 732 deletions(-)

diff --git a/docs/src/test/scala/docs/http/scaladsl/Http2ClientApp.scala 
b/docs/src/test/scala/docs/http/scaladsl/Http2ClientApp.scala
index af3d841ef..52d084250 100644
--- a/docs/src/test/scala/docs/http/scaladsl/Http2ClientApp.scala
+++ b/docs/src/test/scala/docs/http/scaladsl/Http2ClientApp.scala
@@ -34,50 +34,54 @@ import scala.concurrent.Future
 import scala.concurrent.Promise
 
 /** A small example app that shows how to use the HTTP/2 client API currently 
against actual internet servers */
-object Http2ClientApp extends App {
-  val config =
-    ConfigFactory.parseString(
-      """
-         # pekko.loglevel = debug
-         pekko.http.client.http2.log-frames = true
-         pekko.http.client.parsing.max-content-length = 20m
-      """).withFallback(ConfigFactory.defaultApplication())
-
-  implicit val system: ActorSystem = ActorSystem("Http2ClientApp", config)
-  implicit val ec: ExecutionContext = system.dispatcher
+object Http2ClientApp {
+  def main(args: Array[String]): Unit = {
+    val config =
+      ConfigFactory.parseString(
+        """
+           # pekko.loglevel = debug
+           pekko.http.client.http2.log-frames = true
+           pekko.http.client.parsing.max-content-length = 20m
+        """).withFallback(ConfigFactory.defaultApplication())
+
+    implicit val system: ActorSystem = ActorSystem("Http2ClientApp", config)
+    implicit val ec: ExecutionContext = system.dispatcher
+
+    // #response-future-association
+    val dispatch = 
singleRequest(Http().connectionTo("pekko.apache.org").http2())
+
+    dispatch(
+      HttpRequest(
+        uri = 
"https://pekko.apache.org/api/pekko/current/org/apache/pekko/actor/typed/scaladsl/index.html";,
+        headers = headers.`Accept-Encoding`(HttpEncodings.gzip) :: 
Nil)).onComplete { res =>
+      println(s"[1] Got index.html: $res")
+      res.get.entity.dataBytes.runWith(Sink.ignore).onComplete(res => 
println(s"Finished reading [1] $res"))
+    }
 
-  // #response-future-association
-  val dispatch = singleRequest(Http().connectionTo("pekko.apache.org").http2())
-
-  dispatch(
-    HttpRequest(
-      uri = 
"https://pekko.apache.org/api/pekko/current/org/apache/pekko/actor/typed/scaladsl/index.html";,
-      headers = headers.`Accept-Encoding`(HttpEncodings.gzip) :: 
Nil)).onComplete { res =>
-    println(s"[1] Got index.html: $res")
-    res.get.entity.dataBytes.runWith(Sink.ignore).onComplete(res => 
println(s"Finished reading [1] $res"))
-  }
+    // #response-future-association
 
-  // #response-future-association
+    dispatch(
+      HttpRequest(
+        uri = "https://pekko.apache.org/api/pekko/current/index.js";,
+        headers = /*headers.`Accept-Encoding`(HttpEncodings.gzip) ::*/ 
Nil)).onComplete { res =>
+      println(s"[2] Got index.js: $res")
+      res.get.entity.dataBytes.runWith(Sink.ignore).onComplete(res => 
println(s"Finished reading [2] $res"))
+    }
 
-  dispatch(
-    HttpRequest(
-      uri = "https://pekko.apache.org/api/pekko/current/index.js";,
-      headers = /*headers.`Accept-Encoding`(HttpEncodings.gzip) ::*/ 
Nil)).onComplete { res =>
-    println(s"[2] Got index.js: $res")
-    res.get.entity.dataBytes.runWith(Sink.ignore).onComplete(res => 
println(s"Finished reading [2] $res"))
-  }
+    dispatch(HttpRequest(uri = 
"https://pekko.apache.org/api/pekko/current/lib/MaterialIcons-Regular.woff";))
+      .flatMap(_.toStrict(1.second))
+      .onComplete(res => println(s"[3] Got font: $res"))
 
-  dispatch(HttpRequest(uri = 
"https://pekko.apache.org/api/pekko/current/lib/MaterialIcons-Regular.woff";))
-    .flatMap(_.toStrict(1.second))
-    .onComplete(res => println(s"[3] Got font: $res"))
+    dispatch(HttpRequest(uri = "https://pekko.apache.org/favicon.ico";))
+      .flatMap(_.toStrict(1.second))
+      .onComplete(res => println(s"[4] Got favicon: $res"))
 
-  dispatch(HttpRequest(uri = "https://pekko.apache.org/favicon.ico";))
-    .flatMap(_.toStrict(1.second))
-    .onComplete(res => println(s"[4] Got favicon: $res"))
+  }
 
   // #response-future-association
-  def singleRequest(
-      connection: Flow[HttpRequest, HttpResponse, Any], bufferSize: Int = 
100): HttpRequest => Future[HttpResponse] = {
+  private def singleRequest(
+      connection: Flow[HttpRequest, HttpResponse, Any], bufferSize: Int = 
100)(implicit system: ActorSystem)
+      : HttpRequest => Future[HttpResponse] = {
     val queue =
       Source.queue(bufferSize)
         .via(connection)
@@ -102,5 +106,4 @@ object Http2ClientApp extends App {
     }
   }
   // #response-future-association
-
 }
diff --git 
a/docs/src/test/scala/docs/http/scaladsl/server/ExceptionHandlerExamplesSpec.scala
 
b/docs/src/test/scala/docs/http/scaladsl/server/ExceptionHandlerExamplesSpec.scala
index dc5aeb546..992d9f0b7 100644
--- 
a/docs/src/test/scala/docs/http/scaladsl/server/ExceptionHandlerExamplesSpec.scala
+++ 
b/docs/src/test/scala/docs/http/scaladsl/server/ExceptionHandlerExamplesSpec.scala
@@ -39,17 +39,19 @@ object MyExplicitExceptionHandler {
       }
   }
 
-  object MyApp extends App {
+  object MyApp {
+    def main(args: Array[String]): Unit = {
 
-    implicit val system: ActorSystem = ActorSystem()
+      implicit val system: ActorSystem = ActorSystem()
 
-    val route: Route =
-      handleExceptions(myExceptionHandler) {
-        // ... some route structure
-        null // #hide
-      }
+      val route: Route =
+        handleExceptions(myExceptionHandler) {
+          // ... some route structure
+          null // #hide
+        }
 
-    Http().newServerAt("localhost", 8080).bind(route)
+      Http().newServerAt("localhost", 8080).bind(route)
+    }
   }
 
   //#explicit-handler-example
@@ -75,15 +77,17 @@ object MyImplicitExceptionHandler {
         }
     }
 
-  object MyApp extends App {
+  object MyApp {
+    def main(args: Array[String]): Unit = {
 
-    implicit val system: ActorSystem = ActorSystem()
+      implicit val system: ActorSystem = ActorSystem()
 
-    val route: Route =
-    // ... some route structure
-      null // #hide
+      val route: Route =
+      // ... some route structure
+        null // #hide
 
-    Http().newServerAt("localhost", 8080).bind(route)
+      Http().newServerAt("localhost", 8080).bind(route)
+    }
   }
 
   //#implicit-handler-example
@@ -162,10 +166,12 @@ object RespondWithHeaderExceptionHandlerExample {
       }
   }
 
-  object MyApp extends App {
-    implicit val system: ActorSystem = ActorSystem()
+  object MyApp {
+    def main(args: Array[String]): Unit = {
+      implicit val system: ActorSystem = ActorSystem()
 
-    Http().newServerAt("localhost", 8080).bind(route)
+      Http().newServerAt("localhost", 8080).bind(route)
+    }
   }
   //#respond-with-header-exceptionhandler-example
 }
diff --git 
a/docs/src/test/scala/docs/http/scaladsl/server/RejectionHandlerExamplesSpec.scala
 
b/docs/src/test/scala/docs/http/scaladsl/server/RejectionHandlerExamplesSpec.scala
index dc06f9c01..2c33e0290 100644
--- 
a/docs/src/test/scala/docs/http/scaladsl/server/RejectionHandlerExamplesSpec.scala
+++ 
b/docs/src/test/scala/docs/http/scaladsl/server/RejectionHandlerExamplesSpec.scala
@@ -30,36 +30,38 @@ object MyRejectionHandler {
   import StatusCodes._
   import Directives._
 
-  object MyApp extends App {
-    def myRejectionHandler =
-      RejectionHandler.newBuilder()
-        .handle {
-          case MissingCookieRejection(cookieName) =>
-            complete(HttpResponse(BadRequest, entity = "No cookies, no 
service!!!"))
-        }
-        .handle {
-          case AuthorizationFailedRejection =>
-            complete(Forbidden, "You're out of your depth!")
-        }
-        .handle {
-          case ValidationRejection(msg, _) =>
-            complete(InternalServerError, "That wasn't valid! " + msg)
-        }
-        .handleAll[MethodRejection] { methodRejections =>
-          val names = methodRejections.map(_.supported.name)
-          complete(MethodNotAllowed, s"Can't do that! Supported: 
${names.mkString(" or ")}!")
-        }
-        .handleNotFound { complete((NotFound, "Not here!")) }
-        .result()
-
-    implicit val system: ActorSystem = ActorSystem()
+  object MyApp {
+    def main(args: Array[String]): Unit = {
+      def myRejectionHandler =
+        RejectionHandler.newBuilder()
+          .handle {
+            case MissingCookieRejection(cookieName) =>
+              complete(HttpResponse(BadRequest, entity = "No cookies, no 
service!!!"))
+          }
+          .handle {
+            case AuthorizationFailedRejection =>
+              complete(Forbidden, "You're out of your depth!")
+          }
+          .handle {
+            case ValidationRejection(msg, _) =>
+              complete(InternalServerError, "That wasn't valid! " + msg)
+          }
+          .handleAll[MethodRejection] { methodRejections =>
+            val names = methodRejections.map(_.supported.name)
+            complete(MethodNotAllowed, s"Can't do that! Supported: 
${names.mkString(" or ")}!")
+          }
+          .handleNotFound { complete((NotFound, "Not here!")) }
+          .result()
+
+      implicit val system: ActorSystem = ActorSystem()
+
+      val route: Route = handleRejections(myRejectionHandler) {
+        // ... some route structure
+        null // #hide
+      }
 
-    val route: Route = handleRejections(myRejectionHandler) {
-      // ... some route structure
-      null // #hide
+      Http().newServerAt("localhost", 8080).bind(route)
     }
-
-    Http().newServerAt("localhost", 8080).bind(route)
   }
   // #custom-handler-example
 }
diff --git 
a/docs/src/test/scala/docs/http/scaladsl/server/directives/JsonStreamingFullExamples.scala
 
b/docs/src/test/scala/docs/http/scaladsl/server/directives/JsonStreamingFullExamples.scala
index cb2874a47..c2d93a26a 100644
--- 
a/docs/src/test/scala/docs/http/scaladsl/server/directives/JsonStreamingFullExamples.scala
+++ 
b/docs/src/test/scala/docs/http/scaladsl/server/directives/JsonStreamingFullExamples.scala
@@ -54,35 +54,37 @@ class JsonStreamingFullExamples extends AnyWordSpec {
       })
   }
 
-  object ApiServer extends App with UserProtocol {
-    implicit val system: ActorSystem = ActorSystem("api")
-    implicit val executionContext: ExecutionContext = system.dispatcher
-
-    implicit val jsonStreamingSupport: JsonEntityStreamingSupport = 
EntityStreamingSupport.json()
-      .withContentType(ct)
-      .withParallelMarshalling(parallelism = 10, unordered = false)
-
-    // (fake) async database query api
-    def dummyUser(id: String) = User(s"User $id", id.toString)
-
-    def fetchUsers(): Source[User, NotUsed] = Source.fromIterator(() =>
-      Iterator.fill(10000) {
-        val id = Random.nextInt()
-        dummyUser(id.toString)
-      })
-
-    val route =
-      pathPrefix("users") {
-        get {
-          complete(fetchUsers())
+  object ApiServer extends UserProtocol {
+    def main(args: Array[String]): Unit = {
+      implicit val system: ActorSystem = ActorSystem("api")
+      implicit val executionContext: ExecutionContext = system.dispatcher
+
+      implicit val jsonStreamingSupport: JsonEntityStreamingSupport = 
EntityStreamingSupport.json()
+        .withContentType(ct)
+        .withParallelMarshalling(parallelism = 10, unordered = false)
+
+      // (fake) async database query api
+      def dummyUser(id: String) = User(s"User $id", id.toString)
+
+      def fetchUsers(): Source[User, NotUsed] = Source.fromIterator(() =>
+        Iterator.fill(10000) {
+          val id = Random.nextInt()
+          dummyUser(id.toString)
+        })
+
+      val route =
+        pathPrefix("users") {
+          get {
+            complete(fetchUsers())
+          }
         }
-      }
 
-    val bindingFuture = Http().newServerAt("localhost", 8080).bind(route)
+      val bindingFuture = Http().newServerAt("localhost", 8080).bind(route)
 
-    println(s"Server online at http://localhost:8080/\nPress RETURN to 
stop...")
-    StdIn.readLine()
-    bindingFuture.flatMap(_.unbind()).onComplete(_ => system.terminate())
+      println(s"Server online at http://localhost:8080/\nPress RETURN to 
stop...")
+      StdIn.readLine()
+      bindingFuture.flatMap(_.unbind()).onComplete(_ => system.terminate())
+    }
   }
 
   // #custom-content-type
diff --git 
a/http-core/src/test/scala/org/apache/pekko/http/impl/engine/parsing/HttpHeaderParserTestBed.scala
 
b/http-core/src/test/scala/org/apache/pekko/http/impl/engine/parsing/HttpHeaderParserTestBed.scala
index 78daefe64..e8a4c522f 100644
--- 
a/http-core/src/test/scala/org/apache/pekko/http/impl/engine/parsing/HttpHeaderParserTestBed.scala
+++ 
b/http-core/src/test/scala/org/apache/pekko/http/impl/engine/parsing/HttpHeaderParserTestBed.scala
@@ -18,32 +18,34 @@ import pekko.actor.ActorSystem
 import pekko.http.scaladsl.settings.ParserSettings
 import com.typesafe.config.{ Config, ConfigFactory }
 
-object HttpHeaderParserTestBed extends App {
-
-  val testConf: Config = ConfigFactory.parseString("""
-    pekko.event-handlers = ["org.apache.pekko.testkit.TestEventListener"]
-    pekko.loglevel = ERROR
-    pekko.http.parsing.max-header-name-length = 20
-    pekko.http.parsing.max-header-value-length = 21
-    pekko.http.parsing.header-cache.Host = 300""")
-  val system: ActorSystem = ActorSystem("HttpHeaderParserTestBed", testConf)
-
-  val parser = HttpHeaderParser.prime {
-    HttpHeaderParser.unprimed(ParserSettings(system), system.log,
-      warnOnIllegalHeader = info => system.log.warning(info.formatPretty))
+object HttpHeaderParserTestBed {
+  def main(args: Array[String]): Unit = {
+
+    val testConf: Config = ConfigFactory.parseString("""
+      pekko.event-handlers = ["org.apache.pekko.testkit.TestEventListener"]
+      pekko.loglevel = ERROR
+      pekko.http.parsing.max-header-name-length = 20
+      pekko.http.parsing.max-header-value-length = 21
+      pekko.http.parsing.header-cache.Host = 300""")
+    val system: ActorSystem = ActorSystem("HttpHeaderParserTestBed", testConf)
+
+    val parser = HttpHeaderParser.prime {
+      HttpHeaderParser.unprimed(ParserSettings(system), system.log,
+        warnOnIllegalHeader = info => system.log.warning(info.formatPretty))
+    }
+
+    println {
+      s"""
+         |HttpHeaderParser primed Trie
+         |----------------------------
+         |
+         |%TRIE%
+         |
+         |formatSizes: ${parser.formatSizes}
+         |contentHistogram: ${parser.contentHistogram.mkString("\n  ", "\n  ", 
"\n")}
+       """.stripMargin.replace("%TRIE%", parser.formatTrie)
+    }
+
+    system.terminate()
   }
-
-  println {
-    s"""
-       |HttpHeaderParser primed Trie
-       |----------------------------
-       |
-       |%TRIE%
-       |
-       |formatSizes: ${parser.formatSizes}
-       |contentHistogram: ${parser.contentHistogram.mkString("\n  ", "\n  ", 
"\n")}
-     """.stripMargin.replace("%TRIE%", parser.formatTrie)
-  }
-
-  system.terminate()
 }
diff --git 
a/http-core/src/test/scala/org/apache/pekko/http/impl/engine/ws/EchoTestClientApp.scala
 
b/http-core/src/test/scala/org/apache/pekko/http/impl/engine/ws/EchoTestClientApp.scala
index de81c7678..a89b643de 100644
--- 
a/http-core/src/test/scala/org/apache/pekko/http/impl/engine/ws/EchoTestClientApp.scala
+++ 
b/http-core/src/test/scala/org/apache/pekko/http/impl/engine/ws/EchoTestClientApp.scala
@@ -30,49 +30,51 @@ import scala.util.{ Failure, Success }
 /**
  * An example App that runs a quick test against the websocket server at 
wss://echo.websocket.org
  */
-object EchoTestClientApp extends App {
-  implicit val system: ActorSystem = ActorSystem()
-  import system.dispatcher
+object EchoTestClientApp {
+  def main(args: Array[String]): Unit = {
+    implicit val system: ActorSystem = ActorSystem()
+    import system.dispatcher
 
-  def delayedCompletion(delay: FiniteDuration): Source[Nothing, NotUsed] =
-    Source.single(1)
-      .mapAsync(1)(_ => pekko.pattern.after(delay, 
system.scheduler)(Future(1)))
-      .drop(1).asInstanceOf[Source[Nothing, NotUsed]]
+    def delayedCompletion(delay: FiniteDuration): Source[Nothing, NotUsed] =
+      Source.single(1)
+        .mapAsync(1)(_ => pekko.pattern.after(delay, 
system.scheduler)(Future(1)))
+        .drop(1).asInstanceOf[Source[Nothing, NotUsed]]
 
-  def messages: List[Message] =
-    List(
-      TextMessage("Test 1"),
-      BinaryMessage(ByteString("abc")),
-      TextMessage("Test 2"),
-      BinaryMessage(ByteString("def")))
+    def messages: List[Message] =
+      List(
+        TextMessage("Test 1"),
+        BinaryMessage(ByteString("abc")),
+        TextMessage("Test 2"),
+        BinaryMessage(ByteString("def")))
 
-  def source: Source[Message, NotUsed] =
-    Source(messages) ++ delayedCompletion(1.second) // otherwise, we may start 
closing too soon
+    def source: Source[Message, NotUsed] =
+      Source(messages) ++ delayedCompletion(1.second) // otherwise, we may 
start closing too soon
 
-  def sink: Sink[Message, Future[Seq[String]]] =
-    Flow[Message]
-      .mapAsync(1) {
-        case tm: TextMessage =>
-          tm.textStream.runWith(Sink.fold("")(_ + _)).map(str => 
s"TextMessage: '$str'")
-        case bm: BinaryMessage =>
-          bm.dataStream.runWith(Sink.fold(ByteString.empty)(_ ++ _)).map(bs => 
s"BinaryMessage: '${bs.utf8String}'")
-      }
-      .grouped(10000)
-      .toMat(Sink.head)(Keep.right)
+    def sink: Sink[Message, Future[Seq[String]]] =
+      Flow[Message]
+        .mapAsync(1) {
+          case tm: TextMessage =>
+            tm.textStream.runWith(Sink.fold("")(_ + _)).map(str => 
s"TextMessage: '$str'")
+          case bm: BinaryMessage =>
+            bm.dataStream.runWith(Sink.fold(ByteString.empty)(_ ++ _)).map(bs 
=> s"BinaryMessage: '${bs.utf8String}'")
+        }
+        .grouped(10000)
+        .toMat(Sink.head)(Keep.right)
 
-  def echoClient = Flow.fromSinkAndSourceMat(sink, source)(Keep.left)
+    def echoClient = Flow.fromSinkAndSourceMat(sink, source)(Keep.left)
 
-  val (upgrade, res) = 
Http().singleWebSocketRequest("wss://echo.websocket.org", echoClient)
-  res.onComplete {
-    case Success(res) =>
-      println("Run successful. Got these elements:")
-      res.foreach(println)
-      system.terminate()
-    case Failure(e) =>
-      println("Run failed.")
-      e.printStackTrace()
-      system.terminate()
-  }
+    val (upgrade, res) = 
Http().singleWebSocketRequest("wss://echo.websocket.org", echoClient)
+    res.onComplete {
+      case Success(res) =>
+        println("Run successful. Got these elements:")
+        res.foreach(println)
+        system.terminate()
+      case Failure(e) =>
+        println("Run failed.")
+        e.printStackTrace()
+        system.terminate()
+    }
 
-  system.scheduler.scheduleOnce(10.seconds)(system.terminate())
+    system.scheduler.scheduleOnce(10.seconds)(system.terminate())
+  }
 }
diff --git 
a/http-core/src/test/scala/org/apache/pekko/http/impl/engine/ws/WSClientAutobahnTest.scala
 
b/http-core/src/test/scala/org/apache/pekko/http/impl/engine/ws/WSClientAutobahnTest.scala
index aec0fe995..5d002308d 100644
--- 
a/http-core/src/test/scala/org/apache/pekko/http/impl/engine/ws/WSClientAutobahnTest.scala
+++ 
b/http-core/src/test/scala/org/apache/pekko/http/impl/engine/ws/WSClientAutobahnTest.scala
@@ -25,188 +25,190 @@ import pekko.http.scaladsl.Http
 import pekko.http.scaladsl.model.Uri
 import pekko.http.scaladsl.model.ws._
 
-object WSClientAutobahnTest extends App {
-  implicit val system: ActorSystem = ActorSystem()
-  import system.dispatcher
+object WSClientAutobahnTest {
+  def main(args: Array[String]): Unit = {
+    implicit val system: ActorSystem = ActorSystem()
+    import system.dispatcher
 
-  val Agent = "pekko-http"
-  val Parallelism = 4
+    val Agent = "pekko-http"
+    val Parallelism = 4
 
-  val getCaseCountUri: Uri =
-    s"ws://localhost:9001/getCaseCount"
+    val getCaseCountUri: Uri =
+      s"ws://localhost:9001/getCaseCount"
 
-  def runCaseUri(caseIndex: Int, agent: String): Uri =
-    s"ws://localhost:9001/runCase?case=$caseIndex&agent=$agent"
+    def runCaseUri(caseIndex: Int, agent: String): Uri =
+      s"ws://localhost:9001/runCase?case=$caseIndex&agent=$agent"
 
-  def getCaseStatusUri(caseIndex: Int, agent: String): Uri =
-    s"ws://localhost:9001/getCaseStatus?case=$caseIndex&agent=$agent"
+    def getCaseStatusUri(caseIndex: Int, agent: String): Uri =
+      s"ws://localhost:9001/getCaseStatus?case=$caseIndex&agent=$agent"
 
-  def getCaseInfoUri(caseIndex: Int): Uri =
-    s"ws://localhost:9001/getCaseInfo?case=$caseIndex"
+    def getCaseInfoUri(caseIndex: Int): Uri =
+      s"ws://localhost:9001/getCaseInfo?case=$caseIndex"
 
-  def updateReportsUri(agent: String): Uri =
-    s"ws://localhost:9001/updateReports?agent=$agent"
+    def updateReportsUri(agent: String): Uri =
+      s"ws://localhost:9001/updateReports?agent=$agent"
 
-  def runCase(caseIndex: Int, agent: String = Agent): Future[CaseStatus] =
-    runWs(runCaseUri(caseIndex, agent), echo).recover { case _ => () }.flatMap 
{ _ =>
-      getCaseStatus(caseIndex, agent)
-    }
+    def runCase(caseIndex: Int, agent: String = Agent): Future[CaseStatus] =
+      runWs(runCaseUri(caseIndex, agent), echo).recover { case _ => () 
}.flatMap { _ =>
+        getCaseStatus(caseIndex, agent)
+      }
 
-  def richRunCase(caseIndex: Int, agent: String = Agent): Future[CaseResult] = 
{
-    val info = getCaseInfo(caseIndex)
-    val startMillis = System.currentTimeMillis()
-    val status = runCase(caseIndex, agent).map { res =>
-      val lastedMillis = System.currentTimeMillis() - startMillis
-      (res, lastedMillis)
+    def richRunCase(caseIndex: Int, agent: String = Agent): Future[CaseResult] 
= {
+      val info = getCaseInfo(caseIndex)
+      val startMillis = System.currentTimeMillis()
+      val status = runCase(caseIndex, agent).map { res =>
+        val lastedMillis = System.currentTimeMillis() - startMillis
+        (res, lastedMillis)
+      }
+      import Console._
+      info.flatMap { i =>
+        val prefix = f"$YELLOW${i.caseInfo.id}%-7s$RESET - 
$RESET${i.caseInfo.description}$RESET ... "
+
+        status.onComplete {
+          case Success((CaseStatus(status), millis)) =>
+            val color = if (status == "OK") GREEN else RED
+            println(f"${color}$status%-15s$RESET$millis%5d ms $prefix")
+          case Failure(e) =>
+            println(s"$prefix${RED}failed with '${e.getMessage}'$RESET")
+        }
+
+        status.map(s => CaseResult(i.caseInfo, s._1))
+      }
     }
-    import Console._
-    info.flatMap { i =>
-      val prefix = f"$YELLOW${i.caseInfo.id}%-7s$RESET - 
$RESET${i.caseInfo.description}$RESET ... "
 
-      status.onComplete {
-        case Success((CaseStatus(status), millis)) =>
-          val color = if (status == "OK") GREEN else RED
-          println(f"${color}$status%-15s$RESET$millis%5d ms $prefix")
-        case Failure(e) =>
-          println(s"$prefix${RED}failed with '${e.getMessage}'$RESET")
+    def getCaseCount(): Future[Int] =
+      runToSingleText(getCaseCountUri).map(_.toInt)
+
+    def getCaseInfo(caseId: Int): Future[IndexedCaseInfo] =
+      
runToSingleJsonValue[CaseInfo](getCaseInfoUri(caseId)).map(IndexedCaseInfo(caseId,
 _))
+
+    def getCaseStatus(caseId: Int, agent: String = Agent): Future[CaseStatus] =
+      runToSingleJsonValue[CaseStatus](getCaseStatusUri(caseId, agent))
+
+    def updateReports(agent: String = Agent): Future[Unit] =
+      runToSingleText(updateReportsUri(agent)).map(_ => ())
+
+    /**
+     * Map from textual case ID (like 1.1.1) to IndexedCaseInfo
+     * @return
+     */
+    def getCaseMap(): Future[Map[String, IndexedCaseInfo]] = {
+      val res =
+        getCaseCount().flatMap { count =>
+          println(s"Retrieving case info for $count cases...")
+          Future.traverse(1 to count)(getCaseInfo).map(_.map(e => 
e.caseInfo.id -> e).toMap)
+        }
+      res.foreach { res =>
+        println(s"Received info for ${res.size} cases")
       }
-
-      status.map(s => CaseResult(i.caseInfo, s._1))
+      res
     }
-  }
-
-  def getCaseCount(): Future[Int] =
-    runToSingleText(getCaseCountUri).map(_.toInt)
-
-  def getCaseInfo(caseId: Int): Future[IndexedCaseInfo] =
-    
runToSingleJsonValue[CaseInfo](getCaseInfoUri(caseId)).map(IndexedCaseInfo(caseId,
 _))
-
-  def getCaseStatus(caseId: Int, agent: String = Agent): Future[CaseStatus] =
-    runToSingleJsonValue[CaseStatus](getCaseStatusUri(caseId, agent))
 
-  def updateReports(agent: String = Agent): Future[Unit] =
-    runToSingleText(updateReportsUri(agent)).map(_ => ())
+    def echo = Flow[Message].viaMat(completionSignal)(Keep.right)
 
-  /**
-   * Map from textual case ID (like 1.1.1) to IndexedCaseInfo
-   * @return
-   */
-  def getCaseMap(): Future[Map[String, IndexedCaseInfo]] = {
-    val res =
+    import Console._
+    if (args.size >= 1) {
+      // run one
+      val testId = args(0)
+      println(s"Trying to run test $testId")
+      getCaseMap().flatMap { map =>
+        val info = map(testId)
+        richRunCase(info.index)
+      }.onComplete {
+        case Success(res) =>
+          println(s"[OK] Run successfully finished!")
+          updateReportsAndShutdown()
+        case Failure(e) =>
+          println(s"[${RED}FAILED$RESET] Run failed with this exception: ")
+          e.printStackTrace()
+          updateReportsAndShutdown()
+      }
+    } else {
+      println("Running complete test suite")
       getCaseCount().flatMap { count =>
-        println(s"Retrieving case info for $count cases...")
-        Future.traverse(1 to count)(getCaseInfo).map(_.map(e => e.caseInfo.id 
-> e).toMap)
+        println(s"Found $count tests.")
+        Source(1 to 
count).mapAsyncUnordered(Parallelism)(richRunCase(_)).grouped(count).runWith(Sink.head)
+      }.map { results =>
+        val grouped =
+          results.groupBy(_.status.behavior)
+
+        println(s"${results.size} tests run.")
+        println()
+        println(s"${GREEN}OK$RESET: ${grouped.getOrElse("OK", Nil).size}")
+        val notOk = grouped.filterNot(_._1 == "OK")
+        notOk.toSeq.sortBy(_._2.size).foreach {
+          case (status, cases) => println(s"$RED$status$RESET: ${cases.size}")
+        }
+        println()
+        println("Not OK tests: ")
+        println()
+        results.filterNot(_.status.behavior == "OK").foreach { r =>
+          println(f"$RED${r.status.behavior}%-20s$RESET 
$YELLOW${r.info.id}%-7s$RESET - $RESET${r.info.description}")
+        }
+
+        ()
       }
-    res.foreach { res =>
-      println(s"Received info for ${res.size} cases")
+        .onComplete(completion)
     }
-    res
-  }
 
-  def echo = Flow[Message].viaMat(completionSignal)(Keep.right)
-
-  import Console._
-  if (args.size >= 1) {
-    // run one
-    val testId = args(0)
-    println(s"Trying to run test $testId")
-    getCaseMap().flatMap { map =>
-      val info = map(testId)
-      richRunCase(info.index)
-    }.onComplete {
+    def completion[T]: Try[T] => Unit = {
       case Success(res) =>
-        println(s"[OK] Run successfully finished!")
+        println(s"Run successfully finished!")
         updateReportsAndShutdown()
       case Failure(e) =>
-        println(s"[${RED}FAILED$RESET] Run failed with this exception: ")
+        println("Run failed with this exception")
         e.printStackTrace()
         updateReportsAndShutdown()
     }
-  } else {
-    println("Running complete test suite")
-    getCaseCount().flatMap { count =>
-      println(s"Found $count tests.")
-      Source(1 to 
count).mapAsyncUnordered(Parallelism)(richRunCase(_)).grouped(count).runWith(Sink.head)
-    }.map { results =>
-      val grouped =
-        results.groupBy(_.status.behavior)
-
-      println(s"${results.size} tests run.")
-      println()
-      println(s"${GREEN}OK$RESET: ${grouped.getOrElse("OK", Nil).size}")
-      val notOk = grouped.filterNot(_._1 == "OK")
-      notOk.toSeq.sortBy(_._2.size).foreach {
-        case (status, cases) => println(s"$RED$status$RESET: ${cases.size}")
-      }
-      println()
-      println("Not OK tests: ")
-      println()
-      results.filterNot(_.status.behavior == "OK").foreach { r =>
-        println(f"$RED${r.status.behavior}%-20s$RESET 
$YELLOW${r.info.id}%-7s$RESET - $RESET${r.info.description}")
+    def updateReportsAndShutdown(): Unit =
+      updateReports().onComplete { res =>
+        println("Reports should now be accessible at 
http://localhost:8080/cwd/reports/clients/index.html";)
+        system.terminate()
       }
 
-      ()
+    import scala.concurrent.duration._
+    import system.dispatcher
+    system.scheduler.scheduleOnce(60.seconds)(system.terminate())
+
+    def runWs[T](uri: Uri, clientFlow: Flow[Message, Message, T]): T =
+      Http().singleWebSocketRequest(uri, clientFlow)._2
+
+    def completionSignal[T]: Flow[T, T, Future[Done]] =
+      Flow[T].watchTermination((_, res) => res)
+
+    /**
+     * The autobahn tests define a weird API where every request must be a 
WebSocket request and
+     * they will send a single websocket message with the result. WebSocket 
everywhere? Strange,
+     * but somewhat consistent.
+     */
+    def runToSingleText(uri: Uri): Future[String] = {
+      val sink = Sink.head[Message]
+      runWs(uri, Flow.fromSinkAndSourceMat(sink, 
Source.maybe[Message])(Keep.left)).flatMap {
+        case tm: TextMessage => tm.textStream.runWith(Sink.fold("")(_ + _))
+        case other           =>
+          throw new IllegalStateException(s"unexpected element of type 
${other.getClass}")
+      }
     }
-      .onComplete(completion)
-  }
+    def runToSingleJsonValue[T: JsonReader](uri: Uri): Future[T] =
+      runToSingleText(uri).map(_.parseJson.convertTo[T])
 
-  def completion[T]: Try[T] => Unit = {
-    case Success(res) =>
-      println(s"Run successfully finished!")
-      updateReportsAndShutdown()
-    case Failure(e) =>
-      println("Run failed with this exception")
-      e.printStackTrace()
-      updateReportsAndShutdown()
-  }
-  def updateReportsAndShutdown(): Unit =
-    updateReports().onComplete { res =>
-      println("Reports should now be accessible at 
http://localhost:8080/cwd/reports/clients/index.html";)
-      system.terminate()
-    }
+    case class IndexedCaseInfo(index: Int, caseInfo: CaseInfo)
+    case class CaseResult(info: CaseInfo, status: CaseStatus)
 
-  import scala.concurrent.duration._
-  import system.dispatcher
-  system.scheduler.scheduleOnce(60.seconds)(system.terminate())
-
-  def runWs[T](uri: Uri, clientFlow: Flow[Message, Message, T]): T =
-    Http().singleWebSocketRequest(uri, clientFlow)._2
-
-  def completionSignal[T]: Flow[T, T, Future[Done]] =
-    Flow[T].watchTermination((_, res) => res)
-
-  /**
-   * The autobahn tests define a weird API where every request must be a 
WebSocket request and
-   * they will send a single websocket message with the result. WebSocket 
everywhere? Strange,
-   * but somewhat consistent.
-   */
-  def runToSingleText(uri: Uri): Future[String] = {
-    val sink = Sink.head[Message]
-    runWs(uri, Flow.fromSinkAndSourceMat(sink, 
Source.maybe[Message])(Keep.left)).flatMap {
-      case tm: TextMessage => tm.textStream.runWith(Sink.fold("")(_ + _))
-      case other           =>
-        throw new IllegalStateException(s"unexpected element of type 
${other.getClass}")
+    // {"behavior": "OK"}
+    case class CaseStatus(behavior: String) {
+      def isSuccessful: Boolean = behavior == "OK"
+    }
+    object CaseStatus {
+      import DefaultJsonProtocol._
+      implicit def caseStatusFormat: JsonFormat[CaseStatus] = 
jsonFormat1(CaseStatus.apply)
     }
-  }
-  def runToSingleJsonValue[T: JsonReader](uri: Uri): Future[T] =
-    runToSingleText(uri).map(_.parseJson.convertTo[T])
-
-  case class IndexedCaseInfo(index: Int, caseInfo: CaseInfo)
-  case class CaseResult(info: CaseInfo, status: CaseStatus)
-
-  // {"behavior": "OK"}
-  case class CaseStatus(behavior: String) {
-    def isSuccessful: Boolean = behavior == "OK"
-  }
-  object CaseStatus {
-    import DefaultJsonProtocol._
-    implicit def caseStatusFormat: JsonFormat[CaseStatus] = 
jsonFormat1(CaseStatus.apply)
-  }
 
-  // {"id": "1.1.1", "description": "Send text message with payload 0."}
-  case class CaseInfo(id: String, description: String)
-  object CaseInfo {
-    import DefaultJsonProtocol._
-    implicit def caseInfoFormat: JsonFormat[CaseInfo] = 
jsonFormat2(CaseInfo.apply)
+    // {"id": "1.1.1", "description": "Send text message with payload 0."}
+    case class CaseInfo(id: String, description: String)
+    object CaseInfo {
+      import DefaultJsonProtocol._
+      implicit def caseInfoFormat: JsonFormat[CaseInfo] = 
jsonFormat2(CaseInfo.apply)
+    }
   }
 }
diff --git 
a/http-core/src/test/scala/org/apache/pekko/http/impl/engine/ws/WSServerAutobahnTest.scala
 
b/http-core/src/test/scala/org/apache/pekko/http/impl/engine/ws/WSServerAutobahnTest.scala
index 8fa78f7fc..39eff7427 100644
--- 
a/http-core/src/test/scala/org/apache/pekko/http/impl/engine/ws/WSServerAutobahnTest.scala
+++ 
b/http-core/src/test/scala/org/apache/pekko/http/impl/engine/ws/WSServerAutobahnTest.scala
@@ -28,34 +28,36 @@ import pekko.stream.scaladsl.Flow
 
 import scala.io.StdIn
 
-object WSServerAutobahnTest extends App {
-  implicit val system: ActorSystem = ActorSystem("WSServerTest")
-
-  val host = System.getProperty("pekko.ws-host", "127.0.0.1")
-  val port = System.getProperty("pekko.ws-port", "9001").toInt
-  val mode = System.getProperty("pekko.ws-mode", "read") // read or sleep
-
-  try {
-    val binding = Http().newServerAt(host, port).bindSync {
-      case req @ HttpRequest(GET, Uri.Path("/"), _, _, _) if 
req.attribute(webSocketUpgrade).isDefined =>
-        req.attribute(webSocketUpgrade) match {
-          case Some(upgrade) => upgrade.handleMessages(echoWebSocketService) 
// needed for running the autobahn test suite
-          case None          => HttpResponse(400, entity = "Not a valid 
websocket request!")
-        }
-      case _: HttpRequest => HttpResponse(404, entity = "Unknown resource!")
+object WSServerAutobahnTest {
+  def main(args: Array[String]): Unit = {
+    implicit val system: ActorSystem = ActorSystem("WSServerTest")
+
+    val host = System.getProperty("pekko.ws-host", "127.0.0.1")
+    val port = System.getProperty("pekko.ws-port", "9001").toInt
+    val mode = System.getProperty("pekko.ws-mode", "read") // read or sleep
+
+    try {
+      val binding = Http().newServerAt(host, port).bindSync {
+        case req @ HttpRequest(GET, Uri.Path("/"), _, _, _) if 
req.attribute(webSocketUpgrade).isDefined =>
+          req.attribute(webSocketUpgrade) match {
+            case Some(upgrade) => upgrade.handleMessages(echoWebSocketService) 
// needed for running the autobahn test suite
+            case None          => HttpResponse(400, entity = "Not a valid 
websocket request!")
+          }
+        case _: HttpRequest => HttpResponse(404, entity = "Unknown resource!")
+      }
+
+      Await.result(binding, 3.second) // throws if binding fails
+      println(s"Server online at http://$host:$port";)
+      mode match {
+        case "sleep" => while (true) Thread.sleep(1.minute.toMillis)
+        case "read"  => StdIn.readLine("Press RETURN to stop...")
+        case _       => throw new Exception("pekko.ws-mode MUST be sleep or 
read.")
+      }
+    } finally {
+      system.terminate()
     }
 
-    Await.result(binding, 3.second) // throws if binding fails
-    println(s"Server online at http://$host:$port";)
-    mode match {
-      case "sleep" => while (true) Thread.sleep(1.minute.toMillis)
-      case "read"  => StdIn.readLine("Press RETURN to stop...")
-      case _       => throw new Exception("pekko.ws-mode MUST be sleep or 
read.")
-    }
-  } finally {
-    system.terminate()
+    def echoWebSocketService: Flow[Message, Message, NotUsed] =
+      Flow[Message] // just let message flow directly to the output
   }
-
-  def echoWebSocketService: Flow[Message, Message, NotUsed] =
-    Flow[Message] // just let message flow directly to the output
 }
diff --git 
a/http-core/src/test/scala/org/apache/pekko/http/scaladsl/TestClient.scala 
b/http-core/src/test/scala/org/apache/pekko/http/scaladsl/TestClient.scala
index 56f6fa6e1..dc97b49ca 100644
--- a/http-core/src/test/scala/org/apache/pekko/http/scaladsl/TestClient.scala
+++ b/http-core/src/test/scala/org/apache/pekko/http/scaladsl/TestClient.scala
@@ -30,94 +30,96 @@ import pekko.util.ByteString
 import scala.concurrent.{ Await, Future }
 import scala.concurrent.duration._
 
-object TestClient extends App {
-  val testConf: Config = ConfigFactory.parseString("""
-    pekko.loglevel = DEBUG
-    pekko.log-dead-letters = off
-    pekko.io.tcp.trace-logging = off""")
-  implicit val system: ActorSystem = ActorSystem("ServerTest", testConf)
-  import system.dispatcher
-
-  installEventStreamLoggerFor[UnhandledMessage]
-
-  val host = "github.com"
-
-  fetchServerVersion1()
-
-  //  Console.readLine()
-  //  system.terminate()
-
-  def fetchServerVersion1(): Unit = {
-    println(s"Fetching HTTPS server version of host `$host` via a direct 
low-level connection ...")
-
-    val connection = Http().outgoingConnectionHttps(host)
-    val result = 
Source.single(HttpRequest()).via(connection).runWith(Sink.head)
-    result.map(_.header[headers.Server]).onComplete {
-      case Success(res) =>
-        println(s"$host is running ${res.mkString(", ")}")
-        println()
-        fetchServerVersion2()
-
-      case Failure(error) =>
-        println(s"Error: $error")
-        println()
-        fetchServerVersion2()
-    }
-  }
+object TestClient {
+  def main(args: Array[String]): Unit = {
+    val testConf: Config = ConfigFactory.parseString("""
+      pekko.loglevel = DEBUG
+      pekko.log-dead-letters = off
+      pekko.io.tcp.trace-logging = off""")
+    implicit val system: ActorSystem = ActorSystem("ServerTest", testConf)
+    import system.dispatcher
 
-  def fetchServerVersion2(): Unit = {
-    println(s"Fetching HTTP server version of host `$host` via the high-level 
API ...")
-    val result = Http().singleRequest(HttpRequest(uri = s"https://$host/";))
-    result.map(_.header[headers.Server]).onComplete {
-      case Success(res) =>
-        println(s"$host is running ${res.mkString(", ")}")
-        Http().shutdownAllConnectionPools().onComplete { _ => 
system.log.info("STOPPED"); shutdown() }
-
-      case Failure(error) =>
-        println(s"Error: $error")
-        shutdown()
-    }
-  }
+    installEventStreamLoggerFor[UnhandledMessage]
 
-  // for gathering dumps of entity and headers from pekko http client
-  // and curl in parallel to compare
-  def fetchAndStoreABunchOfUrlsWithHttpAndCurl(urls: Seq[String]): Unit = {
-    assert(urls.nonEmpty)
-    assert(new File("/tmp/client-dumps/").exists(), "you need to create 
/tmp/client-dumps/ before running")
+    val host = "github.com"
 
-    val testConf: Config = ConfigFactory.parseString("""
-    pekko.loglevel = DEBUG
-    pekko.log-dead-letters = off
-    pekko.io.tcp.trace-logging = off""")
-    implicit val system = ActorSystem("ServerTest", testConf)
-
-    try {
-      val done = Future.traverse(urls.zipWithIndex) {
-        case (url, index) =>
-          Http().singleRequest(HttpRequest(uri = url)).map { response =>
-            val path = new 
File(s"/tmp/client-dumps/pekko-body-$index.dump").toPath
-            val headersPath = new 
File(s"/tmp/client-dumps/pekko-headers-$index.dump").toPath
-
-            import scala.sys.process._
-            (s"""curl -D /tmp/client-dumps/curl-headers-$index.dump $url""" #> 
new File(
-              s"/tmp/client-dumps/curl-body-$index.dump")).!
-
-            val headers = Source(response.headers).map(header => 
ByteString(header.name + ": " + header.value + "\n"))
-              .runWith(FileIO.toPath(headersPath))
-
-            val body = response.entity.dataBytes
-              .runWith(FileIO.toPath(path))
-              .map(res => (url, path, res)): Future[(String, Path, IOResult)]
-
-            headers.flatMap(_ => body)
-          }
+    fetchServerVersion1()
+
+    //  Console.readLine()
+    //  system.terminate()
+
+    def fetchServerVersion1(): Unit = {
+      println(s"Fetching HTTPS server version of host `$host` via a direct 
low-level connection ...")
+
+      val connection = Http().outgoingConnectionHttps(host)
+      val result = 
Source.single(HttpRequest()).via(connection).runWith(Sink.head)
+      result.map(_.header[headers.Server]).onComplete {
+        case Success(res) =>
+          println(s"$host is running ${res.mkString(", ")}")
+          println()
+          fetchServerVersion2()
+
+        case Failure(error) =>
+          println(s"Error: $error")
+          println()
+          fetchServerVersion2()
       }
+    }
 
-      println("Fetched urls: " + Await.result(done, 10.minutes))
-    } finally {
-      Http().shutdownAllConnectionPools().flatMap(_ => system.terminate())
+    def fetchServerVersion2(): Unit = {
+      println(s"Fetching HTTP server version of host `$host` via the 
high-level API ...")
+      val result = Http().singleRequest(HttpRequest(uri = s"https://$host/";))
+      result.map(_.header[headers.Server]).onComplete {
+        case Success(res) =>
+          println(s"$host is running ${res.mkString(", ")}")
+          Http().shutdownAllConnectionPools().onComplete { _ => 
system.log.info("STOPPED"); shutdown() }
+
+        case Failure(error) =>
+          println(s"Error: $error")
+          shutdown()
+      }
     }
-  }
 
-  def shutdown(): Unit = system.terminate()
+    // for gathering dumps of entity and headers from pekko http client
+    // and curl in parallel to compare
+    def fetchAndStoreABunchOfUrlsWithHttpAndCurl(urls: Seq[String]): Unit = {
+      assert(urls.nonEmpty)
+      assert(new File("/tmp/client-dumps/").exists(), "you need to create 
/tmp/client-dumps/ before running")
+
+      val testConf: Config = ConfigFactory.parseString("""
+      pekko.loglevel = DEBUG
+      pekko.log-dead-letters = off
+      pekko.io.tcp.trace-logging = off""")
+      implicit val system = ActorSystem("ServerTest", testConf)
+
+      try {
+        val done = Future.traverse(urls.zipWithIndex) {
+          case (url, index) =>
+            Http().singleRequest(HttpRequest(uri = url)).map { response =>
+              val path = new 
File(s"/tmp/client-dumps/pekko-body-$index.dump").toPath
+              val headersPath = new 
File(s"/tmp/client-dumps/pekko-headers-$index.dump").toPath
+
+              import scala.sys.process._
+              (s"""curl -D /tmp/client-dumps/curl-headers-$index.dump $url""" 
#> new File(
+                s"/tmp/client-dumps/curl-body-$index.dump")).!
+
+              val headers = Source(response.headers).map(header => 
ByteString(header.name + ": " + header.value + "\n"))
+                .runWith(FileIO.toPath(headersPath))
+
+              val body = response.entity.dataBytes
+                .runWith(FileIO.toPath(path))
+                .map(res => (url, path, res)): Future[(String, Path, IOResult)]
+
+              headers.flatMap(_ => body)
+            }
+        }
+
+        println("Fetched urls: " + Await.result(done, 10.minutes))
+      } finally {
+        Http().shutdownAllConnectionPools().flatMap(_ => system.terminate())
+      }
+    }
+
+    def shutdown(): Unit = system.terminate()
+  }
 }
diff --git 
a/http-core/src/test/scala/org/apache/pekko/http/scaladsl/TestServer.scala 
b/http-core/src/test/scala/org/apache/pekko/http/scaladsl/TestServer.scala
index c7c5e8d0c..e1f65ced9 100644
--- a/http-core/src/test/scala/org/apache/pekko/http/scaladsl/TestServer.scala
+++ b/http-core/src/test/scala/org/apache/pekko/http/scaladsl/TestServer.scala
@@ -30,67 +30,69 @@ import com.typesafe.config.{ Config, ConfigFactory }
 
 import scala.io.StdIn
 
-object TestServer extends App {
-  val testConf: Config = ConfigFactory.parseString("""
-    pekko.loglevel = INFO
-    pekko.log-dead-letters = off
-    pekko.stream.materializer.debug.fuzzing-mode = off
-    pekko.actor.serialize-creators = off
-    pekko.actor.serialize-messages = off
-    pekko.actor.default-dispatcher.throughput = 1000
-    """)
-  implicit val system: ActorSystem = ActorSystem("ServerTest", testConf)
+object TestServer {
+  def main(args: Array[String]): Unit = {
+    val testConf: Config = ConfigFactory.parseString("""
+      pekko.loglevel = INFO
+      pekko.log-dead-letters = off
+      pekko.stream.materializer.debug.fuzzing-mode = off
+      pekko.actor.serialize-creators = off
+      pekko.actor.serialize-messages = off
+      pekko.actor.default-dispatcher.throughput = 1000
+      """)
+    implicit val system: ActorSystem = ActorSystem("ServerTest", testConf)
 
-  implicit val fm: Materializer = Materializer(system)
-  try {
-    val binding = Http().newServerAt("localhost", 9001).bindSync {
-      case req @ HttpRequest(GET, Uri.Path("/"), _, _, _) =>
-        req.attribute(webSocketUpgrade) match {
-          case Some(upgrade) => upgrade.handleMessages(echoWebSocketService) 
// needed for running the autobahn test suite
-          case None          => index
-        }
-      case HttpRequest(GET, Uri.Path("/ping"), _, _, _)             => 
HttpResponse(entity = "PONG!")
-      case HttpRequest(GET, Uri.Path("/crash"), _, _, _)            => 
sys.error("BOOM!")
-      case req @ HttpRequest(GET, Uri.Path("/ws-greeter"), _, _, _) =>
-        req.attribute(webSocketUpgrade) match {
-          case Some(upgrade) => upgrade.handleMessages(greeterWebSocketService)
-          case None          => HttpResponse(400, entity = "Not a valid 
websocket request!")
-        }
-      case _: HttpRequest => HttpResponse(404, entity = "Unknown resource!")
-    }
+    implicit val fm: Materializer = Materializer(system)
+    try {
+      val binding = Http().newServerAt("localhost", 9001).bindSync {
+        case req @ HttpRequest(GET, Uri.Path("/"), _, _, _) =>
+          req.attribute(webSocketUpgrade) match {
+            case Some(upgrade) => upgrade.handleMessages(echoWebSocketService) 
// needed for running the autobahn test suite
+            case None          => index
+          }
+        case HttpRequest(GET, Uri.Path("/ping"), _, _, _)             => 
HttpResponse(entity = "PONG!")
+        case HttpRequest(GET, Uri.Path("/crash"), _, _, _)            => 
sys.error("BOOM!")
+        case req @ HttpRequest(GET, Uri.Path("/ws-greeter"), _, _, _) =>
+          req.attribute(webSocketUpgrade) match {
+            case Some(upgrade) => 
upgrade.handleMessages(greeterWebSocketService)
+            case None          => HttpResponse(400, entity = "Not a valid 
websocket request!")
+          }
+        case _: HttpRequest => HttpResponse(404, entity = "Unknown resource!")
+      }
 
-    Await.result(binding, 1.second) // throws if binding fails
-    println("Server online at http://localhost:9001";)
-    println("Press RETURN to stop...")
-    StdIn.readLine()
-  } finally {
-    system.terminate()
-  }
+      Await.result(binding, 1.second) // throws if binding fails
+      println("Server online at http://localhost:9001";)
+      println("Press RETURN to stop...")
+      StdIn.readLine()
+    } finally {
+      system.terminate()
+    }
 
-  ////////////// helpers //////////////
+    ////////////// helpers //////////////
 
-  lazy val index = HttpResponse(
-    entity = HttpEntity(
-      ContentTypes.`text/html(UTF-8)`,
-      """|<html>
-         | <body>
-         |    <h1>Say hello to <i>pekko-http-core</i>!</h1>
-         |    <p>Defined resources:</p>
-         |    <ul>
-         |      <li><a href="/ping">/ping</a></li>
-         |      <li><a href="/crash">/crash</a></li>
-         |    </ul>
-         |  </body>
-         |</html>""".stripMargin))
+    lazy val index = HttpResponse(
+      entity = HttpEntity(
+        ContentTypes.`text/html(UTF-8)`,
+        """|<html>
+           | <body>
+           |    <h1>Say hello to <i>pekko-http-core</i>!</h1>
+           |    <p>Defined resources:</p>
+           |    <ul>
+           |      <li><a href="/ping">/ping</a></li>
+           |      <li><a href="/crash">/crash</a></li>
+           |    </ul>
+           |  </body>
+           |</html>""".stripMargin))
 
-  def echoWebSocketService: Flow[Message, Message, NotUsed] =
-    Flow[Message] // just let message flow directly to the output
+    def echoWebSocketService: Flow[Message, Message, NotUsed] =
+      Flow[Message] // just let message flow directly to the output
 
-  def greeterWebSocketService: Flow[Message, Message, NotUsed] =
-    Flow[Message]
-      .collect {
-        case TextMessage.Strict(name) => TextMessage(s"Hello '$name'")
-        case tm: TextMessage          => TextMessage(Source.single("Hello ") 
++ tm.textStream)
-        // ignore binary messages
-      }
+    def greeterWebSocketService: Flow[Message, Message, NotUsed] =
+      Flow[Message]
+        .collect {
+          case TextMessage.Strict(name) => TextMessage(s"Hello '$name'")
+          case tm: TextMessage          => TextMessage(Source.single("Hello ") 
++ tm.textStream)
+          // ignore binary messages
+        }
+  }
 }
diff --git 
a/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/TestSingleRequest.scala
 
b/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/TestSingleRequest.scala
index cfd07a238..484b81fc9 100644
--- 
a/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/TestSingleRequest.scala
+++ 
b/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/TestSingleRequest.scala
@@ -24,26 +24,28 @@ import pekko.util.ByteString
 
 import com.typesafe.config.{ Config, ConfigFactory }
 
-object TestSingleRequest extends App {
-  val testConf: Config = ConfigFactory.parseString("""
-    pekko.loglevel = INFO
-    pekko.log-dead-letters = off
-    pekko.stream.materializer.debug.fuzzing-mode = off
-    """)
-  implicit val system: ActorSystem = ActorSystem("ServerTest", testConf)
-  import system.dispatcher
+object TestSingleRequest {
+  def main(args: Array[String]): Unit = {
+    val testConf: Config = ConfigFactory.parseString("""
+      pekko.loglevel = INFO
+      pekko.log-dead-letters = off
+      pekko.stream.materializer.debug.fuzzing-mode = off
+      """)
+    implicit val system: ActorSystem = ActorSystem("ServerTest", testConf)
+    import system.dispatcher
 
-  val url = StdIn.readLine("url? ")
+    val url = StdIn.readLine("url? ")
 
-  val x = Http().singleRequest(HttpRequest(uri = url))
+    val x = Http().singleRequest(HttpRequest(uri = url))
 
-  val res = Await.result(x, 10.seconds)
+    val res = Await.result(x, 10.seconds)
 
-  val response = res.entity.dataBytes.runFold(ByteString.empty)(_ ++ 
_).map(_.utf8String)
+    val response = res.entity.dataBytes.runFold(ByteString.empty)(_ ++ 
_).map(_.utf8String)
 
-  println(" ------------ RESPONSE ------------")
-  println(Await.result(response, 10.seconds))
-  println(" -------- END OF RESPONSE ---------")
+    println(" ------------ RESPONSE ------------")
+    println(Await.result(response, 10.seconds))
+    println(" -------- END OF RESPONSE ---------")
 
-  system.terminate()
+    system.terminate()
+  }
 }
diff --git 
a/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/server/TcpLeakApp.scala
 
b/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/server/TcpLeakApp.scala
index 5e0de71de..1fb945127 100644
--- 
a/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/server/TcpLeakApp.scala
+++ 
b/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/server/TcpLeakApp.scala
@@ -26,38 +26,43 @@ import pekko.util.ByteString
 
 import com.typesafe.config.{ Config, ConfigFactory }
 
-object TcpLeakApp extends App {
-  val testConf: Config = ConfigFactory.parseString(
-    """
-    pekko.loglevel = DEBUG
-    pekko.log-dead-letters = on
-    pekko.io.tcp.trace-logging = on""")
-  implicit val system: ActorSystem = ActorSystem("ServerTest", testConf)
-
-  import system.dispatcher
-
-  val tcpFlow = Tcp(system).outgoingConnection(new 
InetSocketAddress("127.0.0.1", 1234)).named("TCP-outgoingConnection")
-  List
-    .fill(100)(
-      Source
-        .single(ByteString("FOO"))
-        
.log("outerFlow-beforeTcpFlow").withAttributes(ActorAttributes.logLevels(Logging.DebugLevel,
 Logging.ErrorLevel,
-          Logging.ErrorLevel))
-        .via(tcpFlow)
-        
.log("outerFlow-afterTcpFlow").withAttributes(ActorAttributes.logLevels(Logging.DebugLevel,
 Logging.ErrorLevel,
-          Logging.ErrorLevel))
-        .toMat(Sink.head)(Keep.right).run())
-    .last
-    .onComplete {
-      result =>
-        println(s"Result: $result")
-        Thread.sleep(10000)
-        println("===================== \n\n" +
-          system.asInstanceOf[
-            ActorSystemImpl].printTree + "\n\n========================")
-    }
-
-  Thread.sleep(11000)
-  StdIn.readLine("Press Enter to stop the application")
-  system.terminate()
+object TcpLeakApp {
+  def main(args: Array[String]): Unit = {
+    val testConf: Config = ConfigFactory.parseString(
+      """
+      pekko.loglevel = DEBUG
+      pekko.log-dead-letters = on
+      pekko.io.tcp.trace-logging = on""")
+    implicit val system: ActorSystem = ActorSystem("ServerTest", testConf)
+
+    import system.dispatcher
+
+    val tcpFlow =
+      Tcp(system).outgoingConnection(new InetSocketAddress("127.0.0.1", 
1234)).named("TCP-outgoingConnection")
+    List
+      .fill(100)(
+        Source
+          .single(ByteString("FOO"))
+          
.log("outerFlow-beforeTcpFlow").withAttributes(ActorAttributes.logLevels(Logging.DebugLevel,
+            Logging.ErrorLevel,
+            Logging.ErrorLevel))
+          .via(tcpFlow)
+          
.log("outerFlow-afterTcpFlow").withAttributes(ActorAttributes.logLevels(Logging.DebugLevel,
+            Logging.ErrorLevel,
+            Logging.ErrorLevel))
+          .toMat(Sink.head)(Keep.right).run())
+      .last
+      .onComplete {
+        result =>
+          println(s"Result: $result")
+          Thread.sleep(10000)
+          println("===================== \n\n" +
+            system.asInstanceOf[
+              ActorSystemImpl].printTree + "\n\n========================")
+      }
+
+    Thread.sleep(11000)
+    StdIn.readLine("Press Enter to stop the application")
+    system.terminate()
+  }
 }
diff --git 
a/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/server/TestServer.scala
 
b/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/server/TestServer.scala
index ecb98af38..0c35be3b6 100644
--- 
a/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/server/TestServer.scala
+++ 
b/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/server/TestServer.scala
@@ -32,99 +32,101 @@ import pekko.stream.scaladsl._
 
 import com.typesafe.config.{ Config, ConfigFactory }
 
-object TestServer extends App {
-  val testConf: Config = ConfigFactory.parseString("""
-    pekko.loglevel = INFO
-    pekko.log-dead-letters = off
-    pekko.stream.materializer.debug.fuzzing-mode = off
-    """)
-
-  implicit val system: ActorSystem = ActorSystem("ServerTest", testConf)
-  implicit val ec: ExecutionContext = system.dispatcher
-
-  import spray.json.DefaultJsonProtocol._
-
-  import pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
-  final case class Tweet(message: String)
-  implicit val tweetFormat: RootJsonFormat[Tweet] = jsonFormat1(Tweet.apply)
-
-  implicit val jsonStreaming: JsonEntityStreamingSupport = 
EntityStreamingSupport.json()
-
-  import Directives._
-  import ScalaXmlSupport._
-
-  def auth: AuthenticatorPF[String] = {
-    case p @ Credentials.Provided(name) if p.verify(name + "-password") => name
-  }
-
-  // format: OFF
-  val routes = {
-    get {
-      path("") {
-        withRequestTimeout(1.milli, _ => HttpResponse(
-          StatusCodes.EnhanceYourCalm,
-          entity = "Unable to serve response within time limit, please enhance 
your calm.")) {
-          Thread.sleep(1000)
-          complete(index)
-        }
-      } ~
-      path("secure") {
-        authenticateBasicPF("My very secure site", auth) { user =>
-          complete(<html> <body> Hello <b>{user}</b>. Access has been granted! 
</body> </html>)
-        }
-      } ~
-      path("ping") {
-        complete("PONG!")
-      } ~
-      path("crash") {
-        complete(sys.error("BOOM!"))
-      } ~
-      path("tweet") {
-        complete(Tweet("Hello, world!"))
-      } ~
-      (path("tweets") & parameter("n".as[Int])) { n =>
-        get {
-          val tweets = Source.repeat(Tweet("Hello, world!")).take(n)
-          complete(tweets)
+object TestServer {
+  def main(args: Array[String]): Unit = {
+    val testConf: Config = ConfigFactory.parseString("""
+      pekko.loglevel = INFO
+      pekko.log-dead-letters = off
+      pekko.stream.materializer.debug.fuzzing-mode = off
+      """)
+
+    implicit val system: ActorSystem = ActorSystem("ServerTest", testConf)
+    implicit val ec: ExecutionContext = system.dispatcher
+
+    import spray.json.DefaultJsonProtocol._
+
+    import pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+    final case class Tweet(message: String)
+    implicit val tweetFormat: RootJsonFormat[Tweet] = jsonFormat1(Tweet.apply)
+
+    implicit val jsonStreaming: JsonEntityStreamingSupport = 
EntityStreamingSupport.json()
+
+    import Directives._
+    import ScalaXmlSupport._
+
+    def auth: AuthenticatorPF[String] = {
+      case p @ Credentials.Provided(name) if p.verify(name + "-password") => 
name
+    }
+
+    // format: OFF
+    lazy val index =
+      <html>
+        <body>
+          <h1>Say hello to <i>pekko-http-core</i>!</h1>
+          <p>Defined resources:</p>
+          <ul>
+            <li><a href="/ping">/ping</a></li>
+            <li><a href="/secure">/secure</a> Use any username and 
'&lt;username&gt;-password' as credentials</li>
+            <li><a href="/crash">/crash</a></li>
+          </ul>
+        </body>
+      </html>
+
+    val routes = {
+      get {
+        path("") {
+          withRequestTimeout(1.milli, _ => HttpResponse(
+            StatusCodes.EnhanceYourCalm,
+            entity = "Unable to serve response within time limit, please 
enhance your calm.")) {
+            Thread.sleep(1000)
+            complete(index)
+          }
         } ~
-        post {
-          entity(asSourceOf[Tweet]) { tweets =>
-            onComplete(tweets.runFold(0)({ case (acc, t) => acc + 1 })) { 
count =>
-              complete(s"Total tweets received: " + count)
-            }
+        path("secure") {
+          authenticateBasicPF("My very secure site", auth) { user =>
+            complete(<html> <body> Hello <b>{user}</b>. Access has been 
granted! </body> </html>)
           }
         } ~
-        put {
-          // checking the alternative syntax also works:
-          entity(as[Source[Tweet, NotUsed]]) { tweets =>
-            onComplete(tweets.runFold(0)({ case (acc, t) => acc + 1 })) { 
count =>
-              complete(s"Total tweets received: " + count)
+        path("ping") {
+          complete("PONG!")
+        } ~
+        path("crash") {
+          complete(sys.error("BOOM!"))
+        } ~
+        path("tweet") {
+          complete(Tweet("Hello, world!"))
+        } ~
+        (path("tweets") & parameter("n".as[Int])) { n =>
+          get {
+            val tweets = Source.repeat(Tweet("Hello, world!")).take(n)
+            complete(tweets)
+          } ~
+          post {
+            entity(asSourceOf[Tweet]) { tweets =>
+              onComplete(tweets.runFold(0)({ case (acc, t) => acc + 1 })) { 
count =>
+                complete(s"Total tweets received: " + count)
+              }
+            }
+          } ~
+          put {
+            // checking the alternative syntax also works:
+            entity(as[Source[Tweet, NotUsed]]) { tweets =>
+              onComplete(tweets.runFold(0)({ case (acc, t) => acc + 1 })) { 
count =>
+                complete(s"Total tweets received: " + count)
+              }
             }
           }
         }
-      }
-    } ~
-    pathPrefix("inner")(getFromResourceDirectory("someDir"))
+      } ~
+      pathPrefix("inner")(getFromResourceDirectory("someDir"))
+    }
+    // format: ON
+
+    val bindingFuture = Http().newServerAt(interface = "0.0.0.0", port = 
8080).bind(routes)
+
+    println(s"Server online at http://0.0.0.0:8080/\nPress RETURN to stop...")
+    StdIn.readLine()
+
+    bindingFuture.flatMap(_.unbind()).onComplete(_ => system.terminate())
   }
-  // format: ON
-
-  val bindingFuture = Http().newServerAt(interface = "0.0.0.0", port = 
8080).bind(routes)
-
-  println(s"Server online at http://0.0.0.0:8080/\nPress RETURN to stop...")
-  StdIn.readLine()
-
-  bindingFuture.flatMap(_.unbind()).onComplete(_ => system.terminate())
-
-  lazy val index =
-    <html>
-      <body>
-        <h1>Say hello to <i>pekko-http-core</i>!</h1>
-        <p>Defined resources:</p>
-        <ul>
-          <li><a href="/ping">/ping</a></li>
-          <li><a href="/secure">/secure</a> Use any username and 
'&lt;username&gt;-password' as credentials</li>
-          <li><a href="/crash">/crash</a></li>
-        </ul>
-      </body>
-    </html>
 }
diff --git 
a/http2-tests/src/test/scala/org/apache/pekko/http/scaladsl/Http2ServerTest.scala
 
b/http2-tests/src/test/scala/org/apache/pekko/http/scaladsl/Http2ServerTest.scala
index e56da5329..bff34438e 100644
--- 
a/http2-tests/src/test/scala/org/apache/pekko/http/scaladsl/Http2ServerTest.scala
+++ 
b/http2-tests/src/test/scala/org/apache/pekko/http/scaladsl/Http2ServerTest.scala
@@ -36,108 +36,108 @@ import com.typesafe.config.ConfigFactory
 /**
  * App to manually test an HTTP2 server
  */
-object Http2ServerTest extends App {
-  val testConf: Config = ConfigFactory.parseString("""
-    pekko.log-dead-letters = off
-    pekko.stream.materializer.debug.fuzzing-mode = off
-    pekko.actor.serialize-creators = off
-    pekko.actor.serialize-messages = off
-    #pekko.actor.default-dispatcher.throughput = 1000
-    pekko.actor.default-dispatcher.fork-join-executor.parallelism-max=8""")
-  implicit val system: ActorSystem = ActorSystem("ServerTest", testConf)
-  implicit val ec: ExecutionContext = system.dispatcher
-
-  def slowDown[T](millis: Int): T => Future[T] = { t =>
-    pekko.pattern.after(millis.millis, system.scheduler)(Future.successful(t))
-  }
-
-  val syncHandler: HttpRequest => HttpResponse = {
-    case HttpRequest(GET, Uri.Path("/"), _, _, _)                              
            => index
-    case HttpRequest(GET, Uri.Path("/ping"), _, _, _)                          
            => HttpResponse(entity = "PONG!")
-    case HttpRequest(GET, Uri.Path("/image-page"), _, _, _)                    
            => imagePage
-    case HttpRequest(GET, Uri(_, _, p, _, _), _, _, _) if 
p.toString.startsWith("/image1") =>
-      HttpResponse(entity = HttpEntity(MediaTypes.`image/jpeg`,
-        FileIO.fromPath(Paths.get("bigimage.jpg"), 
100000).mapAsync(1)(slowDown(1))))
-    case HttpRequest(GET, Uri(_, _, p, _, _), _, _, _) if 
p.toString.startsWith("/image2") =>
-      HttpResponse(entity = HttpEntity(MediaTypes.`image/jpeg`,
-        FileIO.fromPath(Paths.get("bigimage2.jpg"), 
150000).mapAsync(1)(slowDown(2))))
-    case HttpRequest(GET, Uri.Path("/crash"), _, _, _) => sys.error("BOOM!")
-    case _: HttpRequest                                => HttpResponse(404, 
entity = "Unknown resource!")
-  }
-
-  val asyncHandler: HttpRequest => Future[HttpResponse] = {
-    case HttpRequest(POST, Uri.Path("/upload"), _, entity, _) =>
-      Unmarshal(entity).to[Multipart.FormData]
-        .flatMap { formData =>
-          formData.parts.runFoldAsync("") { (msg, part) =>
-            part.entity.dataBytes.runFold(0)(_ + _.size)
-              .map(dataSize =>
-                msg +
-                s"${part.name} ${part.filename} $dataSize 
${part.entity.contentType} ${part.additionalDispositionParams}\n")
-          }
-        }
-        .map { msg =>
-          HttpResponse(entity = s"Got upload: $msg")
-        }
-    case req => Future.successful(syncHandler(req))
-  }
-
-  try {
-    val bindings =
-      for {
-        httpsBinding <- Http().newServerAt(interface = "localhost", port = 
9000).enableHttps(
-          ExampleHttpContexts.exampleServerContext).bind(asyncHandler)
-        plainBinding <- Http().newServerAt(interface = "localhost", port = 
9002).bind(asyncHandler)
-      } yield (httpsBinding, plainBinding)
-
-    Await.result(bindings, 1.second) // throws if binding fails
-    println(Console.BOLD + "Server (HTTP/1.1 and HTTP/2) online at 
https://localhost:9000"; + Console.RESET)
-    println(Console.BOLD + "Server (HTTP/1/1 and HTTP/2) online at 
http://localhost:9002"; + Console.RESET)
-    println("Press RETURN to stop...")
-    StdIn.readLine()
-  } finally {
-    system.terminate()
-  }
-
-  ////////////// helpers //////////////
-
-  lazy val index = HttpResponse(
-    entity = HttpEntity(
-      ContentTypes.`text/html(UTF-8)`,
-      """|<html>
-        | <body>
-        |    <h1>Say hello to <i>pekko-http-core</i>!</h1>
-        |    <p>Defined resources:</p>
-        |    <ul>
-        |      <li><a href="/ping">/ping</a></li>
-        |      <li><a href="/image-page">/image-page</a></li>
-        |      <li><a href="/crash">/crash</a></li>
-        |    </ul>
-        |    <div>
-        |      <form method="post" enctype="multipart/form-data" 
action="upload">
-        |        <label for="file">File</label><input type="file" name="file" 
multiple="true"/><br/>
-        |        <input type="submit" />
-        |      </form>
-        |    </div>
-        |  </body>
-        |</html>""".stripMargin))
-
-  def imagesBlock = {
-    def one(): String =
-      s"""<img width="80" height="60" 
src="/image1?cachebuster=${Random.nextInt()}"></img>
-         |<img width="80" height="60" 
src="/image2?cachebuster=${Random.nextInt()}"></img>
-         |""".stripMargin
-
-    Seq.fill(20)(one()).mkString
-  }
-
-  lazy val imagePage = HttpResponse(
-    entity = HttpEntity(
-      ContentTypes.`text/html(UTF-8)`,
-      s"""|<html>
+object Http2ServerTest {
+  def main(args: Array[String]): Unit = {
+    val testConf: Config = ConfigFactory.parseString("""
+      pekko.log-dead-letters = off
+      pekko.stream.materializer.debug.fuzzing-mode = off
+      pekko.actor.serialize-creators = off
+      pekko.actor.serialize-messages = off
+      #pekko.actor.default-dispatcher.throughput = 1000
+      pekko.actor.default-dispatcher.fork-join-executor.parallelism-max=8""")
+    implicit val system: ActorSystem = ActorSystem("ServerTest", testConf)
+    implicit val ec: ExecutionContext = system.dispatcher
+
+    def slowDown[T](millis: Int): T => Future[T] = { t =>
+      pekko.pattern.after(millis.millis, 
system.scheduler)(Future.successful(t))
+    }
+
+    lazy val index = HttpResponse(
+      entity = HttpEntity(
+        ContentTypes.`text/html(UTF-8)`,
+        """|<html>
           | <body>
-          |    <h1>Image Page</h1>
-          |    $imagesBlock
+          |    <h1>Say hello to <i>pekko-http-core</i>!</h1>
+          |    <p>Defined resources:</p>
+          |    <ul>
+          |      <li><a href="/ping">/ping</a></li>
+          |      <li><a href="/image-page">/image-page</a></li>
+          |      <li><a href="/crash">/crash</a></li>
+          |    </ul>
+          |    <div>
+          |      <form method="post" enctype="multipart/form-data" 
action="upload">
+          |        <label for="file">File</label><input type="file" 
name="file" multiple="true"/><br/>
+          |        <input type="submit" />
+          |      </form>
+          |    </div>
           |  </body>
           |</html>""".stripMargin))
+
+    def imagesBlock = {
+      def one(): String =
+        s"""<img width="80" height="60" 
src="/image1?cachebuster=${Random.nextInt()}"></img>
+           |<img width="80" height="60" 
src="/image2?cachebuster=${Random.nextInt()}"></img>
+           |""".stripMargin
+
+      Seq.fill(20)(one()).mkString
+    }
+
+    lazy val imagePage = HttpResponse(
+      entity = HttpEntity(
+        ContentTypes.`text/html(UTF-8)`,
+        s"""|<html>
+            | <body>
+            |    <h1>Image Page</h1>
+            |    $imagesBlock
+            |  </body>
+            |</html>""".stripMargin))
+
+    val syncHandler: HttpRequest => HttpResponse = {
+      case HttpRequest(GET, Uri.Path("/"), _, _, _)                            
              => index
+      case HttpRequest(GET, Uri.Path("/ping"), _, _, _)                        
              => HttpResponse(entity = "PONG!")
+      case HttpRequest(GET, Uri.Path("/image-page"), _, _, _)                  
              => imagePage
+      case HttpRequest(GET, Uri(_, _, p, _, _), _, _, _) if 
p.toString.startsWith("/image1") =>
+        HttpResponse(entity = HttpEntity(MediaTypes.`image/jpeg`,
+          FileIO.fromPath(Paths.get("bigimage.jpg"), 
100000).mapAsync(1)(slowDown(1))))
+      case HttpRequest(GET, Uri(_, _, p, _, _), _, _, _) if 
p.toString.startsWith("/image2") =>
+        HttpResponse(entity = HttpEntity(MediaTypes.`image/jpeg`,
+          FileIO.fromPath(Paths.get("bigimage2.jpg"), 
150000).mapAsync(1)(slowDown(2))))
+      case HttpRequest(GET, Uri.Path("/crash"), _, _, _) => sys.error("BOOM!")
+      case _: HttpRequest                                => HttpResponse(404, 
entity = "Unknown resource!")
+    }
+
+    val asyncHandler: HttpRequest => Future[HttpResponse] = {
+      case HttpRequest(POST, Uri.Path("/upload"), _, entity, _) =>
+        Unmarshal(entity).to[Multipart.FormData]
+          .flatMap { formData =>
+            formData.parts.runFoldAsync("") { (msg, part) =>
+              part.entity.dataBytes.runFold(0)(_ + _.size)
+                .map(dataSize =>
+                  msg +
+                  s"${part.name} ${part.filename} $dataSize 
${part.entity.contentType} ${part.additionalDispositionParams}\n")
+            }
+          }
+          .map { msg =>
+            HttpResponse(entity = s"Got upload: $msg")
+          }
+      case req => Future.successful(syncHandler(req))
+    }
+
+    try {
+      val bindings =
+        for {
+          httpsBinding <- Http().newServerAt(interface = "localhost", port = 
9000).enableHttps(
+            ExampleHttpContexts.exampleServerContext).bind(asyncHandler)
+          plainBinding <- Http().newServerAt(interface = "localhost", port = 
9002).bind(asyncHandler)
+        } yield (httpsBinding, plainBinding)
+
+      Await.result(bindings, 1.second) // throws if binding fails
+      println(Console.BOLD + "Server (HTTP/1.1 and HTTP/2) online at 
https://localhost:9000"; + Console.RESET)
+      println(Console.BOLD + "Server (HTTP/1/1 and HTTP/2) online at 
http://localhost:9002"; + Console.RESET)
+      println("Press RETURN to stop...")
+      StdIn.readLine()
+    } finally {
+      system.terminate()
+    }
+  }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to