From fa5a7ec81f114e755eb1e6668442bdb3695f8e8f Mon Sep 17 00:00:00 2001 From: shrutiverma97 Date: Mon, 21 Feb 2022 12:55:41 +0530 Subject: [PATCH 1/7] perf: added when operator in http --- .../scala/example/PlainTextBenchmarkServer.scala | 4 ++-- zio-http/src/main/scala/zhttp/http/Http.scala | 16 ++++++++++++++++ zio-http/src/main/scala/zhttp/http/Request.scala | 14 +++++++++++++- .../src/main/scala/zhttp/service/Handler.scala | 2 ++ .../src/test/scala/zhttp/http/HttpSpec.scala | 14 +++++++++++++- 5 files changed, 46 insertions(+), 4 deletions(-) diff --git a/example/src/main/scala/example/PlainTextBenchmarkServer.scala b/example/src/main/scala/example/PlainTextBenchmarkServer.scala index 882ea286ed..1d1e9fb301 100644 --- a/example/src/main/scala/example/PlainTextBenchmarkServer.scala +++ b/example/src/main/scala/example/PlainTextBenchmarkServer.scala @@ -27,8 +27,8 @@ object Main extends App { .provideCustomLayer(ServerChannelFactory.auto ++ EventLoopGroup.auto(8)) .exitCode } - - private def app(response: Response) = Http.response(response) + private val path = "/home" + private def app(response: Response) = Http.response(response).whenPath(path) private def server(response: Response) = Server.app(app(response)) ++ diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index 55ccc8bed6..dc9375585b 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -360,6 +360,12 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => final def unwrap[R1 <: R, E1 >: E, C](implicit ev: B <:< ZIO[R1, E1, C]): Http[R1, E1, A, C] = self.flatMap(Http.fromZIO(_)) + /** + * Applies Http based only if the condition function evaluates to true + */ + final def when[A1 <: A](f: A1 => Boolean): Http[R, E, A1, B] = + Http.fromFunctionHExit[A1](a => if (f(a)) self.execute(a.asInstanceOf[A]) else HExit.empty) + /** * Widens the type of the output */ @@ -450,6 +456,16 @@ object Http { */ override def updateHeaders(update: Headers => Headers): HttpApp[R, E] = http.map(_.updateHeaders(update)) + /** + * Applies Http based on the path + */ + def whenPath(p: Path): HttpApp[R, E] = http.when((a: Request) => a.path.equals(p)) + + /** + * Applies Http based on the path as string + */ + def whenPath(p: String): HttpApp[R, E] = http.when((a: Request) => a.unsafeEncode.uri().contentEquals(p)) + private[zhttp] def compile[R1 <: R]( zExec: HttpRuntime[R1], settings: Server.Config[R1, Throwable], diff --git a/zio-http/src/main/scala/zhttp/http/Request.scala b/zio-http/src/main/scala/zhttp/http/Request.scala index 558fcfe5c4..b938b85880 100644 --- a/zio-http/src/main/scala/zhttp/http/Request.scala +++ b/zio-http/src/main/scala/zhttp/http/Request.scala @@ -1,6 +1,7 @@ package zhttp.http import io.netty.buffer.{ByteBuf, ByteBufUtil} +import io.netty.handler.codec.http.FullHttpRequest import zhttp.http.headers.HeaderExtension import zio.{Chunk, Task, UIO} @@ -21,6 +22,7 @@ trait Request extends HeaderExtension[Request] { self => override def method: Method = m override def url: URL = u override def headers: Headers = h + override def unsafeEncode: FullHttpRequest = self.unsafeEncode override def remoteAddress: Option[InetAddress] = self.remoteAddress override def data: HttpData = self.data } @@ -83,6 +85,11 @@ trait Request extends HeaderExtension[Request] { self => */ def setUrl(url: URL): Request = self.copy(url = url) + /** + * Gets the FullHttpRequest + */ + private[zhttp] def unsafeEncode: FullHttpRequest + /** * Gets the complete url */ @@ -102,16 +109,19 @@ object Request { headers: Headers = Headers.empty, remoteAddress: Option[InetAddress] = None, data: HttpData = HttpData.Empty, + jRequest: FullHttpRequest = null, ): Request = { val m = method val u = url val h = headers val ra = remoteAddress val d = data + val p = jRequest new Request { override def method: Method = m override def url: URL = u override def headers: Headers = h + override def unsafeEncode: FullHttpRequest = p override def remoteAddress: Option[InetAddress] = ra override def data: HttpData = d } @@ -126,8 +136,9 @@ object Request { headers: Headers = Headers.empty, remoteAddress: Option[InetAddress], content: HttpData = HttpData.empty, + jRequest: FullHttpRequest = null, ): UIO[Request] = - UIO(Request(method, url, headers, remoteAddress, content)) + UIO(Request(method, url, headers, remoteAddress, content, jRequest)) /** * Lift request to TypedRequest with option to extract params @@ -137,6 +148,7 @@ object Request { override def method: Method = req.method override def remoteAddress: Option[InetAddress] = req.remoteAddress override def url: URL = req.url + override def unsafeEncode: FullHttpRequest = req.unsafeEncode override def data: HttpData = req.data } diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index 5667e673cd..37e9853050 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -33,6 +33,8 @@ private[zhttp] final case class Handler[R]( override def headers: Headers = Headers.make(jReq.headers()) + override def unsafeEncode: FullHttpRequest = jReq + override def remoteAddress: Option[InetAddress] = { ctx.channel().remoteAddress() match { case m: InetSocketAddress => Some(m.getAddress) diff --git a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala index 4447414453..995204ad66 100644 --- a/zio-http/src/test/scala/zhttp/http/HttpSpec.scala +++ b/zio-http/src/test/scala/zhttp/http/HttpSpec.scala @@ -305,6 +305,18 @@ object HttpSpec extends DefaultRunnableSpec with HExitAssertion { assert(actual)(isSuccess(equalTo("bar"))) } } - }, + } + + suite("when")( + test("should execute http only when condition applies") { + val app = Http.succeed(1).when((_: Any) => true) + val actual = app.execute(0) + assert(actual)(isSuccess(equalTo(1))) + } + + test("should not execute http when condition doesn't apply") { + val app = Http.succeed(1).when((_: Any) => false) + val actual = app.execute(0) + assert(actual)(isEmpty) + }, + ), ) @@ timeout(10 seconds) } From e16cc35fd565f760eea070d3883145a87e0f8b8b Mon Sep 17 00:00:00 2001 From: shrutiverma97 Date: Mon, 21 Feb 2022 13:07:06 +0530 Subject: [PATCH 2/7] build fix --- example/src/main/scala/example/PlainTextBenchmarkServer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/src/main/scala/example/PlainTextBenchmarkServer.scala b/example/src/main/scala/example/PlainTextBenchmarkServer.scala index 1d1e9fb301..42338d058c 100644 --- a/example/src/main/scala/example/PlainTextBenchmarkServer.scala +++ b/example/src/main/scala/example/PlainTextBenchmarkServer.scala @@ -27,7 +27,7 @@ object Main extends App { .provideCustomLayer(ServerChannelFactory.auto ++ EventLoopGroup.auto(8)) .exitCode } - private val path = "/home" + private val path = "/plaintext" private def app(response: Response) = Http.response(response).whenPath(path) private def server(response: Response) = From 3f4d79527e53c4a4c1ebceefc98afc795a8508bd Mon Sep 17 00:00:00 2001 From: shrutiverma97 Date: Wed, 23 Feb 2022 12:24:09 +0530 Subject: [PATCH 3/7] removed jrequest from constructor --- .../src/main/scala/zhttp/http/Request.scala | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Request.scala b/zio-http/src/main/scala/zhttp/http/Request.scala index b938b85880..47aaa94df5 100644 --- a/zio-http/src/main/scala/zhttp/http/Request.scala +++ b/zio-http/src/main/scala/zhttp/http/Request.scala @@ -1,7 +1,7 @@ package zhttp.http import io.netty.buffer.{ByteBuf, ByteBufUtil} -import io.netty.handler.codec.http.FullHttpRequest +import io.netty.handler.codec.http.{DefaultFullHttpRequest, FullHttpRequest} import zhttp.http.headers.HeaderExtension import zio.{Chunk, Task, UIO} @@ -109,19 +109,22 @@ object Request { headers: Headers = Headers.empty, remoteAddress: Option[InetAddress] = None, data: HttpData = HttpData.Empty, - jRequest: FullHttpRequest = null, ): Request = { - val m = method - val u = url - val h = headers - val ra = remoteAddress - val d = data - val p = jRequest + val m = method + val u = url + val h = headers + val ra = remoteAddress + val d = data + val jVersion = Version.`HTTP/1.1`.toJava + val path = url.relative.encode + + // TODO: we should also add a default user-agent req header as some APIs might reject requests without it. + val jReq = new DefaultFullHttpRequest(jVersion, method.toJava, path) new Request { override def method: Method = m override def url: URL = u override def headers: Headers = h - override def unsafeEncode: FullHttpRequest = p + override def unsafeEncode: FullHttpRequest = jReq override def remoteAddress: Option[InetAddress] = ra override def data: HttpData = d } @@ -136,9 +139,8 @@ object Request { headers: Headers = Headers.empty, remoteAddress: Option[InetAddress], content: HttpData = HttpData.empty, - jRequest: FullHttpRequest = null, ): UIO[Request] = - UIO(Request(method, url, headers, remoteAddress, content, jRequest)) + UIO(Request(method, url, headers, remoteAddress, content)) /** * Lift request to TypedRequest with option to extract params From b00e4ad345bfa06d5e257f56639711c5ed85378a Mon Sep 17 00:00:00 2001 From: shrutiverma97 Date: Wed, 23 Feb 2022 12:33:21 +0530 Subject: [PATCH 4/7] fmt --- zio-http/src/main/scala/zhttp/http/Request.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Request.scala b/zio-http/src/main/scala/zhttp/http/Request.scala index 47aaa94df5..baeff99a9e 100644 --- a/zio-http/src/main/scala/zhttp/http/Request.scala +++ b/zio-http/src/main/scala/zhttp/http/Request.scala @@ -117,9 +117,8 @@ object Request { val d = data val jVersion = Version.`HTTP/1.1`.toJava val path = url.relative.encode + val jReq = new DefaultFullHttpRequest(jVersion, method.toJava, path) - // TODO: we should also add a default user-agent req header as some APIs might reject requests without it. - val jReq = new DefaultFullHttpRequest(jVersion, method.toJava, path) new Request { override def method: Method = m override def url: URL = u From 117821009c7dc3bc7e117e1126b984518a7908d7 Mon Sep 17 00:00:00 2001 From: shrutiverma97 Date: Wed, 23 Feb 2022 17:21:02 +0530 Subject: [PATCH 5/7] unsafeEncode: HttpRequest --- .../src/main/scala/zhttp/http/Request.scala | 29 ++++++++++--------- .../main/scala/zhttp/service/Handler.scala | 2 +- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Request.scala b/zio-http/src/main/scala/zhttp/http/Request.scala index baeff99a9e..144c6c8407 100644 --- a/zio-http/src/main/scala/zhttp/http/Request.scala +++ b/zio-http/src/main/scala/zhttp/http/Request.scala @@ -1,7 +1,7 @@ package zhttp.http import io.netty.buffer.{ByteBuf, ByteBufUtil} -import io.netty.handler.codec.http.{DefaultFullHttpRequest, FullHttpRequest} +import io.netty.handler.codec.http.{DefaultFullHttpRequest, HttpRequest} import zhttp.http.headers.HeaderExtension import zio.{Chunk, Task, UIO} @@ -22,7 +22,7 @@ trait Request extends HeaderExtension[Request] { self => override def method: Method = m override def url: URL = u override def headers: Headers = h - override def unsafeEncode: FullHttpRequest = self.unsafeEncode + override def unsafeEncode: HttpRequest = self.unsafeEncode override def remoteAddress: Option[InetAddress] = self.remoteAddress override def data: HttpData = self.data } @@ -86,9 +86,9 @@ trait Request extends HeaderExtension[Request] { self => def setUrl(url: URL): Request = self.copy(url = url) /** - * Gets the FullHttpRequest + * Gets the HttpRequest */ - private[zhttp] def unsafeEncode: FullHttpRequest + private[zhttp] def unsafeEncode: HttpRequest /** * Gets the complete url @@ -110,20 +110,21 @@ object Request { remoteAddress: Option[InetAddress] = None, data: HttpData = HttpData.Empty, ): Request = { - val m = method - val u = url - val h = headers - val ra = remoteAddress - val d = data - val jVersion = Version.`HTTP/1.1`.toJava - val path = url.relative.encode - val jReq = new DefaultFullHttpRequest(jVersion, method.toJava, path) + val m = method + val u = url + val h = headers + val ra = remoteAddress + val d = data new Request { override def method: Method = m override def url: URL = u override def headers: Headers = h - override def unsafeEncode: FullHttpRequest = jReq + override def unsafeEncode: HttpRequest = { + val jVersion = Version.`HTTP/1.1`.toJava + val path = url.relative.encode + new DefaultFullHttpRequest(jVersion, method.toJava, path) + } override def remoteAddress: Option[InetAddress] = ra override def data: HttpData = d } @@ -149,7 +150,7 @@ object Request { override def method: Method = req.method override def remoteAddress: Option[InetAddress] = req.remoteAddress override def url: URL = req.url - override def unsafeEncode: FullHttpRequest = req.unsafeEncode + override def unsafeEncode: HttpRequest = req.unsafeEncode override def data: HttpData = req.data } diff --git a/zio-http/src/main/scala/zhttp/service/Handler.scala b/zio-http/src/main/scala/zhttp/service/Handler.scala index 37e9853050..4c2076ccfe 100644 --- a/zio-http/src/main/scala/zhttp/service/Handler.scala +++ b/zio-http/src/main/scala/zhttp/service/Handler.scala @@ -33,7 +33,7 @@ private[zhttp] final case class Handler[R]( override def headers: Headers = Headers.make(jReq.headers()) - override def unsafeEncode: FullHttpRequest = jReq + override def unsafeEncode: HttpRequest = jReq override def remoteAddress: Option[InetAddress] = { ctx.channel().remoteAddress() match { From da0837f3c99cc15ab2206af8de0a85597d86bcf4 Mon Sep 17 00:00:00 2001 From: shrutiverma97 Date: Wed, 23 Feb 2022 18:37:26 +0530 Subject: [PATCH 6/7] added when primitive --- zio-http/src/main/scala/zhttp/http/Http.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index edde303216..b134aa7e8b 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -363,8 +363,8 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => /** * Applies Http based only if the condition function evaluates to true */ - final def when[A1 <: A](f: A1 => Boolean): Http[R, E, A1, B] = - Http.fromFunctionHExit[A1](a => if (f(a)) self.execute(a.asInstanceOf[A]) else HExit.empty) + final def when[A2 <: A](f: A2 => Boolean): Http[R, E, A2, B] = + Http.When(f, self) /** * Widens the type of the output @@ -419,6 +419,8 @@ sealed trait Http[-R, +E, -A, +B] extends (A => ZIO[R, Option[E], B]) { self => self.execute(a).foldExit(ee(_).execute(a), bb(_).execute(a), dd.execute(a)) case RunMiddleware(app, mid) => mid(app).execute(a) + + case When(f, other) => if (f(a)) other.execute(a) else HExit.empty } } @@ -845,5 +847,7 @@ object Http { private final case class FromHExit[R, E, B](h: HExit[R, E, B]) extends Http[R, E, Any, B] + private final case class When[R, E, A, B](f: A => Boolean, other: Http[R, E, A, B]) extends Http[R, E, A, B] + private case object Identity extends Http[Any, Nothing, Any, Nothing] } From 2635505799f5843657bdfc51b0963164e900682f Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Thu, 24 Feb 2022 13:15:30 +0530 Subject: [PATCH 7/7] refactor: rename `whenPath` to `whenPathEq` --- .../src/main/scala/example/PlainTextBenchmarkServer.scala | 2 +- zio-http/src/main/scala/zhttp/http/Http.scala | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/example/src/main/scala/example/PlainTextBenchmarkServer.scala b/example/src/main/scala/example/PlainTextBenchmarkServer.scala index 8465873a29..17952e0e5d 100644 --- a/example/src/main/scala/example/PlainTextBenchmarkServer.scala +++ b/example/src/main/scala/example/PlainTextBenchmarkServer.scala @@ -28,7 +28,7 @@ object Main extends App { .exitCode } private val path = "/plaintext" - private def app(response: Response) = Http.fromHExit(HExit.succeed(response)).whenPath(path) + private def app(response: Response) = Http.fromHExit(HExit.succeed(response)).whenPathEq(path) private def server(response: Response) = Server.app(app(response)) ++ diff --git a/zio-http/src/main/scala/zhttp/http/Http.scala b/zio-http/src/main/scala/zhttp/http/Http.scala index b134aa7e8b..7c77873c42 100644 --- a/zio-http/src/main/scala/zhttp/http/Http.scala +++ b/zio-http/src/main/scala/zhttp/http/Http.scala @@ -462,12 +462,12 @@ object Http { /** * Applies Http based on the path */ - def whenPath(p: Path): HttpApp[R, E] = http.when((a: Request) => a.path.equals(p)) + def whenPathEq(p: Path): HttpApp[R, E] = http.whenPathEq(p.toString) /** * Applies Http based on the path as string */ - def whenPath(p: String): HttpApp[R, E] = http.when((a: Request) => a.unsafeEncode.uri().contentEquals(p)) + def whenPathEq(p: String): HttpApp[R, E] = http.when(_.unsafeEncode.uri().contentEquals(p)) private[zhttp] def compile[R1 <: R]( zExec: HttpRuntime[R1], @@ -843,11 +843,11 @@ object Http { private case class Attempt[A](a: () => A) extends Http[Any, Nothing, Any, A] - private case object Empty extends Http[Any, Nothing, Any, Nothing] - private final case class FromHExit[R, E, B](h: HExit[R, E, B]) extends Http[R, E, Any, B] private final case class When[R, E, A, B](f: A => Boolean, other: Http[R, E, A, B]) extends Http[R, E, A, B] + private case object Empty extends Http[Any, Nothing, Any, Nothing] + private case object Identity extends Http[Any, Nothing, Any, Nothing] }