Skip to content

Commit

Permalink
Return a meaningful error message when the submitted payload is too l…
Browse files Browse the repository at this point in the history
…arge (#4421)

Co-authored-by: Simon Dumas <[email protected]>
  • Loading branch information
imsdu and Simon Dumas authored Oct 25, 2023
1 parent cee85b0 commit 66113f4
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package ch.epfl.bluebrain.nexus.delta.sdk.marshalling

import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.model.{EntityStreamSizeException, StatusCodes}
import akka.http.scaladsl.server.ExceptionHandler
import ch.epfl.bluebrain.nexus.delta.kernel.Logger
import akka.http.scaladsl.server.Directives._
import ch.epfl.bluebrain.nexus.delta.kernel.utils.ClassUtils
import ch.epfl.bluebrain.nexus.delta.rdf.RdfError
import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.contexts
Expand All @@ -14,12 +16,11 @@ import ch.epfl.bluebrain.nexus.delta.sdk.error.ServiceError.AuthorizationFailed
import ch.epfl.bluebrain.nexus.delta.sdk.error.{AuthTokenError, IdentityError, ServiceError}
import ch.epfl.bluebrain.nexus.delta.sdk.model.BaseUri
import ch.epfl.bluebrain.nexus.delta.sdk.permissions.model.PermissionsRejection
import com.typesafe.scalalogging.Logger
import io.circe.syntax._
import io.circe.{Encoder, JsonObject}

object RdfExceptionHandler {
private val logger: Logger = Logger[RdfExceptionHandler.type]
private val logger = Logger.cats[RdfExceptionHandler.type]

/**
* An [[ExceptionHandler]] that returns RDF output (Json-LD compacted, Json-LD expanded, Dot or NTriples) depending
Expand All @@ -31,14 +32,16 @@ object RdfExceptionHandler {
base: BaseUri
): ExceptionHandler =
ExceptionHandler {
case err: IdentityError => discardEntityAndForceEmit(err)
case err: PermissionsRejection => discardEntityAndForceEmit(err)
case err: AuthTokenError => discardEntityAndForceEmit(err)
case AuthorizationFailed => discardEntityAndForceEmit(AuthorizationFailed: ServiceError)
case err: RdfError => discardEntityAndForceEmit(err)
case err: Throwable =>
logger.error(s"An exception was thrown while evaluating a Route'", err)
discardEntityAndForceEmit(UnexpectedError)
case err: IdentityError => discardEntityAndForceEmit(err)
case err: PermissionsRejection => discardEntityAndForceEmit(err)
case err: AuthTokenError => discardEntityAndForceEmit(err)
case AuthorizationFailed => discardEntityAndForceEmit(AuthorizationFailed: ServiceError)
case err: RdfError => discardEntityAndForceEmit(err)
case err: EntityStreamSizeException => discardEntityAndForceEmit(err)
case err: Throwable =>
onComplete(logger.error(err)(s"An exception was thrown while evaluating a Route'").unsafeToFuture()) { _ =>
discardEntityAndForceEmit(UnexpectedError)
}
}

implicit private val rdfErrorEncoder: Encoder[RdfError] =
Expand All @@ -53,6 +56,21 @@ object RdfExceptionHandler {
implicit private val rdfErrorHttpFields: HttpResponseFields[RdfError] =
HttpResponseFields(_ => StatusCodes.InternalServerError)

implicit private val entityStreamSizeExceptionEncoder: Encoder[EntityStreamSizeException] =
Encoder.AsObject.instance { r =>
val tpe = "PayloadTooLarge"
val reason = s"""Incoming payload size (${r.actualSize.getOrElse(
"while streaming"
)}) exceeded size limit (${r.limit} bytes)"""
JsonObject(keywords.tpe -> tpe.asJson, "reason" -> reason.asJson)
}

implicit private val entityStreamSizeExceptionJsonLdEncoder: JsonLdEncoder[EntityStreamSizeException] =
JsonLdEncoder.computeFromCirce(ContextValue(contexts.error))

implicit private val entityStreamSizeExceptionHttpFields: HttpResponseFields[EntityStreamSizeException] =
HttpResponseFields(_ => StatusCodes.PayloadTooLarge)

final private case object UnexpectedError
private type UnexpectedError = UnexpectedError.type

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import io.circe.optics.JsonPath.root
import monocle.Optional
import org.scalatest.Assertion
import org.scalatest.matchers.{HavePropertyMatchResult, HavePropertyMatcher}
import org.testcontainers.utility.Base58.randomString

import java.net.URLEncoder

Expand Down Expand Up @@ -601,4 +602,17 @@ class ResourcesSpec extends BaseIntegrationSpec {
}
}
}

"uploading a payload too large" should {

"fail with the appropriate message" in {
val value = randomString(270000)
val payload = json"""{ "value": "$value" }"""
deltaClient.post[Json](s"/resources/$id1/", payload, Rick) { (json, response) =>
response.status shouldEqual StatusCodes.PayloadTooLarge
Optics.`@type`.getOption(json) shouldEqual Some("PayloadTooLarge")
}
}

}
}

0 comments on commit 66113f4

Please sign in to comment.