`EntityLimiter` with Multipart Error
LaurenceWarne opened this issue · comments
Hi, I found that when I send a multipart request with a greater length than allowed by EntityLimiter
I receive org.http4s.InvalidMessageBodyFailure: Invalid message body: Invalid multipart body
rather what I would expect: org.http4s.server.middleware.EntityLimiter$EntityTooLarge
:
//> using lib "org.http4s::http4s-server:0.23.23"
//> using lib "org.http4s::http4s-dsl:0.23.23"
//> using lib "org.http4s::http4s-client:0.23.23"
import cats.effect._
import cats.effect.unsafe.IORuntime
import cats.syntax.all._
import fs2.Stream
import org.http4s._
import org.http4s.client.Client
import org.http4s.dsl.io._
import org.http4s.implicits._
import org.http4s.server.middleware.EntityLimiter
import org.http4s.multipart._
import org.typelevel.ci._
import org.typelevel.log4cats.LoggerFactory
import org.typelevel.log4cats.slf4j.Slf4jFactory
implicit val runtime: IORuntime = cats.effect.unsafe.IORuntime.global
implicit val loggerFactory: LoggerFactory[IO] = Slf4jFactory.create[IO]
println(org.http4s.BuildInfo.version)
val service = HttpRoutes.of[IO] {
case r @ POST -> Root / "reverse" => r.as[String].flatMap(s => Ok(s.reverse))
case req @ POST -> Root / "mp" => {
req.as[Multipart[IO]].flatMap { m =>
Ok(s"""Multipart Data\nParts:${m.parts.length}""".stripMargin)
}
}
}
val postRequest = Request[IO](Method.POST, uri"/reverse")
val mpRequest = Request[IO](Method.POST, uri"/mp")
val client = Client.fromHttpApp(service.orNotFound)
// Entity Limiter
val limiterService = EntityLimiter.httpApp(service.orNotFound, limit = 16L)
val limiterClient = Client.fromHttpApp(limiterService)
val smallRequest = postRequest.withEntity("*" * 15)
val bigRequest = postRequest.withEntity("*" * 16)
val mpBody = Multiparts
.forSync[IO]
.flatMap(
_.multipart(
Vector(
Part.formData("foo", "bar")
)
)
)
.unsafeRunSync()
val mpBigRequest = mpRequest.withEntity(mpBody).withHeaders(mpBody.headers)
println(limiterClient.status(smallRequest).unsafeRunSync())
// 200 OK
println(limiterClient.status(bigRequest).attempt.unsafeRunSync())
// Left(org.http4s.server.middleware.EntityLimiter$EntityTooLarge)
println(
limiterClient
.status(mpBigRequest)
.attempt
.unsafeRunSync()
)
// Left(org.http4s.InvalidMessageBodyFailure: Invalid message body: Invalid multipart body)
(scala-cli - output commented)
I found I get a similar result with 1.0.0-M40
.
Thanks!
Seems like the problem is all kinds of errors are handled here. To fix we should replace handleError
with recover
and only match exceptions that are relevant to multipart decoding.
http4s/core/shared/src/main/scala/org/http4s/multipart/MultipartDecoder.scala
Lines 141 to 144 in b9d6947
Seems like the problem is all kinds of errors are handled here. To fix we should replace handleError with recover and only match exceptions that are relevant to multipart decoding.
Looks to have done the trick, thanks for the pointer. I've opened a PR here: #7265 🙂