Repository: http4s/http4s
Pull request #4197 (2021-01-12 15:21:14)

use blocking where necessary to use the right thread pool
Those places were found by running the tests with a compute thread pool of 2 threads.
In theory, we should be able to run all the tests on only one thread, but some blocking places are quite difficult to get rid of for now.


Patch:
diff --git a/core/src/main/scala/org/http4s/StaticFile.scala b/core/src/main/scala/org/http4s/StaticFile.scala
index 7ec272ff2a..5d69ca5dcf 100644
--- a/core/src/main/scala/org/http4s/StaticFile.scala
+++ b/core/src/main/scala/org/http4s/StaticFile.scala
@@ -55,7 +55,7 @@ object StaticFile {
     val normalizedName = name.split("/").filter(_.nonEmpty).mkString("/")
 
     def getResource(name: String) =
-      OptionT(Sync[F].delay(Option(loader.getResource(name))))
+      OptionT(Sync[F].blocking(Option(loader.getResource(name))))
 
     val gzUrl: OptionT[F, URL] =
       if (tryGzipped) getResource(normalizedName + ".gz") else OptionT.none


Test patch:
diff --git a/blaze-client/src/test/scala/org/http4s/client/blaze/BlazeClientBase.scala b/blaze-client/src/test/scala/org/http4s/client/blaze/BlazeClientBase.scala
index a0dc215528..d379af0ed5 100644
--- a/blaze-client/src/test/scala/org/http4s/client/blaze/BlazeClientBase.scala
+++ b/blaze-client/src/test/scala/org/http4s/client/blaze/BlazeClientBase.scala
@@ -71,11 +71,11 @@ trait BlazeClientBase extends Http4sSuite {
 
                 val writeBody: IO[Unit] = res.body
                   .evalMap { byte =>
-                    IO(os.write(Array(byte)))
+                    IO.blocking(os.write(Array(byte)))
                   }
                   .compile
                   .drain
-                val flushOutputStream: IO[Unit] = IO(os.flush())
+                val flushOutputStream: IO[Unit] = IO.blocking(os.flush())
                 writeBody >> flushOutputStream
               }
               .unsafeRunSync()
@@ -83,10 +83,11 @@ trait BlazeClientBase extends Http4sSuite {
           case None => srv.sendError(404)
         }
 
-      override def doPost(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
-        resp.setStatus(Status.Ok.code)
-        req.getInputStream.close()
-      }
+      override def doPost(req: HttpServletRequest, resp: HttpServletResponse): Unit =
+        IO.blocking {
+          resp.setStatus(Status.Ok.code)
+          req.getInputStream.close()
+        }.unsafeRunSync()
     }
 
   def jettyScaffold: FunFixture[(JettyScaffold, JettyScaffold)] =
diff --git a/blaze-server/src/test/scala/org/http4s/server/blaze/BlazeServerSuite.scala b/blaze-server/src/test/scala/org/http4s/server/blaze/BlazeServerSuite.scala
index 30c4c5f44d..6c87b2bd1b 100644
--- a/blaze-server/src/test/scala/org/http4s/server/blaze/BlazeServerSuite.scala
+++ b/blaze-server/src/test/scala/org/http4s/server/blaze/BlazeServerSuite.scala
@@ -70,26 +70,25 @@ class BlazeServerSuite extends Http4sSuite {
       (_: TestOptions, _: Server) => IO.unit,
       (_: Server) => IO.sleep(100.milliseconds) *> IO.unit)
 
-  // This should be in IO and shifted but I'm tired of fighting this.
-  def get(server: Server, path: String): IO[String] = IO {
+  def get(server: Server, path: String): IO[String] = IO.blocking {
     Source
       .fromURL(new URL(s"http://127.0.0.1:${server.address.getPort}$path"))
       .getLines()
       .mkString
   }
 
-  // This should be in IO and shifted but I'm tired of fighting this.
   def getStatus(server: Server, path: String): IO[Status] = {
     val url = new URL(s"http://127.0.0.1:${server.address.getPort}$path")
     for {
-      conn <- IO(url.openConnection().asInstanceOf[HttpURLConnection])
+      conn <- IO.blocking(url.openConnection().asInstanceOf[HttpURLConnection])
       _ = conn.setRequestMethod("GET")
-      status <- IO.fromEither(Status.fromInt(conn.getResponseCode()))
+      status <- IO
+        .blocking(conn.getResponseCode())
+        .flatMap(code => IO.fromEither(Status.fromInt(code)))
     } yield status
   }
 
-  // This too
-  def post(server: Server, path: String, body: String): IO[String] = IO {
+  def post(server: Server, path: String, body: String): IO[String] = IO.blocking {
     val url = new URL(s"http://127.0.0.1:${server.address.getPort}$path")
     val conn = url.openConnection().asInstanceOf[HttpURLConnection]
     val bytes = body.getBytes(StandardCharsets.UTF_8)
@@ -100,13 +99,12 @@ class BlazeServerSuite extends Http4sSuite {
     Source.fromInputStream(conn.getInputStream, StandardCharsets.UTF_8.name).getLines().mkString
   }
 
-  // This too
   def postChunkedMultipart(
       server: Server,
       path: String,
       boundary: String,
       body: String): IO[String] =
-    IO {
+    IO.blocking {
       val url = new URL(s"http://127.0.0.1:${server.address.getPort}$path")
       val conn = url.openConnection().asInstanceOf[HttpURLConnection]
       val bytes = body.getBytes(StandardCharsets.UTF_8)
diff --git a/client/src/test/scala/org/http4s/client/JettyScaffold.scala b/client/src/test/scala/org/http4s/client/JettyScaffold.scala
index a0a22d56a0..a86435ab77 100644
--- a/client/src/test/scala/org/http4s/client/JettyScaffold.scala
+++ b/client/src/test/scala/org/http4s/client/JettyScaffold.scala
@@ -28,10 +28,10 @@ import org.eclipse.jetty.util.ssl.SslContextFactory
 object JettyScaffold {
   def apply[F[_]](num: Int, secure: Boolean, testServlet: HttpServlet)(implicit
       F: Sync[F]): Resource[F, JettyScaffold] =
-    Resource.make(F.delay {
+    Resource.make(F.blocking {
       val scaffold = new JettyScaffold(num, secure)
       scaffold.startServers(testServlet)
-    })(s => F.delay(s.stopServers()))
+    })(s => F.blocking(s.stopServers()))
 }
 
 class JettyScaffold private (num: Int, secure: Boolean) {
diff --git a/tests/src/test/scala/org/http4s/EntityDecoderSuite.scala b/tests/src/test/scala/org/http4s/EntityDecoderSuite.scala
index b22629575b..3ac10577c4 100644
--- a/tests/src/test/scala/org/http4s/EntityDecoderSuite.scala
+++ b/tests/src/test/scala/org/http4s/EntityDecoderSuite.scala
@@ -391,14 +391,14 @@ class EntityDecoderSuite extends Http4sSuite {
 
   val binData: Array[Byte] = "Bytes 10111".getBytes
 
-  def readFile(in: File): IO[Array[Byte]] = IO {
+  def readFile(in: File): IO[Array[Byte]] = IO.blocking {
     val os = new FileInputStream(in)
     val data = new Array[Byte](in.length.asInstanceOf[Int])
     os.read(data)
     data
   }
 
-  def readTextFile(in: File): IO[String] = IO {
+  def readTextFile(in: File): IO[String] = IO.blocking {
     val os = new InputStreamReader(new FileInputStream(in))
     val data = new Array[Char](in.length.asInstanceOf[Int])
     os.read(data, 0, in.length.asInstanceOf[Int])