Timeout exception in the ember parser when using fs2 >= `2.4.0`
See original GitHub issueI was not sure if I should report this here or in the fs2 repo, but I guess it made more sense to report it here first and move it to fs2 if we can take ember out of the equation.
The error is very simple, if using an ember server (with tls
) and the fs2 jar in the classpath is the version 2.4.0
(or superior) then calling the endpoints will fail with the underlying error:
java.util.concurrent.TimeoutException: 5 seconds at timeout @ org.http4s.ember.core.Parser$Request$.$anonfun$parser$8(Parser.scala:394) at flatMap @ org.http4s.ember.core.Parser$Request$.parser(Parser.scala:363) at flatMap @ org.http4s.ember.server.internal.ServerHelpers$.runApp(ServerHelpers.scala:132) at map @ org.http4s.ember.server.internal.ServerHelpers$.$anonfun$runConnection$2(ServerHelpers.scala:198)
MCVE
// scala 2.13.4
import $ivy.`org.typelevel::cats-effect:2.3.1`
import $ivy.`co.fs2::fs2-io:2.5.0` // Replace with 2.3.0 to fix the problem.
import $ivy.`org.http4s::http4s-ember-server:0.21.19`
import $ivy.`org.http4s::http4s-dsl:0.21.19`
import cats.effect.{Blocker, IO}
import fs2.Stream
import fs2.io.tls.TLSContext
import org.http4s.ember.server.EmberServerBuilder
import java.nio.file.Paths
object Main {
implicit val cs = IO.contextShift(scala.concurrent.ExecutionContext.global)
implicit val timer = IO.timer(scala.concurrent.ExecutionContext.global)
val app = {
import org.http4s.dsl.io._
import org.http4s.HttpRoutes
import org.http4s.server.Router
import org.http4s.syntax.kleisli._ // Provides the orNotFound method.
val test = HttpRoutes.of[IO] {
case GET -> Root / "test" => Ok("OK")
}
Router(
"/" -> test
).orNotFound
}
val keystorePassword = "password".toCharArray
def tlsContextR(blocker: Blocker) =
TLSContext.fromKeyStoreFile[IO](
file = Paths.get("./keystore.jks"),
keyPassword = keystorePassword,
storePassword = keystorePassword,
blocker = blocker
)
def serverR(blocker: Blocker, tlsContext: TLSContext) =
EmberServerBuilder
.default[IO]
.withHost("0.0.0.0")
.withPort(8080)
.withHttpApp(app)
.withTLS(tlsContext)
.withBlocker(blocker)
.withErrorHandler {
case e =>
IO(e.printStackTrace()).as(org.http4s.Response.timeout[IO])
}.build
val program =
for {
blocker <- Stream.resource(Blocker[IO])
tlsContext <- Stream.eval(tlsContextR(blocker))
server <- Stream.resource(serverR(blocker, tlsContext))
_ <- Stream.eval(IO(println(s"Server has started at ${server.address}")))
_ <- Stream.never[IO] >> Stream.emit(())
} yield ()
def run(): Unit = {
program.compile.drain.unsafeRunSync()
}
}
@main
def main(): Unit = {
Main.run()
}
To generate the keystore.jks
file run the following command:
keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -storepass password -validity 360 -keysize 2048
There is something really weird about this and it is that when using the original code that produced the error it was only reproducible by querying the endpoint using Postman or Axios (a NodeJS http client, which was actually the one that identified the problem) but it worked if using curl, chrome or vertx-web-client. But, using this minimal snippet even using curl I can reproduce the problem.
Issue Analytics
- State:
- Created 3 years ago
- Comments:21 (21 by maintainers)
Top GitHub Comments
@rossabaker No worries, as I said it was very easy to fix this in userland by just manually upgrading the fs2 version. But I was not sure if I should have closed this when it was fixed in fs2 or wait until it was “fixed” by http4s directly. Thanks for all the hard work!
@mpilquist I just tested it, it works!
Using the reproducible snippet now I can query it using postman and it answers without problems, (although the exception is still logged after a couple of seconds). Then I upgraded the version in the original / real code and I can confirm the issue is fixed!
Now, should I close this since it is technically fixed, or should we wait until a new version of http4s that depends on at least that version is released?