diff --git a/adapters/play/src/test/resources/application.conf b/adapters/play/src/test/resources/application.conf index e69de29bb2..c8251a0bfe 100644 --- a/adapters/play/src/test/resources/application.conf +++ b/adapters/play/src/test/resources/application.conf @@ -0,0 +1,2 @@ +http.server.max-content-length = infinite +play.http.parser.maxMemoryBuffer = 100M diff --git a/adapters/quick/src/main/scala/caliban/QuickRequestHandler.scala b/adapters/quick/src/main/scala/caliban/QuickRequestHandler.scala index d409764f2c..6fc8425a43 100644 --- a/adapters/quick/src/main/scala/caliban/QuickRequestHandler.scala +++ b/adapters/quick/src/main/scala/caliban/QuickRequestHandler.scala @@ -75,7 +75,8 @@ final private class QuickRequestHandler[R]( def decodeQueryParams(queryParams: QueryParams): Either[Response, GraphQLRequest] = { def extractField(key: String) = - try Right(queryParams.getAll(key).headOption.map(readFromString[InputValue.ObjectValue](_).fields)) + try + Right(queryParams.getAll(key).headOption.map(readFromString[InputValue.ObjectValue](_, readerConfig).fields)) catch { case NonFatal(_) => Left(badRequest(s"Invalid $key query param")) } for { @@ -101,7 +102,7 @@ final private class QuickRequestHandler[R]( body.asArray.foldZIO( _ => Exit.fail(BodyDecodeErrorResponse), arr => - try checkNonEmptyRequest(readFromArray[GraphQLRequest](arr)) + try checkNonEmptyRequest(readFromArray[GraphQLRequest](arr, readerConfig)) catch { case NonFatal(_) => Exit.fail(BodyDecodeErrorResponse) } ) @@ -136,7 +137,7 @@ final private class QuickRequestHandler[R]( Exit .fromOption(partsMap.get(key)) .flatMap(_.asChunk) - .flatMap(v => Exit.fromTry(Try(readFromArray[A](v.toArray)))) + .flatMap(v => Exit.fromTry(Try(readFromArray[A](v.toArray, readerConfig)))) .orElseFail(Response.badRequest) def parsePath(path: String): List[PathValue] = path.split('.').toList.map(PathValue.parse) @@ -265,7 +266,7 @@ final private class QuickRequestHandler[R]( case ChannelEvent.UserEventTriggered(HandshakeComplete) => out.runForeach(frame => ch.send(ChannelEvent.Read(frame))).forkScoped case ChannelEvent.Read(WebSocketFrame.Text(text)) => - ZIO.suspend(queue.offer(readFromString[GraphQLWSInput](text))) + ZIO.suspend(queue.offer(readFromString[GraphQLWSInput](text, readerConfig))) case _ => ZIO.unit }) @@ -309,4 +310,9 @@ object QuickRequestHandler { } private implicit val responseCodec: JsonValueCodec[ResponseValue] = ValueJsoniter.responseValueCodec + + private val readerConfig: ReaderConfig = ReaderConfig + .withAppendHexDumpToParseException(false) + .withMaxBufSize(Int.MaxValue - 2) + .withMaxCharBufSize(Int.MaxValue - 2) } diff --git a/adapters/quick/src/test/scala/caliban/QuickAdapterSpec.scala b/adapters/quick/src/test/scala/caliban/QuickAdapterSpec.scala index 163b17e58d..be3c7a6dbc 100644 --- a/adapters/quick/src/test/scala/caliban/QuickAdapterSpec.scala +++ b/adapters/quick/src/test/scala/caliban/QuickAdapterSpec.scala @@ -42,7 +42,7 @@ object QuickAdapterSpec extends ZIOSpecDefault { suite.provideShared( apiLayer, Scope.default, - Server.defaultWith(_.port(8090).responseCompression()) + Server.defaultWith(_.port(8090).enableRequestStreaming.responseCompression()) ) } } diff --git a/interop/tapir/src/main/scala/caliban/interop/tapir/JsonCodecs.scala b/interop/tapir/src/main/scala/caliban/interop/tapir/JsonCodecs.scala index 2265bd534f..b9fcca87b8 100644 --- a/interop/tapir/src/main/scala/caliban/interop/tapir/JsonCodecs.scala +++ b/interop/tapir/src/main/scala/caliban/interop/tapir/JsonCodecs.scala @@ -2,7 +2,7 @@ package caliban.interop.tapir import caliban.{ GraphQLRequest, ResponseValue } import sttp.tapir.Codec.JsonCodec -import sttp.tapir.json.jsoniter._ +import MaxCharBufSizeJsonJsoniter._ private object JsonCodecs { import caliban.interop.jsoniter.ValueJsoniter.stringListCodec diff --git a/interop/tapir/src/main/scala/caliban/interop/tapir/MaxCharBufSizeJsonJsoniter.scala b/interop/tapir/src/main/scala/caliban/interop/tapir/MaxCharBufSizeJsonJsoniter.scala new file mode 100644 index 0000000000..7b8c0002e5 --- /dev/null +++ b/interop/tapir/src/main/scala/caliban/interop/tapir/MaxCharBufSizeJsonJsoniter.scala @@ -0,0 +1,12 @@ +package caliban.interop.tapir + +import com.github.plokhotnyuk.jsoniter_scala.core.ReaderConfig +import sttp.tapir.json.jsoniter.TapirJsonJsoniter + +object MaxCharBufSizeJsonJsoniter extends TapirJsonJsoniter { + override lazy val readerConfig: ReaderConfig = + ReaderConfig + .withAppendHexDumpToParseException(false) + .withMaxCharBufSize(Int.MaxValue - 2) + .withMaxBufSize(Int.MaxValue - 2) +} diff --git a/interop/tapir/src/main/scala/caliban/interop/tapir/TapirAdapter.scala b/interop/tapir/src/main/scala/caliban/interop/tapir/TapirAdapter.scala index 3bba9ff848..4e8e9fee86 100644 --- a/interop/tapir/src/main/scala/caliban/interop/tapir/TapirAdapter.scala +++ b/interop/tapir/src/main/scala/caliban/interop/tapir/TapirAdapter.scala @@ -9,7 +9,7 @@ import sttp.model.sse.ServerSentEvent import sttp.model.{ headers => _, _ } import sttp.monad.MonadError import sttp.shared.Identity -import sttp.tapir.json.jsoniter._ +import MaxCharBufSizeJsonJsoniter._ import sttp.tapir.model.ServerRequest import sttp.tapir.server.ServerEndpoint import sttp.tapir.ztapir.ZioServerSentEvents diff --git a/interop/tapir/src/main/scala/caliban/interop/tapir/WebSocketInterpreter.scala b/interop/tapir/src/main/scala/caliban/interop/tapir/WebSocketInterpreter.scala index b80bb7dbdd..c1cc3baa2f 100644 --- a/interop/tapir/src/main/scala/caliban/interop/tapir/WebSocketInterpreter.scala +++ b/interop/tapir/src/main/scala/caliban/interop/tapir/WebSocketInterpreter.scala @@ -5,7 +5,7 @@ import caliban.interop.tapir.TapirAdapter._ import caliban.ws.Protocol import sttp.capabilities.zio.ZioStreams import sttp.model.{ headers => _ } -import sttp.tapir.json.jsoniter._ +import MaxCharBufSizeJsonJsoniter._ import sttp.tapir._ import sttp.tapir.model.{ ServerRequest, UnsupportedWebSocketFrameException } import sttp.tapir.server.ServerEndpoint diff --git a/interop/tapir/src/test/scala/caliban/interop/tapir/TapirAdapterSpec.scala b/interop/tapir/src/test/scala/caliban/interop/tapir/TapirAdapterSpec.scala index da3eed1732..ec3ddef0b8 100644 --- a/interop/tapir/src/test/scala/caliban/interop/tapir/TapirAdapterSpec.scala +++ b/interop/tapir/src/test/scala/caliban/interop/tapir/TapirAdapterSpec.scala @@ -3,7 +3,7 @@ package caliban.interop.tapir import caliban.InputValue.ObjectValue import caliban.Value.StringValue import caliban._ -import com.github.plokhotnyuk.jsoniter_scala.core.{ readFromString, writeToString, JsonValueCodec } +import com.github.plokhotnyuk.jsoniter_scala.core.{ readFromString, writeToString, JsonValueCodec, ReaderConfig } import com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker import sttp.capabilities.zio.ZioStreams import sttp.capabilities.{ Effect, WebSockets } @@ -14,7 +14,7 @@ import sttp.model._ import sttp.model.sse.ServerSentEvent import sttp.tapir.client.sttp.SttpClientInterpreter import sttp.tapir.client.sttp.ws.zio._ -import sttp.tapir.json.jsoniter._ +import MaxCharBufSizeJsonJsoniter._ import sttp.tapir.model.{ ConnectionInfo, ServerRequest } import sttp.tapir.{ AttributeKey, DecodeResult } import zio.stream.{ ZPipeline, ZSink, ZStream } @@ -272,6 +272,13 @@ object TapirAdapterSpec { method = Method.GET.method, query = """mutation{ deleteCharacter(name: "Amos Burton") }""" ).map(r => assertTrue(r.code.code == 400)) + }, + test("very long field values in mutations") { + val name = "A".repeat(ReaderConfig.maxCharBufSize + 1) + runHttpRequest( + method = Method.POST.method, + query = s"""mutation { deleteCharacter(name: \"$name\") }""" + ).map(r => assertTrue(r.code.code == 200)) } ) ),