From 6137aa0f734114d8efe1327c7281192f0ca82737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=8BAndrzej=20Ressel?= Date: Mon, 25 Nov 2024 12:33:52 +0100 Subject: [PATCH 1/4] Fix "ZIO.fail(throw" (#3220) --- zio-http/shared/src/main/scala/zio/http/ZClient.scala | 2 +- .../src/main/scala/zio/http/codec/HttpContentCodec.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/ZClient.scala b/zio-http/shared/src/main/scala/zio/http/ZClient.scala index 1b8aa6431b..014d77cca6 100644 --- a/zio-http/shared/src/main/scala/zio/http/ZClient.scala +++ b/zio-http/shared/src/main/scala/zio/http/ZClient.scala @@ -670,7 +670,7 @@ object ZClient extends ZClientPlatformSpecific { webSocketUrl <- url.scheme match { case Some(Scheme.HTTP) | Some(Scheme.WS) | None => ZIO.succeed(url.scheme(Scheme.WS)) case Some(Scheme.WSS) | Some(Scheme.HTTPS) => ZIO.succeed(url.scheme(Scheme.WSS)) - case _ => ZIO.fail(throw new IllegalArgumentException("URL's scheme MUST be WS(S) or HTTP(S)")) + case _ => ZIO.fail(new IllegalArgumentException("URL's scheme MUST be WS(S) or HTTP(S)")) } scope <- ZIO.scope res <- requestAsync( diff --git a/zio-http/shared/src/main/scala/zio/http/codec/HttpContentCodec.scala b/zio-http/shared/src/main/scala/zio/http/codec/HttpContentCodec.scala index 3f8a3f29a2..916742b9e9 100644 --- a/zio-http/shared/src/main/scala/zio/http/codec/HttpContentCodec.scala +++ b/zio-http/shared/src/main/scala/zio/http/codec/HttpContentCodec.scala @@ -35,7 +35,7 @@ sealed trait HttpContentCodec[A] { self => ZIO.fromEither(codec.codec(config).decode(bytes)) } case None => - ZIO.fail(throw new IllegalArgumentException(s"No codec found for content type $contentType")) + ZIO.fail(new IllegalArgumentException(s"No codec found for content type $contentType")) } } @@ -50,7 +50,7 @@ sealed trait HttpContentCodec[A] { self => ZIO.fromEither(codec.codec(config).decode(bytes)) } case None => - ZIO.fail(throw new IllegalArgumentException(s"No codec found for content type $contentType")) + ZIO.fail(new IllegalArgumentException(s"No codec found for content type $contentType")) } } From 3d82a55059f71c3bce9cee6225dee5753d413146 Mon Sep 17 00:00:00 2001 From: Jules Ivanic Date: Mon, 2 Dec 2024 23:15:11 +1100 Subject: [PATCH 2/4] Optimise `zio.http.Body.FileBody.asStream` code (#3215) --- .../shared/src/main/scala/zio/http/Body.scala | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/Body.scala b/zio-http/shared/src/main/scala/zio/http/Body.scala index a72fdce456..00ba269aab 100644 --- a/zio-http/shared/src/main/scala/zio/http/Body.scala +++ b/zio-http/shared/src/main/scala/zio/http/Body.scala @@ -567,21 +567,27 @@ object Body { override def asStream(implicit trace: Trace): ZStream[Any, Throwable, Byte] = ZStream.unwrap { - for { - file <- ZIO.attempt(file) - fs <- ZIO.attemptBlocking(new FileInputStream(file)) - size <- ZIO.attemptBlocking(Math.min(chunkSize.toLong, file.length()).toInt) - } yield ZStream - .repeatZIOOption[Any, Throwable, Chunk[Byte]] { - for { - buffer <- ZIO.succeed(new Array[Byte](size)) - len <- ZIO.attemptBlocking(fs.read(buffer)).mapError(Some(_)) - bytes <- - if (len > 0) ZIO.succeed(Chunk.fromArray(buffer.slice(0, len))) - else ZIO.fail(None) - } yield bytes - } - .ensuring(ZIO.attemptBlocking(fs.close()).ignoreLogged) + ZIO.blocking { + for { + r <- ZIO.attempt { + val fs = new FileInputStream(file) + val size = Math.min(chunkSize.toLong, file.length()).toInt + + (fs, size) + } + (fs, size) = r + } yield ZStream + .repeatZIOOption[Any, Throwable, Chunk[Byte]] { + for { + buffer <- ZIO.succeed(new Array[Byte](size)) + len <- ZIO.attempt(fs.read(buffer)).mapError(Some(_)) + bytes <- + if (len > 0) ZIO.succeed(Chunk.fromArray(buffer.slice(0, len))) + else ZIO.fail(None) + } yield bytes + } + .ensuring(ZIO.attempt(fs.close()).ignoreLogged) + } }.flattenChunks override def contentType(newContentType: Body.ContentType): Body = copy(contentType = Some(newContentType)) From 547975e9fc8c1d3bfdf492f8b1e99288a8f5f686 Mon Sep 17 00:00:00 2001 From: Jules Ivanic Date: Mon, 2 Dec 2024 23:17:23 +1100 Subject: [PATCH 3/4] Optimize `zio.http.codec.internal.EncoderDecoder.Single.decodeBody` code (#3211) --- .../zio/http/codec/internal/EncoderDecoder.scala | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala b/zio-http/shared/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala index 705b432cbf..44d99b72dd 100644 --- a/zio-http/shared/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala +++ b/zio-http/shared/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala @@ -419,18 +419,21 @@ private[codec] object EncoderDecoder { private def decodeBody(config: CodecConfig, body: Body, inputs: Array[Any])(implicit trace: Trace, ): Task[Unit] = { - val codecs = flattened.content + val isNonMultiPart = inputs.length < 2 + if (isNonMultiPart) { + val codecs = flattened.content - if (inputs.length < 2) { - // non multi-part - codecs.headOption.map { codec => + // noinspection SimplifyUnlessInspection + if (codecs.isEmpty) ZIO.unit + else { + val codec = codecs.head codec .decodeFromBody(body, config) .mapBoth( - { err => HttpCodecError.MalformedBody(err.getMessage(), Some(err)) }, + { err => HttpCodecError.MalformedBody(err.getMessage, Some(err)) }, result => inputs(0) = result, ) - }.getOrElse(ZIO.unit) + } } else { // multi-part decodeForm(body.asMultipartFormStream, inputs, config) *> check(inputs) From 1f8ef1ff69c303d7e821ee4ef86205e5365fb3fe Mon Sep 17 00:00:00 2001 From: Oto Brglez Date: Tue, 3 Dec 2024 17:57:25 +0100 Subject: [PATCH 4/4] ZClient enhancements for URL and path (#3227) * Adding url and path improvements to ZClient * Adding simple test. --- zio-http/jvm/src/test/scala/zio/http/ClientSpec.scala | 10 ++++++++++ zio-http/shared/src/main/scala/zio/http/ZClient.scala | 8 ++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/zio-http/jvm/src/test/scala/zio/http/ClientSpec.scala b/zio-http/jvm/src/test/scala/zio/http/ClientSpec.scala index 51695c4c0d..48a0519b74 100644 --- a/zio-http/jvm/src/test/scala/zio/http/ClientSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/ClientSpec.scala @@ -120,6 +120,16 @@ object ClientSpec extends RoutesRunnableSpec { app.deploy(Request(headers = Headers(Header.Authorization.Unparsed("", "my-token")))).flatMap(_.body.asString) assertZIO(responseContent)(equalTo("my-token")) } @@ timeout(5.seconds), + test("URL and path manipulation on client level") { + for { + baseURL <- DynamicServer.httpURL + _ <- + Handler.ok.toRoutes.deployAndRequest { c => + (c.updatePath(_ / "my-service") @@ ZClientAspect.requestLogging()).batched.get("/hello") + }.runZIO(()) + loggedUrl <- ZTestLogger.logOutput.map(_.collectFirst { case m => m.annotations("url") }.mkString) + } yield assertTrue(loggedUrl == baseURL + "/my-service/hello") + }, ) override def spec = { diff --git a/zio-http/shared/src/main/scala/zio/http/ZClient.scala b/zio-http/shared/src/main/scala/zio/http/ZClient.scala index 014d77cca6..ce6d47b332 100644 --- a/zio-http/shared/src/main/scala/zio/http/ZClient.scala +++ b/zio-http/shared/src/main/scala/zio/http/ZClient.scala @@ -151,8 +151,10 @@ final case class ZClient[-Env, ReqEnv, -In, +Err, +Out]( def path(path: String): ZClient[Env, ReqEnv, In, Err, Out] = self.path(Path(path)) - def path(path: Path): ZClient[Env, ReqEnv, In, Err, Out] = - copy(url = url.copy(path = path)) + def path(path: Path): ZClient[Env, ReqEnv, In, Err, Out] = updatePath(_ => path) + + def updatePath(f: Path => Path): ZClient[Env, ReqEnv, In, Err, Out] = + copy(url = url.copy(path = f(url.path))) def patch(suffix: String)(implicit ev: Body <:< In, trace: Trace): ZIO[Env & ReqEnv, Err, Out] = request(Method.PATCH, suffix)(ev(Body.empty)) @@ -263,6 +265,8 @@ final case class ZClient[-Env, ReqEnv, -In, +Err, +Out]( def uri(uri: URI): ZClient[Env, ReqEnv, In, Err, Out] = url(URL.fromURI(uri).getOrElse(URL.empty)) def url(url: URL): ZClient[Env, ReqEnv, In, Err, Out] = copy(url = url) + + def updateURL(f: URL => URL): ZClient[Env, ReqEnv, In, Err, Out] = copy(url = f(url)) } object ZClient extends ZClientPlatformSpecific {