From b188e62848458beed7672c1150757e75fdedf989 Mon Sep 17 00:00:00 2001 From: Nabil Abdel-Hafeez <7283535+987Nabil@users.noreply.github.com> Date: Sun, 21 Apr 2024 09:06:04 +0200 Subject: [PATCH 1/3] Remove Routes from HttpApp --- README.md | 6 +-- docs/index.md | 6 +-- docs/reference/body.md | 2 +- docs/reference/headers.md | 2 +- docs/reference/middleware.md | 2 +- docs/reference/path_codec.md | 2 +- docs/reference/response.md | 6 +-- docs/reference/routes.md | 2 +- docs/reference/server.md | 4 +- .../scala/example/AuthenticationServer.scala | 2 +- .../src/main/scala/example/BasicAuth.scala | 4 +- .../main/scala/example/ConcreteEntity.scala | 2 +- .../main/scala/example/GracefulShutdown.scala | 2 +- .../scala/example/HelloWorldWithCORS.scala | 4 +- .../scala/example/HelloWorldWithMetrics.scala | 4 +- .../example/HelloWorldWithMiddlewares.scala | 2 +- .../main/scala/example/HtmlTemplating.scala | 2 +- .../main/scala/example/HttpsHelloWorld.scala | 2 +- .../scala/example/MultipartFormData.scala | 2 +- .../example/MultipartFormDataStreaming.scala | 2 +- .../example/PlainTextBenchmarkServer.scala | 6 +-- .../src/main/scala/example/SSEServer.scala | 2 +- .../example/ServerSentEventEndpoint.scala | 2 +- .../example/SimpleEffectBenchmarkServer.scala | 2 +- .../scala/example/StreamingResponse.scala | 2 +- .../scala/example/WebSocketAdvanced.scala | 2 +- .../main/scala/example/WebSocketEcho.scala | 2 +- .../src/main/scala/zio/http/TestServer.scala | 6 +-- .../zio/http/netty/server/NettyDriver.scala | 11 ++-- .../netty/server/ServerInboundHandler.scala | 2 +- .../scala/zio/http/netty/server/package.scala | 2 +- .../test/scala/zio/http/DynamicAppTest.scala | 4 +- .../zio/http/ResponseCompressionSpec.scala | 2 +- .../jvm/src/test/scala/zio/http/SSLSpec.scala | 2 +- .../src/test/scala/zio/http/ServerSpec.scala | 6 +-- .../scala/zio/http/ZClientAspectSpec.scala | 4 +- .../zio/http/internal/DynamicServer.scala | 16 +++--- .../http/internal/HttpAppTestExtensions.scala | 6 +-- .../zio/http/internal/HttpRunnableSpec.scala | 6 +-- .../internal/middlewares/MetricsSpec.scala | 2 +- .../http/internal/middlewares/WebSpec.scala | 2 +- .../src/main/scala/zio/http/Driver.scala | 2 +- .../src/main/scala/zio/http/Handler.scala | 6 +-- .../src/main/scala/zio/http/HttpApp.scala | 52 +++++++++++-------- .../src/main/scala/zio/http/Middleware.scala | 6 ++- .../src/main/scala/zio/http/Route.scala | 2 +- .../src/main/scala/zio/http/Routes.scala | 4 +- .../src/main/scala/zio/http/Server.scala | 8 +-- .../main/scala/zio/http/WebSocketApp.scala | 2 +- .../scala/zio/http/endpoint/Endpoint.scala | 2 +- 50 files changed, 119 insertions(+), 114 deletions(-) diff --git a/README.md b/README.md index 78df5f24c8..0e8069274c 100644 --- a/README.md +++ b/README.md @@ -36,15 +36,15 @@ import zio._ import zio.http._ object GreetingServer extends ZIOAppDefault { - val app = + val routes = Routes( Method.GET / "greet" -> handler { (req: Request) => val name = req.queryParamToOrElse("name", "World") Response.text(s"Hello $name!") } - ).toHttpApp + ) - def run = Server.serve(app).provide(Server.default) + def run = Server.serve(routes).provide(Server.default) } ``` diff --git a/docs/index.md b/docs/index.md index 290841b478..0645290c10 100644 --- a/docs/index.md +++ b/docs/index.md @@ -36,15 +36,15 @@ import zio._ import zio.http._ object GreetingServer extends ZIOAppDefault { - val app = + val routes = Routes( Method.GET / "greet" -> handler { (req: Request) => val name = req.queryParamToOrElse("name", "World") Response.text(s"Hello $name!") } - ).toHttpApp + ) - def run = Server.serve(app).provide(Server.default) + def run = Server.serve(routes).provide(Server.default) } ``` diff --git a/docs/reference/body.md b/docs/reference/body.md index 5781689ac0..a4126c0b4f 100644 --- a/docs/reference/body.md +++ b/docs/reference/body.md @@ -20,7 +20,7 @@ import zio._ import zio.http._ object HelloExample extends ZIOAppDefault { - val app: HttpApp[Any] = + val app: HttpApp[Any, Response] = Routes( Method.GET / "hello" -> handler { req: Request => diff --git a/docs/reference/headers.md b/docs/reference/headers.md index 538523f17e..b30e443335 100644 --- a/docs/reference/headers.md +++ b/docs/reference/headers.md @@ -201,7 +201,7 @@ object SimpleResponseDispatcher extends ZIOAppDefault { // Create a message as a Chunk[Byte] val message = Chunk.fromArray("Hello world !\r\n".getBytes(Charsets.Http)) // Use `Http.collect` to match on route - val app: HttpApp[Any] = + val app: HttpApp[Any, Response] = Routes( // Simple (non-stream) based route Method.GET / "health" -> handler(Response.ok), diff --git a/docs/reference/middleware.md b/docs/reference/middleware.md index d8eb82e762..c1672ac666 100644 --- a/docs/reference/middleware.md +++ b/docs/reference/middleware.md @@ -275,7 +275,7 @@ The `Middleware.timeout` middleware is used to set a timeout for the HTTP reques ```scala mdoc:invisible import zio.http._ -val httpApp: HttpApp[Any] = Handler.ok.toHttpApp +val httpApp: HttpApp[Any, Response] = Handler.ok.toHttpApp ``` ```scala mdoc:compile-only diff --git a/docs/reference/path_codec.md b/docs/reference/path_codec.md index d568780198..a9424b53d0 100644 --- a/docs/reference/path_codec.md +++ b/docs/reference/path_codec.md @@ -174,7 +174,7 @@ object Main extends ZIOAppDefault { val userId: PathCodec[UserId] = int("user-id").transformOrFailLeft(UserId.apply)(_.value) - val httpApp: HttpApp[Any] = + val httpApp: HttpApp[Any, Response] = Routes( Method.GET / "users" / userId -> Handler.fromFunctionHandler[(UserId, Request)] { case (userId: UserId, request: Request) => diff --git a/docs/reference/response.md b/docs/reference/response.md index e4ae967c3d..6e85cb10df 100644 --- a/docs/reference/response.md +++ b/docs/reference/response.md @@ -19,7 +19,7 @@ import zio._ import zio.http._ object HelloWorldExample extends ZIOAppDefault { - val app: HttpApp[Any] = + val app: HttpApp[Any, Response] = Routes( Method.GET / "text" -> handler { @@ -295,7 +295,7 @@ import zio.http._ object WebsocketExample extends ZIOAppDefault { - val app: HttpApp[Any] = { + val app: HttpApp[Any, Response] = { Routes( Method.GET / "echo" -> handler { Response.fromSocketApp( @@ -353,4 +353,4 @@ Response.ok.addFlash(flash) ### Working with Headers -There are various methods to work with headers in `Response` which we have discussed in the [Headers](headers.md#headers-operations) page. \ No newline at end of file +There are various methods to work with headers in `Response` which we have discussed in the [Headers](headers.md#headers-operations) page. diff --git a/docs/reference/routes.md b/docs/reference/routes.md index 8c178993ff..e4a516cf7a 100644 --- a/docs/reference/routes.md +++ b/docs/reference/routes.md @@ -221,7 +221,7 @@ import zio._ import zio.http._ object HelloWorld extends ZIOAppDefault { - val app: HttpApp[Any] = Handler.ok.toHttpApp + val app: HttpApp[Any, Response] = Handler.ok.toHttpApp override def run = Server.serve(app).provide(Server.default) } diff --git a/docs/reference/server.md b/docs/reference/server.md index 2b0decd668..0c9d0b4c32 100644 --- a/docs/reference/server.md +++ b/docs/reference/server.md @@ -15,7 +15,7 @@ Assuming we have written an `HttpApp`: import zio.http._ import zio._ -def app: HttpApp[Any] = +def app: HttpApp[Any, Response] = Routes( Method.GET / "hello" -> handler(Response.text("Hello, World!")) @@ -405,7 +405,7 @@ import zio.stream.{ZSink, ZStream} object RequestStreamingServerExample extends ZIOAppDefault { def logBytes = (b: Byte) => ZIO.log(s"received byte: $b") - private val app: HttpApp[Any] = + private val app: HttpApp[Any, Response] = Routes( Method.POST / "upload-stream" / "simple" -> handler { (req: Request) => for { diff --git a/zio-http-example/src/main/scala/example/AuthenticationServer.scala b/zio-http-example/src/main/scala/example/AuthenticationServer.scala index 888ed95417..c689754e31 100644 --- a/zio-http-example/src/main/scala/example/AuthenticationServer.scala +++ b/zio-http-example/src/main/scala/example/AuthenticationServer.scala @@ -43,7 +43,7 @@ object AuthenticationServer extends ZIOAppDefault { } }) - def app: HttpApp[Any] = + def app: HttpApp[Any, Response] = Routes( // A route that is accessible only via a jwt token Method.GET / "profile" / "me" -> handler { (_: Request) => diff --git a/zio-http-example/src/main/scala/example/BasicAuth.scala b/zio-http-example/src/main/scala/example/BasicAuth.scala index 31a6e9e7cb..b86ba1ce44 100644 --- a/zio-http-example/src/main/scala/example/BasicAuth.scala +++ b/zio-http-example/src/main/scala/example/BasicAuth.scala @@ -9,7 +9,7 @@ import zio.http.codec.PathCodec.string object BasicAuth extends ZIOAppDefault { // Http app that requires a JWT claim - val user: HttpApp[Any] = Routes( + val user: HttpApp[Any, Response] = Routes( Method.GET / "user" / string("name") / "greet" -> handler { (name: String, _: Request) => Response.text(s"Welcome to the ZIO party! ${name}") @@ -17,7 +17,7 @@ object BasicAuth extends ZIOAppDefault { ).toHttpApp // Composing all the HttpApps together - val app: HttpApp[Any] = user @@ basicAuth("admin", "admin") + val app: HttpApp[Any, Response] = user @@ basicAuth("admin", "admin") // Run it like any simple app val run = Server.serve(app).provide(Server.default) diff --git a/zio-http-example/src/main/scala/example/ConcreteEntity.scala b/zio-http-example/src/main/scala/example/ConcreteEntity.scala index 55df5f7415..4c7b3edfac 100644 --- a/zio-http-example/src/main/scala/example/ConcreteEntity.scala +++ b/zio-http-example/src/main/scala/example/ConcreteEntity.scala @@ -19,7 +19,7 @@ object ConcreteEntity extends ZIOAppDefault { UserCreated(2) } - val app: HttpApp[Any] = + val app: HttpApp[Any, Response] = user .contramap[Request](req => CreateUser(req.path.encode)) // Http[Any, Nothing, Request, UserCreated] .map(userCreated => Response.text(userCreated.id.toString)) // Http[Any, Nothing, Request, Response] diff --git a/zio-http-example/src/main/scala/example/GracefulShutdown.scala b/zio-http-example/src/main/scala/example/GracefulShutdown.scala index e2f304e17e..a949c9b90d 100644 --- a/zio-http-example/src/main/scala/example/GracefulShutdown.scala +++ b/zio-http-example/src/main/scala/example/GracefulShutdown.scala @@ -22,7 +22,7 @@ import zio.http._ object GracefulShutdown extends ZIOAppDefault { - val app: HttpApp[Any] = Handler + val app: HttpApp[Any, Response] = Handler .fromFunctionZIO[Request] { _ => ZIO.sleep(10.seconds).debug("request handler delay done").as(Response.text("done")) } diff --git a/zio-http-example/src/main/scala/example/HelloWorldWithCORS.scala b/zio-http-example/src/main/scala/example/HelloWorldWithCORS.scala index 84d05c8b49..476d380c22 100644 --- a/zio-http-example/src/main/scala/example/HelloWorldWithCORS.scala +++ b/zio-http-example/src/main/scala/example/HelloWorldWithCORS.scala @@ -18,12 +18,12 @@ object HelloWorldWithCORS extends ZIOAppDefault { }, ) - val backend: HttpApp[Any] = + val backend: HttpApp[Any, Response] = Routes( Method.GET / "json" -> handler(Response.json("""{"message": "Hello World!"}""")), ).toHttpApp @@ cors(config) - val frontend: HttpApp[Any] = + val frontend: HttpApp[Any, Response] = Routes( Method.GET / PathCodec.empty -> handler( Response.html( diff --git a/zio-http-example/src/main/scala/example/HelloWorldWithMetrics.scala b/zio-http-example/src/main/scala/example/HelloWorldWithMetrics.scala index 2a86223ba4..e71e12b022 100644 --- a/zio-http-example/src/main/scala/example/HelloWorldWithMetrics.scala +++ b/zio-http-example/src/main/scala/example/HelloWorldWithMetrics.scala @@ -9,7 +9,7 @@ import zio.http._ object HelloWorldWithMetrics extends ZIOAppDefault { - val backend: HttpApp[Any] = + val backend: HttpApp[Any, Response] = Routes( Method.GET / "json" -> handler((req: Request) => ZIO.succeed(Response.json("""{"message": "Hello World!"}""")) @@ Metric @@ -19,7 +19,7 @@ object HelloWorldWithMetrics extends ZIOAppDefault { Method.GET / "forbidden" -> handler(ZIO.succeed(Response.forbidden)), ).toHttpApp @@ Middleware.metrics() - val metrics: HttpApp[PrometheusPublisher] = + val metrics: HttpApp[PrometheusPublisher, Response] = Routes( Method.GET / "metrics" -> handler(ZIO.serviceWithZIO[PrometheusPublisher](_.get.map(Response.text))), ).toHttpApp diff --git a/zio-http-example/src/main/scala/example/HelloWorldWithMiddlewares.scala b/zio-http-example/src/main/scala/example/HelloWorldWithMiddlewares.scala index 38c6af9032..4d01d2cf7c 100644 --- a/zio-http-example/src/main/scala/example/HelloWorldWithMiddlewares.scala +++ b/zio-http-example/src/main/scala/example/HelloWorldWithMiddlewares.scala @@ -8,7 +8,7 @@ import zio.http._ object HelloWorldWithMiddlewares extends ZIOAppDefault { - val app: HttpApp[Any] = Routes( + val app: HttpApp[Any, Response] = Routes( // this will return result instantly Method.GET / "text" -> handler(ZIO.succeed(Response.text("Hello World!"))), // this will return result after 5 seconds, so with 3 seconds timeout it will fail diff --git a/zio-http-example/src/main/scala/example/HtmlTemplating.scala b/zio-http-example/src/main/scala/example/HtmlTemplating.scala index 4400c4e945..216d12642c 100644 --- a/zio-http-example/src/main/scala/example/HtmlTemplating.scala +++ b/zio-http-example/src/main/scala/example/HtmlTemplating.scala @@ -8,7 +8,7 @@ object HtmlTemplating extends ZIOAppDefault { // Importing everything from `zio.html` import zio.http.template._ - def app: HttpApp[Any] = { + def app: HttpApp[Any, Response] = { // Html response takes in a `Html` instance. Handler.html { diff --git a/zio-http-example/src/main/scala/example/HttpsHelloWorld.scala b/zio-http-example/src/main/scala/example/HttpsHelloWorld.scala index 12de2f40e8..dc99f5a24d 100644 --- a/zio-http-example/src/main/scala/example/HttpsHelloWorld.scala +++ b/zio-http-example/src/main/scala/example/HttpsHelloWorld.scala @@ -6,7 +6,7 @@ import zio.http._ object HttpsHelloWorld extends ZIOAppDefault { // Create HTTP route - val app: HttpApp[Any] = Routes( + val app: HttpApp[Any, Response] = Routes( Method.GET / "text" -> handler(Response.text("Hello World!")), Method.GET / "json" -> handler(Response.json("""{"greetings": "Hello World!"}""")), ).toHttpApp diff --git a/zio-http-example/src/main/scala/example/MultipartFormData.scala b/zio-http-example/src/main/scala/example/MultipartFormData.scala index df821e52cf..8beb8018cb 100644 --- a/zio-http-example/src/main/scala/example/MultipartFormData.scala +++ b/zio-http-example/src/main/scala/example/MultipartFormData.scala @@ -6,7 +6,7 @@ import zio.http._ object MultipartFormData extends ZIOAppDefault { - private val app: HttpApp[Any] = + private val app: HttpApp[Any, Response] = Routes( Method.POST / "upload" -> handler { (req: Request) => diff --git a/zio-http-example/src/main/scala/example/MultipartFormDataStreaming.scala b/zio-http-example/src/main/scala/example/MultipartFormDataStreaming.scala index 382df55882..53ae425d43 100644 --- a/zio-http-example/src/main/scala/example/MultipartFormDataStreaming.scala +++ b/zio-http-example/src/main/scala/example/MultipartFormDataStreaming.scala @@ -8,7 +8,7 @@ import zio.http._ object MultipartFormDataStreaming extends ZIOAppDefault { - private val app: HttpApp[Any] = + private val app: HttpApp[Any, Response] = Routes( Method.POST / "upload-simple" -> handler { (req: Request) => for { diff --git a/zio-http-example/src/main/scala/example/PlainTextBenchmarkServer.scala b/zio-http-example/src/main/scala/example/PlainTextBenchmarkServer.scala index c6be74ee7a..c352ed68f8 100644 --- a/zio-http-example/src/main/scala/example/PlainTextBenchmarkServer.scala +++ b/zio-http-example/src/main/scala/example/PlainTextBenchmarkServer.scala @@ -29,13 +29,13 @@ object PlainTextBenchmarkServer extends ZIOAppDefault { // .serverTime .addHeader(Header.Server(STATIC_SERVER_NAME)) - private def plainTextApp(response: Response): HttpApp[Any] = + private def plainTextApp(response: Response): HttpApp[Any, Response] = Routes(Method.GET / plaintextPath -> Handler.fromResponse(response)).toHttpApp - private def jsonApp(json: Response): HttpApp[Any] = + private def jsonApp(json: Response): HttpApp[Any, Response] = Routes(Method.GET / jsonPath -> Handler.fromResponse(json)).toHttpApp - val app: HttpApp[Any] = plainTextApp(frozenPlainTextResponse) ++ jsonApp(frozenJsonResponse) + val app: HttpApp[Any, Response] = plainTextApp(frozenPlainTextResponse) ++ jsonApp(frozenJsonResponse) private val config = Server.Config.default .port(8080) diff --git a/zio-http-example/src/main/scala/example/SSEServer.scala b/zio-http-example/src/main/scala/example/SSEServer.scala index a3afa686e9..4b4dc86c54 100644 --- a/zio-http-example/src/main/scala/example/SSEServer.scala +++ b/zio-http-example/src/main/scala/example/SSEServer.scala @@ -14,7 +14,7 @@ object SSEServer extends ZIOAppDefault { val stream: ZStream[Any, Nothing, ServerSentEvent] = ZStream.repeatWithSchedule(ServerSentEvent(ISO_LOCAL_TIME.format(LocalDateTime.now)), Schedule.spaced(1.second)) - val app: HttpApp[Any] = + val app: HttpApp[Any, Response] = Routes( Method.GET / "sse" -> handler(Response.fromServerSentEvents(stream)), diff --git a/zio-http-example/src/main/scala/example/ServerSentEventEndpoint.scala b/zio-http-example/src/main/scala/example/ServerSentEventEndpoint.scala index c31ed5cdb6..742e40a76d 100644 --- a/zio-http-example/src/main/scala/example/ServerSentEventEndpoint.scala +++ b/zio-http-example/src/main/scala/example/ServerSentEventEndpoint.scala @@ -23,7 +23,7 @@ object ServerSentEventEndpoint extends ZIOAppDefault { val sseRoute = sseEndpoint.implement(Handler.succeed(stream)) - val app: HttpApp[Any] = sseRoute.toHttpApp + val app: HttpApp[Any, Response] = sseRoute.toHttpApp override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = { Server.serve(app).provide(Server.default).exitCode diff --git a/zio-http-example/src/main/scala/example/SimpleEffectBenchmarkServer.scala b/zio-http-example/src/main/scala/example/SimpleEffectBenchmarkServer.scala index 12ea29aea4..96d9a36d01 100644 --- a/zio-http-example/src/main/scala/example/SimpleEffectBenchmarkServer.scala +++ b/zio-http-example/src/main/scala/example/SimpleEffectBenchmarkServer.scala @@ -16,7 +16,7 @@ object SimpleEffectBenchmarkServer extends ZIOAppDefault { private val STATIC_SERVER_NAME = "zio-http" - private val app: HttpApp[Any] = Routes( + private val app: HttpApp[Any, Response] = Routes( Method.GET / "plaintext" -> handler( Response diff --git a/zio-http-example/src/main/scala/example/StreamingResponse.scala b/zio-http-example/src/main/scala/example/StreamingResponse.scala index e884b06a6f..efdb38c2eb 100644 --- a/zio-http-example/src/main/scala/example/StreamingResponse.scala +++ b/zio-http-example/src/main/scala/example/StreamingResponse.scala @@ -16,7 +16,7 @@ object StreamingResponse extends ZIOAppDefault { // Create a message as a Chunk[Byte] def message = Chunk.fromArray("Hello world !\r\n".getBytes(Charsets.Http)) - def app: HttpApp[Any] = Routes( + def app: HttpApp[Any, Response] = Routes( // Simple (non-stream) based route Method.GET / "health" -> handler(Response.ok), diff --git a/zio-http-example/src/main/scala/example/WebSocketAdvanced.scala b/zio-http-example/src/main/scala/example/WebSocketAdvanced.scala index 77f2ab1189..be1b918e4a 100644 --- a/zio-http-example/src/main/scala/example/WebSocketAdvanced.scala +++ b/zio-http-example/src/main/scala/example/WebSocketAdvanced.scala @@ -48,7 +48,7 @@ object WebSocketAdvanced extends ZIOAppDefault { } } - val app: HttpApp[Any] = + val app: HttpApp[Any, Response] = Routes( Method.GET / "greet" / string("name") -> handler { (name: String, _: Request) => Response.text(s"Greetings ${name}!") diff --git a/zio-http-example/src/main/scala/example/WebSocketEcho.scala b/zio-http-example/src/main/scala/example/WebSocketEcho.scala index df3e34a379..fa4217ab7c 100644 --- a/zio-http-example/src/main/scala/example/WebSocketEcho.scala +++ b/zio-http-example/src/main/scala/example/WebSocketEcho.scala @@ -21,7 +21,7 @@ object WebSocketEcho extends ZIOAppDefault { } } - private val app: HttpApp[Any] = + private val app: HttpApp[Any, Response] = Routes( Method.GET / "greet" / string("name") -> handler { (name: String, _: Request) => Response.text(s"Greetings {$name}!") diff --git a/zio-http-testkit/src/main/scala/zio/http/TestServer.scala b/zio-http-testkit/src/main/scala/zio/http/TestServer.scala index 44f916eecc..11e88eba24 100644 --- a/zio-http-testkit/src/main/scala/zio/http/TestServer.scala +++ b/zio-http-testkit/src/main/scala/zio/http/TestServer.scala @@ -69,7 +69,7 @@ final case class TestServer(driver: Driver, bindPort: Int) extends Server { for { r <- ZIO.environment[R] provided = route.provideEnvironment(r) - app: HttpApp[Any] = provided.toHttpApp + app: HttpApp[Any, Response] = provided.toHttpApp _ <- driver.addApp(app, r) } yield () @@ -92,11 +92,11 @@ final case class TestServer(driver: Driver, bindPort: Int) extends Server { for { r <- ZIO.environment[R] provided = routes.provideEnvironment(r) - app: HttpApp[Any] = provided.toHttpApp + app: HttpApp[Any, Response] = provided.toHttpApp _ <- driver.addApp(app, r) } yield () - override def install[R](httpApp: HttpApp[R])(implicit + override def install[R](httpApp: HttpApp[R, Response])(implicit trace: zio.Trace, ): URIO[R, Unit] = ZIO diff --git a/zio-http/jvm/src/main/scala/zio/http/netty/server/NettyDriver.scala b/zio-http/jvm/src/main/scala/zio/http/netty/server/NettyDriver.scala index 3867ae06bd..f6d3b48c7b 100644 --- a/zio-http/jvm/src/main/scala/zio/http/netty/server/NettyDriver.scala +++ b/zio-http/jvm/src/main/scala/zio/http/netty/server/NettyDriver.scala @@ -18,14 +18,11 @@ package zio.http.netty.server import java.net.InetSocketAddress import java.util.concurrent.atomic.AtomicReference - import zio._ - import zio.http.Driver.StartResult import zio.http.netty._ import zio.http.netty.client.NettyClientDriver -import zio.http.{ClientDriver, Driver, HttpApp, Server} - +import zio.http.{ClientDriver, Driver, HttpApp, Response, Server} import io.netty.bootstrap.ServerBootstrap import io.netty.channel._ import io.netty.util.ResourceLeakDetector @@ -53,12 +50,12 @@ private[zio] final case class NettyDriver( ) } yield StartResult(port, serverInboundHandler.inFlightRequests) - def addApp[R](newApp: HttpApp[R], env: ZEnvironment[R])(implicit trace: Trace): UIO[Unit] = ZIO.succeed { + def addApp[R](newApp: HttpApp[R, Response], env: ZEnvironment[R])(implicit trace: Trace): UIO[Unit] = ZIO.succeed { var loop = true while (loop) { val oldAppAndEnv = appRef.get() val (oldApp, oldEnv) = oldAppAndEnv - val updatedApp = (oldApp ++ newApp).asInstanceOf[HttpApp[Any]] + val updatedApp = (oldApp ++ newApp).asInstanceOf[HttpApp[Any, Response]] val updatedEnv = oldEnv.unionAll(env) val updatedAppAndEnv = (updatedApp, updatedEnv) @@ -114,7 +111,7 @@ object NettyDriver { implicit val trace: Trace = Trace.empty ZLayer.makeSome[EventLoopGroup & ChannelFactory[ServerChannel] & Server.Config & NettyConfig, Driver]( ZLayer.succeed( - new AtomicReference[(HttpApp[Any], ZEnvironment[Any])]((HttpApp.empty, ZEnvironment.empty)), + new AtomicReference[(HttpApp[Any, Response], ZEnvironment[Any])]((HttpApp.empty, ZEnvironment.empty)), ), NettyRuntime.live, ServerChannelInitializer.layer, diff --git a/zio-http/jvm/src/main/scala/zio/http/netty/server/ServerInboundHandler.scala b/zio-http/jvm/src/main/scala/zio/http/netty/server/ServerInboundHandler.scala index c2b1477820..85b73519e8 100644 --- a/zio-http/jvm/src/main/scala/zio/http/netty/server/ServerInboundHandler.scala +++ b/zio-http/jvm/src/main/scala/zio/http/netty/server/ServerInboundHandler.scala @@ -49,7 +49,7 @@ private[zio] final case class ServerInboundHandler( implicit private val unsafe: Unsafe = Unsafe.unsafe - private var app: HttpApp[Any] = _ + private var app: HttpApp[Any, Response] = _ private var env: ZEnvironment[Any] = _ val inFlightRequests: LongAdder = new LongAdder() diff --git a/zio-http/jvm/src/main/scala/zio/http/netty/server/package.scala b/zio-http/jvm/src/main/scala/zio/http/netty/server/package.scala index 1c42f5d56f..3e0fef2584 100644 --- a/zio-http/jvm/src/main/scala/zio/http/netty/server/package.scala +++ b/zio-http/jvm/src/main/scala/zio/http/netty/server/package.scala @@ -23,7 +23,7 @@ import zio.http._ import java.util.concurrent.atomic.AtomicReference // scalafix:ok; import zio.stacktracer.TracingImplicits.disableAutoTrace package object server { - private[server] type AppRef = AtomicReference[(HttpApp[Any], ZEnvironment[Any])] + private[server] type AppRef = AtomicReference[(HttpApp[Any, Response], ZEnvironment[Any])] private[server] type EnvRef = AtomicReference[ZEnvironment[Any]] val live: ZLayer[Server.Config, Throwable, Driver] = diff --git a/zio-http/jvm/src/test/scala/zio/http/DynamicAppTest.scala b/zio-http/jvm/src/test/scala/zio/http/DynamicAppTest.scala index 1413ff23f0..aef46a1dfa 100644 --- a/zio-http/jvm/src/test/scala/zio/http/DynamicAppTest.scala +++ b/zio-http/jvm/src/test/scala/zio/http/DynamicAppTest.scala @@ -26,12 +26,12 @@ import zio.http.netty.client.NettyClientDriver object DynamicAppTest extends ZIOHttpSpec { def extractStatus(response: Response): Status = response.status - val httpApp1: HttpApp[Any] = + val httpApp1: HttpApp[Any, Response] = Routes( Method.GET / "good" -> Handler.ok, ).sandbox.toHttpApp - val httpApp2: HttpApp[Any] = + val httpApp2: HttpApp[Any, Response] = Routes( Method.GET / "better" -> handler(Response.status(Status.Created)), ).sandbox.toHttpApp diff --git a/zio-http/jvm/src/test/scala/zio/http/ResponseCompressionSpec.scala b/zio-http/jvm/src/test/scala/zio/http/ResponseCompressionSpec.scala index 7335a0f988..fd8a0a429f 100644 --- a/zio-http/jvm/src/test/scala/zio/http/ResponseCompressionSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/ResponseCompressionSpec.scala @@ -29,7 +29,7 @@ import zio.http.netty.NettyConfig object ResponseCompressionSpec extends ZIOHttpSpec { - private val text: HttpApp[Any] = + private val text: HttpApp[Any, Response] = Routes( Method.GET / "text" -> handler(Response.text("Hello World!\n")), ).toHttpApp diff --git a/zio-http/jvm/src/test/scala/zio/http/SSLSpec.scala b/zio-http/jvm/src/test/scala/zio/http/SSLSpec.scala index db1cf7e4a1..e8945232de 100644 --- a/zio-http/jvm/src/test/scala/zio/http/SSLSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/SSLSpec.scala @@ -33,7 +33,7 @@ object SSLSpec extends ZIOHttpSpec { val payload = Gen.alphaNumericStringBounded(10000, 20000) - val app: HttpApp[Any] = Routes( + val app: HttpApp[Any, Response] = Routes( Method.GET / "success" -> handler(Response.ok), ).sandbox.toHttpApp diff --git a/zio-http/jvm/src/test/scala/zio/http/ServerSpec.scala b/zio-http/jvm/src/test/scala/zio/http/ServerSpec.scala index 43c838f210..f9a3b0ff3c 100644 --- a/zio-http/jvm/src/test/scala/zio/http/ServerSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/ServerSpec.scala @@ -159,7 +159,7 @@ object ServerSpec extends HttpRunnableSpec { ).sandbox.toHttpApp.deploy def roundTrip[R, E <: Throwable]( - app: HttpApp[R], + app: HttpApp[R, Response], headers: Headers, contentStream: ZStream[R, E, Byte], compressor: ZPipeline[R, E, Byte, Byte], @@ -267,7 +267,7 @@ object ServerSpec extends HttpRunnableSpec { ) def requestSpec = suite("RequestSpec") { - val app: HttpApp[Any] = + val app: HttpApp[Any, Response] = Routes .singleton(handler { (_: Path, req: Request) => Response.text(req.header(Header.ContentLength).map(_.length).getOrElse(-1).toString) @@ -464,7 +464,7 @@ object ServerSpec extends HttpRunnableSpec { def requestBodySpec = suite("RequestBodySpec")( test("POST Request stream") { - val app: HttpApp[Any] = Routes.singleton { + val app: HttpApp[Any, Response] = Routes.singleton { handler { (_: Path, req: Request) => Response(body = Body.fromStreamChunked(req.body.asStream)) } diff --git a/zio-http/jvm/src/test/scala/zio/http/ZClientAspectSpec.scala b/zio-http/jvm/src/test/scala/zio/http/ZClientAspectSpec.scala index f72ab11e20..fc0501667b 100644 --- a/zio-http/jvm/src/test/scala/zio/http/ZClientAspectSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/ZClientAspectSpec.scala @@ -26,11 +26,11 @@ import zio.http.netty.NettyConfig object ZClientAspectSpec extends ZIOHttpSpec { def extractStatus(response: Response): Status = response.status - val app: HttpApp[Any] = { + val app: HttpApp[Any, Response] = { Route.handled(Method.GET / "hello")(Handler.fromResponse(Response.text("hello"))) }.toHttpApp - val redir: HttpApp[Any] = { + val redir: HttpApp[Any, Response] = { Route.handled(Method.GET / "redirect")(Handler.fromResponse(Response.redirect(URL.empty / "hello"))) }.toHttpApp diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/DynamicServer.scala b/zio-http/jvm/src/test/scala/zio/http/internal/DynamicServer.scala index ca062f5cac..92a7de9a9a 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/DynamicServer.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/DynamicServer.scala @@ -24,9 +24,9 @@ import zio.http._ import zio.http.internal.DynamicServer.Id sealed trait DynamicServer { - def add(app: HttpApp[Any]): UIO[Id] + def add(app: HttpApp[Any, Response]): UIO[Id] - def get(id: Id): UIO[Option[HttpApp[Any]]] + def get(id: Id): UIO[Option[HttpApp[Any, Response]]] def port: ZIO[Any, Nothing, Int] @@ -59,13 +59,13 @@ object DynamicServer { def baseURL(scheme: Scheme): ZIO[DynamicServer, Nothing, String] = port.map(port => s"${scheme.encode}://localhost:$port") - def deploy[R](app: HttpApp[R]): ZIO[DynamicServer with R, Nothing, String] = + def deploy[R](app: HttpApp[R, Response]): ZIO[DynamicServer with R, Nothing, String] = for { env <- ZIO.environment[R] id <- ZIO.environmentWithZIO[DynamicServer](_.get.add(app.provideEnvironment(env))) } yield id - def get(id: Id): ZIO[DynamicServer, Nothing, Option[HttpApp[Any]]] = + def get(id: Id): ZIO[DynamicServer, Nothing, Option[HttpApp[Any, Response]]] = ZIO.environmentWithZIO[DynamicServer](_.get.get(id)) def httpURL: ZIO[DynamicServer, Nothing, String] = baseURL(Scheme.HTTP) @@ -73,7 +73,7 @@ object DynamicServer { val live: ZLayer[Any, Nothing, DynamicServer] = ZLayer { for { - ref <- Ref.make(Map.empty[Id, HttpApp[Any]]) + ref <- Ref.make(Map.empty[Id, HttpApp[Any, Response]]) pr <- Promise.make[Nothing, Server] } yield new Live(ref, pr) } @@ -87,13 +87,13 @@ object DynamicServer { def wsURL: ZIO[DynamicServer, Nothing, String] = baseURL(Scheme.WS) - final class Live(ref: Ref[Map[Id, HttpApp[Any]]], pr: Promise[Nothing, Server]) extends DynamicServer { - def add(app: HttpApp[Any]): UIO[Id] = for { + final class Live(ref: Ref[Map[Id, HttpApp[Any, Response]]], pr: Promise[Nothing, Server]) extends DynamicServer { + def add(app: HttpApp[Any, Response]): UIO[Id] = for { id <- ZIO.succeed(UUID.randomUUID().toString) _ <- ref.update(map => map + (id -> app)) } yield id - def get(id: Id): UIO[Option[HttpApp[Any]]] = ref.get.map(_.get(id)) + def get(id: Id): UIO[Option[HttpApp[Any, Response]]] = ref.get.map(_.get(id)) def port: ZIO[Any, Nothing, Int] = start.map(_.port) diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/HttpAppTestExtensions.scala b/zio-http/jvm/src/test/scala/zio/http/internal/HttpAppTestExtensions.scala index 2b5df788e9..7c8c7b3cb9 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/HttpAppTestExtensions.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/HttpAppTestExtensions.scala @@ -19,12 +19,12 @@ package zio.http.internal import zio.http._ trait HttpAppTestExtensions { - implicit class HttpAppSyntax[R](route: HttpApp[R]) { + implicit class HttpAppSyntax[R](route: HttpApp[R, Response]) { def rawHeader(name: String): Handler[R, Response, Request, Option[String]] = route.toHandler.map(res => res.rawHeader(name)) def headerValues: Handler[R, Response, Request, List[String]] = - route.toHandler.map(res => res.headers.toList.map(_.renderedValue.toString)) + route.toHandler.map(res => res.headers.toList.map(_.renderedValue)) def headers: Handler[R, Response, Request, Headers] = route.toHandler.map(res => res.headers) @@ -38,7 +38,7 @@ trait HttpAppTestExtensions { handler.map(res => res.rawHeader(name)) def headerValues: Handler[R, E, Request, List[String]] = - handler.map(res => res.headers.toList.map(_.renderedValue.toString)) + handler.map(res => res.headers.toList.map(_.renderedValue)) def headers: Handler[R, E, Request, Headers] = handler.map(res => res.headers) diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/HttpRunnableSpec.scala b/zio-http/jvm/src/test/scala/zio/http/internal/HttpRunnableSpec.scala index dc80560107..76c7f5ca21 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/HttpRunnableSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/HttpRunnableSpec.scala @@ -29,9 +29,9 @@ import zio.http._ * requests. */ abstract class HttpRunnableSpec extends ZIOHttpSpec { self => - implicit class RunnableHttpClientAppSyntax[R](route: HttpApp[R]) { + implicit class RunnableHttpClientAppSyntax[R](route: HttpApp[R, Response]) { - def app: HttpApp[R] = route + def app: HttpApp[R, Response] = route /** * Deploys the http application on the test server and returns a Http of @@ -112,7 +112,7 @@ abstract class HttpRunnableSpec extends ZIOHttpSpec { self => _ <- DynamicServer.setStart(server) } yield port - def serve[R](app: HttpApp[R]): ZIO[R with DynamicServer with Server, Nothing, Int] = + def serve[R](app: HttpApp[R, Response]): ZIO[R with DynamicServer with Server, Nothing, Int] = for { server <- ZIO.service[Server] port <- Server.install(app) diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/MetricsSpec.scala b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/MetricsSpec.scala index 26b31df92e..a2badedaa9 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/MetricsSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/MetricsSpec.scala @@ -86,7 +86,7 @@ object MetricsSpec extends ZIOHttpSpec with HttpAppTestExtensions { .tagged("method", "GET") .tagged("status", "200") - val app: HttpApp[Any] = + val app: HttpApp[Any, Response] = (Method.GET / "ok" -> Handler.ok).toHttpApp @@ metrics(extraLabels = Set(MetricLabel("test", "http_request_duration_seconds")), ) diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/WebSpec.scala b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/WebSpec.scala index 56f413dc37..25cb09fc7a 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/WebSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/WebSpec.scala @@ -317,7 +317,7 @@ object WebSpec extends ZIOHttpSpec with HttpAppTestExtensions { self => private def condZIO(flg: Boolean) = (_: Any) => ZIO.succeed(flg) - private def runApp[R](app: HttpApp[R]): ZIO[R, Response, Response] = { + private def runApp[R](app: HttpApp[R, Response]): ZIO[R, Response, Response] = { for { fib <- app.runZIO { Request.get(url = URL(Root / "health")) }.fork _ <- TestClock.adjust(10 seconds) diff --git a/zio-http/shared/src/main/scala/zio/http/Driver.scala b/zio-http/shared/src/main/scala/zio/http/Driver.scala index 72aac47b05..1eeb0abb0a 100644 --- a/zio-http/shared/src/main/scala/zio/http/Driver.scala +++ b/zio-http/shared/src/main/scala/zio/http/Driver.scala @@ -26,7 +26,7 @@ import zio.http.Driver.StartResult trait Driver { def start(implicit trace: Trace): RIO[Scope, StartResult] - def addApp[R](newApp: HttpApp[R], env: ZEnvironment[R])(implicit trace: Trace): UIO[Unit] + def addApp[R](newApp: HttpApp[R, Response], env: ZEnvironment[R])(implicit trace: Trace): UIO[Unit] def createClientDriver()(implicit trace: Trace): ZIO[Scope, Throwable, ClientDriver] } diff --git a/zio-http/shared/src/main/scala/zio/http/Handler.scala b/zio-http/shared/src/main/scala/zio/http/Handler.scala index 6fa5795616..1b27ca083e 100644 --- a/zio-http/shared/src/main/scala/zio/http/Handler.scala +++ b/zio-http/shared/src/main/scala/zio/http/Handler.scala @@ -608,9 +608,9 @@ sealed trait Handler[-R, +Err, -In, +Out] { self => * the handler has been appropriately sandboxed, turning all possible failures * into well-formed HTTP responses. */ - def toHttpApp(implicit err: Err <:< Response, in: Request <:< In, out: Out <:< Response, trace: Trace): HttpApp[R] = { - val handler: Handler[R, Response, Request, Response] = - self.asInstanceOf[Handler[R, Response, Request, Response]] + def toHttpApp(implicit in: Request <:< In, out: Out <:< Response, trace: Trace): HttpApp[R, Err] = { + val handler: Handler[R, Err, Request, Response] = + self.asInstanceOf[Handler[R, Err, Request, Response]] HttpApp(Routes.singleton(handler.contramap[(Path, Request)](_._2))) } diff --git a/zio-http/shared/src/main/scala/zio/http/HttpApp.scala b/zio-http/shared/src/main/scala/zio/http/HttpApp.scala index 9390826481..df414ce739 100644 --- a/zio-http/shared/src/main/scala/zio/http/HttpApp.scala +++ b/zio-http/shared/src/main/scala/zio/http/HttpApp.scala @@ -17,7 +17,6 @@ package zio.http import zio._ -import zio.stacktracer.TracingImplicits.disableAutoTrace /** * An HTTP application is a collection of routes, all of whose errors have been @@ -26,29 +25,30 @@ import zio.stacktracer.TracingImplicits.disableAutoTrace * HTTP applications can be installed into a [[zio.http.Server]], which is * capable of using them to serve requests. */ -final case class HttpApp[-Env](routes: Routes[Env, Response]) - extends PartialFunction[Request, ZIO[Env, Response, Response]] { self => +final case class HttpApp[-Env, +Err](routes: Chunk[zio.http.Route[Env, Err]]) +/*extends PartialFunction[Request, ZIO[Env, Response, Response]]*/ { self => private var _tree: HttpApp.Tree[_] = null.asInstanceOf[HttpApp.Tree[_]] /** * Applies the specified route aspect to every route in the HTTP application. */ - def @@[Env1 <: Env](aspect: Middleware[Env1]): HttpApp[Env1] = - copy(routes = routes @@ aspect) + def @@[Env1 <: Env](aspect: Middleware[Env1]): HttpApp[Env1, Err] = + aspect(self) /** * Combines this HTTP application with the specified HTTP application. In case * of route conflicts, the routes in this HTTP application take precedence * over the routes in the specified HTTP application. */ - def ++[Env1 <: Env](that: HttpApp[Env1]): HttpApp[Env1] = + def ++[Env1 <: Env, Err1 >: Err](that: HttpApp[Env1, Err1]): HttpApp[Env1, Err1] = copy(routes = routes ++ that.routes) /** * Executes the HTTP application with the specified request input, returning * an effect that will either succeed or fail with a Response. */ - def apply(request: Request): ZIO[Env, Response, Response] = runZIO(request) + def apply(request: Request)(implicit ev: Err <:< Response): ZIO[Env, Response, Response] = + runZIO(request) /** * Checks to see if the HTTP application may be defined at the specified @@ -57,41 +57,41 @@ final case class HttpApp[-Env](routes: Routes[Env, Response]) * This method only checks for the presence of a handler that handles the * method and path of the specified request. */ - def isDefinedAt(request: Request): Boolean = - tree(Trace.empty).get(request.method, request.path).nonEmpty + def isDefinedAt(request: Request)(implicit ev: Err <:< Response): Boolean = + tree(Trace.empty, ev).get(request.method, request.path).nonEmpty /** * Provides the specified environment to the HTTP application, returning a new * HTTP application that has no environmental requirements. */ - def provideEnvironment(env: ZEnvironment[Env]): HttpApp[Any] = - copy(routes = routes.provideEnvironment(env)) + def provideEnvironment(env: ZEnvironment[Env]): HttpApp[Any, Err] = + copy(routes = routes.map(_.provideEnvironment(env))) def run( method: Method = Method.GET, path: Path = Path.root, headers: Headers = Headers.empty, body: Body = Body.empty, - ): ZIO[Env, Nothing, Response] = + )(implicit ev: Err <:< Response): ZIO[Env, Nothing, Response] = runZIO(Request(method = method, url = URL.root.path(path), headers = headers, body = body)) /** * An alias for `apply`. */ - def runZIO(request: Request): ZIO[Env, Nothing, Response] = - toHandler(request) + def runZIO(request: Request)(implicit ev: Err <:< Response): ZIO[Env, Nothing, Response] = + toHandler(ev)(request) /** * Returns a new HTTP application whose requests will be timed out after the * specified duration elapses. */ - def timeout(duration: Duration)(implicit trace: Trace): HttpApp[Env] = + def timeout(duration: Duration)(implicit trace: Trace): HttpApp[Env, Err] = self @@ Middleware.timeout(duration) /** * Converts the HTTP application into a request handler. */ - val toHandler: Handler[Env, Nothing, Request, Response] = { + def toHandler(implicit ev: Err <:< Response): Handler[Env, Nothing, Request, Response] = { implicit val trace: Trace = Trace.empty Handler .fromFunctionHandler[Request] { req => @@ -115,20 +115,26 @@ final case class HttpApp[-Env](routes: Routes[Env, Response]) /** * Accesses the underlying tree that provides fast dispatch to handlers. */ - def tree(implicit trace: Trace): HttpApp.Tree[Env] = { + def tree(implicit trace: Trace, ev: Err <:< Response): HttpApp.Tree[Env] = { if (_tree eq null) { - _tree = HttpApp.Tree.fromRoutes(routes) + _tree = HttpApp.Tree.fromRoutes(routes.asInstanceOf[Chunk[Route[Env, Response]]]) } - _tree.asInstanceOf[HttpApp.Tree[Env]] } } -object HttpApp { + +object HttpApp { /** * An HTTP application that does not handle any routes. */ - val empty: HttpApp[Any] = HttpApp(Routes.empty) + val empty: HttpApp[Any, Nothing] = HttpApp(Routes.empty) + + def apply[Env, Err](route: Route[Env, Err], routes: Route[Env, Err]*): HttpApp[Env, Err] = + HttpApp(Chunk.fromIterable(route +: routes)) + + def apply[Env, Err](routes: Routes[Env, Err]): HttpApp[Env, Err] = + HttpApp(routes.routes) private[http] final case class Tree[-Env](tree: RoutePattern.Tree[RequestHandler[Env, Response]]) { self => final def ++[Env1 <: Env](that: Tree[Env1]): Tree[Env1] = @@ -146,7 +152,7 @@ object HttpApp { private[http] object Tree { val empty: Tree[Any] = Tree(RoutePattern.Tree.empty) - def fromRoutes[Env](routes: Routes[Env, Response])(implicit trace: Trace): Tree[Env] = - empty.addAll(routes.routes) + def fromRoutes[Env](routes: Chunk[zio.http.Route[Env, Response]])(implicit trace: Trace): Tree[Env] = + empty.addAll(routes) } } diff --git a/zio-http/shared/src/main/scala/zio/http/Middleware.scala b/zio-http/shared/src/main/scala/zio/http/Middleware.scala index 6898200829..4b499ef248 100644 --- a/zio-http/shared/src/main/scala/zio/http/Middleware.scala +++ b/zio-http/shared/src/main/scala/zio/http/Middleware.scala @@ -16,17 +16,19 @@ package zio.http import java.io.File - import zio._ import zio.metrics._ - import zio.http.codec.{PathCodec, SegmentCodec} +import zio.http.endpoint.EndpointMiddleware.None.Err trait Middleware[-UpperEnv] { self => def apply[Env1 <: UpperEnv, Err]( routes: Routes[Env1, Err], ): Routes[Env1, Err] + def apply[Env1 <: UpperEnv, Err](app: HttpApp[Env1, Err]): HttpApp[Env1, Err] = + HttpApp(self(Routes.fromIterable(app.routes))) + def @@[UpperEnv1 <: UpperEnv]( that: Middleware[UpperEnv1], ): Middleware[UpperEnv1] = diff --git a/zio-http/shared/src/main/scala/zio/http/Route.scala b/zio-http/shared/src/main/scala/zio/http/Route.scala index d9c3713d4e..44a6a19df7 100644 --- a/zio-http/shared/src/main/scala/zio/http/Route.scala +++ b/zio-http/shared/src/main/scala/zio/http/Route.scala @@ -296,7 +296,7 @@ sealed trait Route[-Env, +Err] { self => def toHandler(implicit ev: Err <:< Response, trace: Trace): Handler[Env, Response, Request, Response] - final def toHttpApp(implicit ev: Err <:< Response): HttpApp[Env] = Routes(self).toHttpApp + final def toHttpApp: HttpApp[Env, Err] = HttpApp(self) def transform[Env1]( f: Handler[Env, Response, Request, Response] => Handler[Env1, Response, Request, Response], diff --git a/zio-http/shared/src/main/scala/zio/http/Routes.scala b/zio-http/shared/src/main/scala/zio/http/Routes.scala index 3c8dd89b34..63d0548464 100644 --- a/zio-http/shared/src/main/scala/zio/http/Routes.scala +++ b/zio-http/shared/src/main/scala/zio/http/Routes.scala @@ -214,8 +214,8 @@ final class Routes[-Env, +Err] private (val routes: Chunk[zio.http.Route[Env, Er * Converts the routes into an app, which can be done only when errors are * handled and converted into responses. */ - def toHttpApp(implicit ev: Err <:< Response): HttpApp[Env] = - HttpApp(asErrorType[Response]) + def toHttpApp: HttpApp[Env, Err] = + HttpApp(self) /** * Returns new routes whose handlers are transformed by the specified diff --git a/zio-http/shared/src/main/scala/zio/http/Server.scala b/zio-http/shared/src/main/scala/zio/http/Server.scala index ba61b56c4e..18f201faae 100644 --- a/zio-http/shared/src/main/scala/zio/http/Server.scala +++ b/zio-http/shared/src/main/scala/zio/http/Server.scala @@ -33,7 +33,7 @@ trait Server { /** * Installs the given HTTP application into the server. */ - def install[R](httpApp: HttpApp[R])(implicit trace: Trace): URIO[R, Unit] + def install[R](httpApp: HttpApp[R, Response])(implicit trace: Trace): URIO[R, Unit] /** * The port on which the server is listening. @@ -330,7 +330,7 @@ object Server extends ServerPlatformSpecific { } def serve[R]( - httpApp: HttpApp[R], + httpApp: HttpApp[R, Response], )(implicit trace: Trace): URIO[R with Server, Nothing] = { ZIO.logInfo("Starting the server...") *> install(httpApp) *> @@ -338,7 +338,7 @@ object Server extends ServerPlatformSpecific { ZIO.never } - def install[R](httpApp: HttpApp[R])(implicit trace: Trace): URIO[R with Server, Int] = { + def install[R](httpApp: HttpApp[R, Response])(implicit trace: Trace): URIO[R with Server, Int] = { ZIO.serviceWithZIO[Server](_.install(httpApp)) *> ZIO.service[Server].map(_.port) } @@ -392,7 +392,7 @@ object Server extends ServerPlatformSpecific { driver: Driver, bindPort: Int, ) extends Server { - override def install[R](httpApp: HttpApp[R])(implicit + override def install[R](httpApp: HttpApp[R, Response])(implicit trace: Trace, ): URIO[R, Unit] = ZIO.environment[R].flatMap(driver.addApp(httpApp, _)) diff --git a/zio-http/shared/src/main/scala/zio/http/WebSocketApp.scala b/zio-http/shared/src/main/scala/zio/http/WebSocketApp.scala index c914ca7ff0..abc784ff41 100644 --- a/zio-http/shared/src/main/scala/zio/http/WebSocketApp.scala +++ b/zio-http/shared/src/main/scala/zio/http/WebSocketApp.scala @@ -72,7 +72,7 @@ final case class WebSocketApp[-R]( Response.fromSocketApp(self.provideEnvironment(env)) } - def toHttpAppWS(implicit trace: Trace): HttpApp[R] = + def toHttpAppWS(implicit trace: Trace): HttpApp[R, Response] = Handler.fromZIO(self.toResponse).toHttpApp def withConfig(config: WebSocketConfig): WebSocketApp[R] = diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala index 41eb981aa3..4d39183234 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala @@ -200,7 +200,7 @@ final case class Endpoint[PathInput, Input, Err, Output, Middleware <: EndpointM } .catchAllCause { cause => asHttpCodecError(cause) match { - case Some(error) => + case Some(_) => Handler.fromFunctionZIO { (request: zio.http.Request) => val error = cause.defects.head.asInstanceOf[HttpCodecError] val log = ZIO.unit From 8a74e330475a5e6116180a35d01ac780e496ac03 Mon Sep 17 00:00:00 2001 From: Nabil Abdel-Hafeez <7283535+987Nabil@users.noreply.github.com> Date: Sun, 21 Apr 2024 22:23:40 +0200 Subject: [PATCH 2/3] Remove Routes from internal API and examples --- README.md | 2 +- docs/reference/body.md | 4 +- docs/reference/cookies.md | 16 +- docs/reference/flash.md | 16 +- docs/reference/handler.md | 26 +-- docs/reference/headers.md | 8 +- docs/reference/middleware.md | 8 +- docs/reference/path_codec.md | 8 +- docs/reference/request.md | 14 +- docs/reference/response.md | 12 +- docs/reference/routes.md | 14 +- docs/reference/server.md | 8 +- docs/reference/socket/socket.md | 2 +- .../http/benchmarks/EndpointBenchmark.scala | 16 +- .../zhttp.benchmarks/HttpCollectEval.scala | 4 +- .../zhttp.benchmarks/HttpCombineEval.scala | 4 +- .../ServerInboundHandlerBenchmark.scala | 2 +- .../scala/zio/http/endpoint/cli/CliSpec.scala | 6 +- .../scala/example/AuthenticationServer.scala | 4 +- .../src/main/scala/example/BasicAuth.scala | 4 +- .../src/main/scala/example/ClientServer.scala | 4 +- .../main/scala/example/CookieServerSide.scala | 4 +- .../main/scala/example/EndpointExamples.scala | 4 +- .../main/scala/example/FileStreaming.scala | 4 +- .../src/main/scala/example/HelloWorld.scala | 2 +- .../scala/example/HelloWorldAdvanced.scala | 8 +- .../scala/example/HelloWorldWithCORS.scala | 8 +- .../scala/example/HelloWorldWithMetrics.scala | 8 +- .../src/main/scala/zio/http/TestClient.scala | 32 +++- .../src/main/scala/zio/http/TestServer.scala | 4 +- .../test/scala/zio/http/TestClientSpec.scala | 4 +- .../zio/http/netty/server/NettyDriver.scala | 3 + .../netty/server/ServerInboundHandler.scala | 4 +- .../scala/zio/http/ClientStreamingSpec.scala | 4 +- .../test/scala/zio/http/DynamicAppTest.scala | 8 +- .../src/test/scala/zio/http/FlashSpec.scala | 2 +- .../zio/http/RequestStreamingServerSpec.scala | 4 +- .../zio/http/ResponseCompressionSpec.scala | 8 +- .../jvm/src/test/scala/zio/http/SSLSpec.scala | 4 +- .../src/test/scala/zio/http/ServerSpec.scala | 10 +- .../scala/zio/http/StaticServerSpec.scala | 14 +- .../zio/http/endpoint/EndpointSpec.scala | 6 +- .../zio/http/endpoint/NotFoundSpec.scala | 8 +- .../http/endpoint/QueryParameterSpec.scala | 30 ++-- .../scala/zio/http/endpoint/RequestSpec.scala | 8 +- .../zio/http/endpoint/RoundtripSpec.scala | 42 ++--- .../http/endpoint/openapi/SwaggerUISpec.scala | 2 +- .../http/internal/middlewares/AuthSpec.scala | 21 +-- .../http/internal/middlewares/CorsSpec.scala | 4 +- .../internal/middlewares/MetricsSpec.scala | 8 +- .../middlewares/RequestLoggingSpec.scala | 4 +- .../http/internal/middlewares/WebSpec.scala | 8 +- .../zio/http/netty/NettyStreamBodySpec.scala | 4 +- .../client/NettyConnectionPoolSpec.scala | 4 +- .../zio/http/HttpAppVersionSpecific.scala | 14 ++ .../zio/http/HttpAppVersionSpecific.scala | 10 ++ .../main/scala/zio/http/HandlerAspect.scala | 21 +++ .../src/main/scala/zio/http/HttpApp.scala | 158 +++++++++++++++++- .../src/main/scala/zio/http/Middleware.scala | 9 +- .../src/main/scala/zio/http/Route.scala | 2 +- .../src/main/scala/zio/http/Routes.scala | 8 +- .../main/scala/zio/http/codec/PathCodec.scala | 4 +- .../scala/zio/http/endpoint/Endpoint.scala | 2 +- .../zio/http/endpoint/openapi/SwaggerUI.scala | 93 +++++++++++ 64 files changed, 553 insertions(+), 238 deletions(-) create mode 100644 zio-http/shared/src/main/scala-2/zio/http/HttpAppVersionSpecific.scala create mode 100644 zio-http/shared/src/main/scala-3/zio/http/HttpAppVersionSpecific.scala diff --git a/README.md b/README.md index 0e8069274c..2149e49844 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ import zio.http._ object GreetingServer extends ZIOAppDefault { val routes = - Routes( + HttpApp( Method.GET / "greet" -> handler { (req: Request) => val name = req.queryParamToOrElse("name", "World") Response.text(s"Hello $name!") diff --git a/docs/reference/body.md b/docs/reference/body.md index a4126c0b4f..e1a94d6c0a 100644 --- a/docs/reference/body.md +++ b/docs/reference/body.md @@ -21,14 +21,14 @@ import zio.http._ object HelloExample extends ZIOAppDefault { val app: HttpApp[Any, Response] = - Routes( + HttpApp( Method.GET / "hello" -> handler { req: Request => for { name <- req.body.asString } yield Response(body = Body.fromString(s"Hello $name!")) }.sandbox, - ).toHttpApp + ) override val run = Server.serve(app).provide(Server.default) } diff --git a/docs/reference/cookies.md b/docs/reference/cookies.md index abd7aa3dc4..cff5746e5b 100644 --- a/docs/reference/cookies.md +++ b/docs/reference/cookies.md @@ -69,13 +69,13 @@ Let's write a simple example to see how it works: import zio.http._ object ResponseCookieExample extends ZIOAppDefault { - val httpApp = Routes( + val httpApp = HttpApp( Method.GET / "cookie" -> handler { Response.ok.addCookie( Cookie.Response(name = "user_id", content = "user123", maxAge = Some(5.days)) ) }, - ).toHttpApp + ) def run = Server.serve(httpApp).provide(Server.default) } @@ -150,11 +150,11 @@ The cookies can be signed with a signature: ```scala mdoc:silent:nest val cookie = Cookie.Response("key", "hello", maxAge = Some(5.days)) val app = - Routes( + HttpApp( Method.GET / "cookie" -> handler { Response.ok.addCookie(cookie.sign("secret")) } - ).toHttpApp + ) ``` - Using `signCookies` middleware: @@ -164,10 +164,10 @@ To sign all the cookies in your routes, we can use `signCookies` middleware: ```scala mdoc:silent:nest import Middleware.signCookies -val app = Routes( +val app = HttpApp( Method.GET / "cookie" -> handler(Response.ok.addCookie(cookie)), Method.GET / "secure-cookie" -> handler(Response.ok.addCookie(cookie.copy(isSecure = true))) -).toHttpApp +) // Run it like any simple app def run(args: List[String]): ZIO[Any, Throwable, Nothing] = @@ -205,7 +205,7 @@ From HTTP requests, a single cookie can be retrieved with `Request#cookie`: ```scala mdoc:compile-only private val app4 = - Routes( + HttpApp( Method.GET / "cookie" -> handler { (req: Request) => val cookieContent = req.cookie("sessionId").map(_.content) Response.text(s"cookie content: $cookieContent") @@ -219,7 +219,7 @@ In HTTP requests, cookies are stored in the `Header.cookie` header: ```scala mdoc:compile-only private val app3 = - Routes( + HttpApp( Method.GET / "cookie" -> handler { (req: Request) => Response.text( req.header(Header.Cookie) diff --git a/docs/reference/flash.md b/docs/reference/flash.md index 8dce9b5438..034914588a 100644 --- a/docs/reference/flash.md +++ b/docs/reference/flash.md @@ -157,7 +157,7 @@ object NotificationWithoutFlash extends ZIOAppDefault { } - def run = Server.serve(Routes(saveUserRoute, homeRoute).toHttpApp) + def run = Server.serve(HttpApp(saveUserRoute, homeRoute)) .provide(Server.default, ZLayer(Ref.make(List.empty[User]))) } ``` @@ -350,7 +350,7 @@ val getUsersRoute: Route[Ref[List[User]] with Flash.Backend, Nothing] = } yield Response.html(html ++ usersHTML) } - val app = Routes(saveUserRoute, getUsersRoute, homeRoute).toHttpApp + val app = HttpApp(saveUserRoute, getUsersRoute, homeRoute) def run = Server.serve(app).provide(Server.default, Flash.Backend.inMemory, ZLayer(Ref.make(List.empty[User]))) } @@ -520,7 +520,7 @@ object ui { } object SetGetBothFlashExample extends ZIOAppDefault { - val httpApp = Routes( + val httpApp = HttpApp( Method.GET / "set-flash" -> handler { val setBoth: Flash.Setter[(String, String)] = Flash.setNotice("The form was submitted successfully!") ++ @@ -536,7 +536,7 @@ object SetGetBothFlashExample extends ZIOAppDefault { req.flash(getBoth).getOrElse(ui.renderNoFlash), ) }, - ).sandbox.toHttpApp + ).sandbox def run = Server.serve(httpApp).provide(Server.default, Flash.Backend.inMemory) } @@ -555,7 +555,7 @@ import zio._ import zio.http._ object CookieBasedFlashExample extends ZIOAppDefault { - val httpApp = Routes( + val httpApp = HttpApp( Method.GET / "set-flash" -> handler { Response .seeOther(URL.root / "get-flash") @@ -568,7 +568,7 @@ object CookieBasedFlashExample extends ZIOAppDefault { req.flash(Flash.getNotice[String]).getOrElse("no-flash"), ) }, - ).sandbox.toHttpApp + ).sandbox def run = Server.serve(httpApp).provide(Server.default) } @@ -609,7 +609,7 @@ import zio.http._ import zio.http.template._ object FlashBackendExample extends ZIOAppDefault { - val httpApp = Routes( + val httpApp = HttpApp( Method.GET / "set-flash" -> handler { for { flashBackend <- ZIO.service[Flash.Backend] @@ -625,7 +625,7 @@ object FlashBackendExample extends ZIOAppDefault { notice <- flashBackend.flash(req, Flash.getNotice[String]) } yield Response.text(notice) }, - ).sandbox.toHttpApp + ).sandbox def run = Server.serve(httpApp).provide(Server.default, Flash.Backend.inMemory) } diff --git a/docs/reference/handler.md b/docs/reference/handler.md index 96a3102899..d6d67eb22b 100644 --- a/docs/reference/handler.md +++ b/docs/reference/handler.md @@ -39,7 +39,7 @@ Let's look at some examples of creating handlers, using the `handler` smart cons import zio._ import zio.http._ -Routes( +HttpApp( // 1. A simple handler that returns a "Hello, World!" response Method.GET / "hello" -> @@ -202,7 +202,7 @@ Let's try an example: import zio.http._ import zio.stream._ -Routes( +HttpApp( Method.GET / "stream" -> Handler .fromStream( @@ -239,7 +239,7 @@ Now, let's try another example, this time using `fromStreamChunked`: import zio.http._ import zio.stream._ -Routes( +HttpApp( Method.GET / "stream" -> Handler .fromStreamChunked( @@ -273,7 +273,7 @@ import zio.http._ import zio.stream._ import zio.http.template._ -Routes( +HttpApp( Method.GET / "html" -> Handler.html( @@ -320,7 +320,7 @@ ZIP HTTP has a simple built-in template which is useful for creating simple HTML import zio.http._ import zio.http.template._ -Routes( +HttpApp( Method.GET / "hello" -> Handler.template("Hello world!")( html( @@ -396,7 +396,7 @@ import zio.http._ import zio.stream._ import zio.schema.codec.JsonCodec.zioJsonBinaryCodec -Routes( +HttpApp( Method.POST / "bounded-body-consumer" -> handler { (request: Request) => Handler @@ -418,7 +418,7 @@ The following example shows how to create a handler that takes an `Int` and `Req import zio.json._ import zio.http._ -Routes( +HttpApp( Method.GET / "users" / int("userId") -> Handler.fromFunction[(Int, Request)] { case (userId: Int, request: Request) => Response.json( @@ -455,7 +455,7 @@ Let's see an example: import zio.http._ import java.io.File -Routes( +HttpApp( Method.GET / "video" -> Handler.fromFile(new File("src/main/resources/TestVideoFile.mp4")), Method.GET / "text" -> @@ -472,7 +472,7 @@ Here is an example: ```scala mdoc:compile-only import zio.http._ -Routes( +HttpApp( Method.GET / "static" / trailing -> handler { // Path extractor val pathExtractor: Handler[Any, Nothing, (Path, Request), Path] = @@ -513,7 +513,7 @@ The following example shows how to create an echo server using the `Handler.webS import zio.http._ import zio.http.ChannelEvent._ -Routes( +HttpApp( Method.GET / "websocket" -> handler { Handler.webSocket { channel => @@ -548,7 +548,7 @@ Let's try an example: ```scala mdoc:compile-only import zio.http._ -Routes( +HttpApp( Method.GET / "stacktrace" -> handler { for { @@ -575,7 +575,7 @@ To attach a handler aspect to a handler, we use the `@@` operator. For instance, ```scala mdoc:compile-only import zio.http._ -Routes( +HttpApp( Method.GET / "echo" -> handler { req: Request => Handler.fromBody(req.body) }.flatten @@ HandlerAspect.requestLogging() @@ -602,7 +602,7 @@ Let's see an example: import zio.http._ import java.nio.file._ -Routes( +HttpApp( Method.GET / "file" -> Handler.fromFile(Paths.get("file.txt").toFile).sandbox, ) diff --git a/docs/reference/headers.md b/docs/reference/headers.md index b30e443335..addfd0980c 100644 --- a/docs/reference/headers.md +++ b/docs/reference/headers.md @@ -168,7 +168,7 @@ Response( ```scala mdoc import Middleware.addHeader -Routes(Method.GET / "hello" -> Handler.ok) @@ addHeader(Header.ContentLength(0L)) +HttpApp(Method.GET / "hello" -> Handler.ok) @@ addHeader(Header.ContentLength(0L)) ``` ### Reading Headers from Request @@ -176,7 +176,7 @@ Routes(Method.GET / "hello" -> Handler.ok) @@ addHeader(Header.ContentLength(0L) On the Server-side you can read Request headers as given below: ```scala mdoc -Routes( +HttpApp( Method.GET / "streamOrNot" -> handler { (req: Request) => Response.text(req.headers.map(_.toString).mkString("\n")) } @@ -202,7 +202,7 @@ object SimpleResponseDispatcher extends ZIOAppDefault { val message = Chunk.fromArray("Hello world !\r\n".getBytes(Charsets.Http)) // Use `Http.collect` to match on route val app: HttpApp[Any, Response] = - Routes( + HttpApp( // Simple (non-stream) based route Method.GET / "health" -> handler(Response.ok), @@ -221,7 +221,7 @@ object SimpleResponseDispatcher extends ZIOAppDefault { Response(status = Status.Accepted, body = Body.fromChunk(message)).addHeader("X-MY-HEADER", "test") } } - ).sandbox.toHttpApp + ).sandbox } ``` diff --git a/docs/reference/middleware.md b/docs/reference/middleware.md index c1672ac666..cd8565c00c 100644 --- a/docs/reference/middleware.md +++ b/docs/reference/middleware.md @@ -24,11 +24,11 @@ The `@@` operator attaches middleware to routes and HTTP applications. The exam ```scala mdoc:compile-only import zio.http._ -val app = Routes( +val app = HttpApp( Method.GET / string("name") -> handler { (name: String, req: Request) => Response.text(s"Hello $name") } -).toHttpApp +) val appWithMiddleware = app @@ Middleware.debug ``` @@ -70,7 +70,7 @@ Consider the following example where we have two endpoints: * **GET /users** - Get all users ```scala -val routes = Routes( +val routes = HttpApp( Method.GET / "users" / int("id") -> handler { (id: Int, req: Request) => // core business logic @@ -152,7 +152,7 @@ val composedMiddlewares = Middleware.basicAuth("user","pw") ++ And then we can attach our composed bundle of middlewares to an Http using `@@` ```scala - val routes = Routes( + val routes = HttpApp( Method.GET / "users" / int("id") -> handler { (id: Int, req: Request) => // core business logic diff --git a/docs/reference/path_codec.md b/docs/reference/path_codec.md index a9424b53d0..ad5d1e8bb9 100644 --- a/docs/reference/path_codec.md +++ b/docs/reference/path_codec.md @@ -175,7 +175,7 @@ object Main extends ZIOAppDefault { val userId: PathCodec[UserId] = int("user-id").transformOrFailLeft(UserId.apply)(_.value) val httpApp: HttpApp[Any, Response] = - Routes( + HttpApp( Method.GET / "users" / userId -> Handler.fromFunctionHandler[(UserId, Request)] { case (userId: UserId, request: Request) => Handler.text(userId.value.toString) @@ -188,7 +188,7 @@ object Main extends ZIOAppDefault { else Response.internalServerError } - }.toHttpApp + } def run = Server.serve(httpApp).provide(Server.default) } @@ -214,12 +214,12 @@ object TrailingExample extends ZIOAppDefault { } yield http val app = - Routes( + HttpApp( Method.GET / "static" / trailing -> Handler.fromFunctionHandler[(Path, Request)] { case (path: Path, _: Request) => staticFileHandler(path).contramap[(Path, Request)](_._2) }, - ).sandbox.toHttpApp @@ HandlerAspect.requestLogging() + ).sandbox @@ HandlerAspect.requestLogging() val run = Server.serve(app).provide(Server.default) } diff --git a/docs/reference/request.md b/docs/reference/request.md index 4160a890f2..71d727ff2b 100644 --- a/docs/reference/request.md +++ b/docs/reference/request.md @@ -13,7 +13,7 @@ To access the incoming request, we can use a `Handler` which takes a `Request` a import zio._ import zio.http._ -Routes( +HttpApp( Method.POST / "echo" -> handler { (req: Request) => req.body.asString(Charsets.Utf8).map(Response.text(_)).sandbox @@ -148,7 +148,7 @@ import zio.http._ object QueryParamExample extends ZIOAppDefault { val app = - Routes( + HttpApp( Method.GET / "search" -> handler { (req: Request) => val queries = req.queryParam("q") queries match { @@ -158,7 +158,7 @@ object QueryParamExample extends ZIOAppDefault { Response.badRequest(s"The q query parameter is missing!") } }, - ).toHttpApp + ) def run = Server.serve(app).provide(Server.default) } @@ -171,7 +171,7 @@ The typed version of `Request#queryParam` is `Request#queryParamTo` which takes import zio.http._ object TypedQueryParamExample extends ZIOAppDefault { val app = - Routes( + HttpApp( Method.GET / "search" -> Handler.fromFunctionHandler { (req: Request) => val response: ZIO[Any, QueryParamsError, Response] = ZIO.fromEither(req.queryParamTo[Int]("age")) @@ -184,7 +184,7 @@ object TypedQueryParamExample extends ZIOAppDefault { Handler.badRequest(s"The value of $name query param is malformed") } }, - ).toHttpApp + ) def run = Server.serve(app).provide(Server.default) } @@ -204,7 +204,7 @@ import zio.http._ object QueryParamsExample extends ZIOAppDefault { val app = - Routes( + HttpApp( Method.GET / "search" -> handler { (req: Request) => val queries = req.queryParams("q") if (queries.nonEmpty) { @@ -214,7 +214,7 @@ object QueryParamsExample extends ZIOAppDefault { Response.badRequest(s"The q query parameter is missing!") } }, - ).toHttpApp + ) def run = Server.serve(app).provide(Server.default) } diff --git a/docs/reference/response.md b/docs/reference/response.md index 6e85cb10df..4282cc2d8c 100644 --- a/docs/reference/response.md +++ b/docs/reference/response.md @@ -20,12 +20,12 @@ import zio.http._ object HelloWorldExample extends ZIOAppDefault { val app: HttpApp[Any, Response] = - Routes( + HttpApp( Method.GET / "text" -> handler { Response.text("Hello World!") }, - ).toHttpApp + ) override val run = Server.serve(app).provide(Server.default) } @@ -246,11 +246,11 @@ object ServerSentExample extends ZIOAppDefault { ) val app = - Routes( + HttpApp( Method.GET / "events" -> handler { Response.fromServerSentEvents(stream) }, - ).toHttpApp + ) def run = Server.serve(app).provide(Server.default) } ``` @@ -296,7 +296,7 @@ import zio.http._ object WebsocketExample extends ZIOAppDefault { val app: HttpApp[Any, Response] = { - Routes( + HttpApp( Method.GET / "echo" -> handler { Response.fromSocketApp( WebSocketApp( @@ -311,7 +311,7 @@ object WebsocketExample extends ZIOAppDefault { ), ) }, - ).toHttpApp + ) } def run = diff --git a/docs/reference/routes.md b/docs/reference/routes.md index e4a516cf7a..e5f6a850d9 100644 --- a/docs/reference/routes.md +++ b/docs/reference/routes.md @@ -10,7 +10,7 @@ Let's see an example of a simple `Routes` that has two routes: ```scala mdoc:compile-only import zio.http._ -Routes( +HttpApp( Method.GET / "hello" -> Handler.text("hello"), Method.GET / "health-check" -> Handler.ok, ) @@ -23,7 +23,7 @@ To build empty routes we have `Routes.empty` constructor: ```scala mdoc:silent import zio.http._ -val routes1 = Routes.empty +val routes1 = HttpApp.empty ``` We can build routes with the `Routes.apply` constructor, which takes varargs of individual `Route` values: @@ -40,7 +40,7 @@ object Routes { Example: ```scala mdoc:compile-only -Routes( +HttpApp( Method.GET / "hello" -> Handler.text("hello"), Method.GET / "health-check" -> Handler.ok, Method.POST / "echo" -> @@ -61,7 +61,7 @@ Using the `/` operator of `Method`, we can construct route patterns, which can t ```scala mdoc:silent val routes2 = - Routes( + HttpApp( Method.GET / "hello" -> Handler.ok, Method.GET / "goodbye" -> Handler.ok ) @@ -83,17 +83,17 @@ import zio.http.codec.PathCodec._ val routes = literal("nest1") / - Routes.fromIterable( + HttpApp.fromIterable( Chunk( Method.GET / "foo" -> Handler.text("foo"), Method.GET / "bar" -> Handler.text("bar"), ) ++ Chunk( - literal("nest2") / Routes( + literal("nest2") / HttpApp( Method.GET / "baz" -> Handler.text("baz"), Method.GET / "qux" -> Handler.text("qux"), ), - literal("nest2") / Routes( + literal("nest2") / HttpApp( Method.GET / "quux" -> Handler.text("quux"), Method.GET / "corge" -> Handler.text("corge"), ), diff --git a/docs/reference/server.md b/docs/reference/server.md index 0c9d0b4c32..f8724e1ae2 100644 --- a/docs/reference/server.md +++ b/docs/reference/server.md @@ -16,10 +16,10 @@ import zio.http._ import zio._ def app: HttpApp[Any, Response] = - Routes( + HttpApp( Method.GET / "hello" -> handler(Response.text("Hello, World!")) - ).toHttpApp + ) ``` We can serve it using the `Server.serve` method: @@ -406,7 +406,7 @@ object RequestStreamingServerExample extends ZIOAppDefault { def logBytes = (b: Byte) => ZIO.log(s"received byte: $b") private val app: HttpApp[Any, Response] = - Routes( + HttpApp( Method.POST / "upload-stream" / "simple" -> handler { (req: Request) => for { count <- req.body.asStream.tap(logBytes).run(ZSink.count) @@ -438,7 +438,7 @@ object RequestStreamingServerExample extends ZIOAppDefault { } yield Response.text(count.toString) else ZIO.succeed(Response(status = Status.NotFound)) }, - ).sandbox.toHttpApp @@ Middleware.debug + ).sandbox @@ Middleware.debug override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = Server diff --git a/docs/reference/socket/socket.md b/docs/reference/socket/socket.md index 1685da9805..c6c3bdd937 100644 --- a/docs/reference/socket/socket.md +++ b/docs/reference/socket/socket.md @@ -18,7 +18,7 @@ val socket = Handler.webSocket { channel => } } -val http = Routes( +val http = HttpApp( Method.GET / "subscriptions" -> handler(socket.toResponse) ) ``` diff --git a/zio-http-benchmarks/src/main/scala-2.13/zio/http/benchmarks/EndpointBenchmark.scala b/zio-http-benchmarks/src/main/scala-2.13/zio/http/benchmarks/EndpointBenchmark.scala index 288dcbe2c1..9155cdf095 100644 --- a/zio-http-benchmarks/src/main/scala-2.13/zio/http/benchmarks/EndpointBenchmark.scala +++ b/zio-http-benchmarks/src/main/scala-2.13/zio/http/benchmarks/EndpointBenchmark.scala @@ -104,14 +104,14 @@ class EndpointBenchmark { val apiHttpApp = handledUsersPosts.toHttpApp // Collect DSL - val collectHttpApp = Routes( + val collectHttpApp = HttpApp( Method.GET / "users" / int("userId") / "posts" / int("postId") -> handler { (userIdInt: Int, postIdInt: Int, req: Request) => val query = req.url.queryParam("query").get Response.json(ExampleData(userIdInt, postIdInt, query).toJson) }, - ).toHttpApp + ) // Tapir Akka DSL @@ -223,14 +223,14 @@ class EndpointBenchmark { // Collect DSL - val deepPathCollectHttpApp = Routes( + val deepPathCollectHttpApp = HttpApp( Method.GET / "first" / int("id1") / "second" / int("id2") / "third" / int("id3") / "fourth" / int( "id4", ) / "fifth" / int("id5") / "sixth" / int("id6") / "seventh" / int("id7") -> handler { (_: Int, _: Int, _: Int, _: Int, _: Int, _: Int, _: Int, _: Request) => ZIO.succeed(Response.ok) }, - ).toHttpApp + ) // Tapir Akka DSL @@ -418,7 +418,7 @@ class EndpointBenchmark { .implement(Handler.unit) val broadApiApp = - Routes( + HttpApp( broadUsers, broadUsersId, boardUsersPosts, @@ -435,11 +435,11 @@ class EndpointBenchmark { broadUsersCommentsId, boardUsersPostsCommentsReplies, boardUsersPostsCommentsRepliesId, - ).toHttpApp + ) // Collect DSL - val broadCollectApp = Routes( + val broadCollectApp = HttpApp( Method.GET / "users" / int("userId") / "posts" / int("postId") / "comments" / int("commentId") -> handler { (userId: Int, postId: Int, commentId: Int, request: Request) => Response() @@ -491,7 +491,7 @@ class EndpointBenchmark { ) / "replies" -> handler { (userId: Int, postId: Int, commentId: Int, req: Request) => Response() }, - ).toHttpApp + ) // Tapir Akka DSL diff --git a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCollectEval.scala b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCollectEval.scala index 37534f3920..5fe7e8362f 100644 --- a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCollectEval.scala +++ b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCollectEval.scala @@ -15,8 +15,8 @@ class HttpCollectEval { private val MAX = 10000 private val req = Request() private val res = Response.ok - private val app = Routes.singleton(handler(res)).toHttpApp - private val http = Routes(Route.route(Method.ANY / "text")(handler(res))).toHttpApp + private val app = HttpApp.singleton(handler(res)) + private val http = HttpApp(Route.route(Method.ANY / "text")(handler(res))) private val base: PartialFunction[Int, Int] = { case 0 => 1 } private val baseTotal: Int => Int = _ => 1 diff --git a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCombineEval.scala b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCombineEval.scala index b89dbdce2b..e65989a71d 100644 --- a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCombineEval.scala +++ b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCombineEval.scala @@ -15,8 +15,8 @@ class HttpCombineEval { private val req = Request.get("/foo") private val res = Response.ok private val MAX = 1000 - private val app = Routes(Method.GET / "" -> handler(res)) - private val spec = (0 to MAX).foldLeft(app)((a, _) => a ++ app).toHttpApp + private val app = HttpApp(Method.GET / "" -> handler(res)) + private val spec = (0 to MAX).foldLeft(app)((a, _) => a ++ app) @Benchmark def empty(): Unit = { diff --git a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ServerInboundHandlerBenchmark.scala b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ServerInboundHandlerBenchmark.scala index 0182543da5..247b9171e4 100644 --- a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ServerInboundHandlerBenchmark.scala +++ b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ServerInboundHandlerBenchmark.scala @@ -59,7 +59,7 @@ class ServerInboundHandlerBenchmark { private def shutdownRoute(shutdownSignal: Promise[Nothing, Unit]) = Route.route(Method.GET / shutdownEndpoint)(handler(shutdownSignal.succeed(()).as(shutdownResponse))) private def http(shutdownSignal: Promise[Nothing, Unit]) = - Routes(testRoute, arrayRoute, chunkRoute, shutdownRoute(shutdownSignal)).toHttpApp + HttpApp(testRoute, arrayRoute, chunkRoute, shutdownRoute(shutdownSignal)) @Setup(Level.Trial) def setup(): Unit = { diff --git a/zio-http-cli/src/test/scala/zio/http/endpoint/cli/CliSpec.scala b/zio-http-cli/src/test/scala/zio/http/endpoint/cli/CliSpec.scala index 2a3418ff94..ba86dd84f9 100644 --- a/zio-http-cli/src/test/scala/zio/http/endpoint/cli/CliSpec.scala +++ b/zio-http-cli/src/test/scala/zio/http/endpoint/cli/CliSpec.scala @@ -46,11 +46,11 @@ object CliSpec extends ZIOSpecDefault { val testClient: ZLayer[Any, Nothing, TestClient & Client] = ZLayer.scopedEnvironment { for { - behavior <- Ref.make[Routes[Any, Response]](Routes.empty) + behavior <- Ref.make[HttpApp[Any, Response]](HttpApp.empty) socketBehavior <- Ref.make[WebSocketApp[Any]](WebSocketApp(Handler.unit)) driver = TestClient(behavior, socketBehavior) - _ <- driver.addRoutes { - Routes( + _ <- driver.addHttpApp { + HttpApp( Method.GET / "fromURL" -> handler(Response.text("342.76")), Method.GET / trailing -> handler { (_: Path, request: Request) => val headers = request.headers diff --git a/zio-http-example/src/main/scala/example/AuthenticationServer.scala b/zio-http-example/src/main/scala/example/AuthenticationServer.scala index c689754e31..2958dc2871 100644 --- a/zio-http-example/src/main/scala/example/AuthenticationServer.scala +++ b/zio-http-example/src/main/scala/example/AuthenticationServer.scala @@ -44,7 +44,7 @@ object AuthenticationServer extends ZIOAppDefault { }) def app: HttpApp[Any, Response] = - Routes( + HttpApp( // A route that is accessible only via a jwt token Method.GET / "profile" / "me" -> handler { (_: Request) => ZIO.serviceWith[String](name => Response.text(s"Welcome $name!")) @@ -69,7 +69,7 @@ object AuthenticationServer extends ZIOAppDefault { else Response.unauthorized("Invalid username or password.") }, - ).toHttpApp @@ Middleware.debug + ) @@ Middleware.debug override val run = Server.serve(app).provide(Server.default) } diff --git a/zio-http-example/src/main/scala/example/BasicAuth.scala b/zio-http-example/src/main/scala/example/BasicAuth.scala index b86ba1ce44..632feba915 100644 --- a/zio-http-example/src/main/scala/example/BasicAuth.scala +++ b/zio-http-example/src/main/scala/example/BasicAuth.scala @@ -9,12 +9,12 @@ import zio.http.codec.PathCodec.string object BasicAuth extends ZIOAppDefault { // Http app that requires a JWT claim - val user: HttpApp[Any, Response] = Routes( + val user: HttpApp[Any, Response] = HttpApp( Method.GET / "user" / string("name") / "greet" -> handler { (name: String, _: Request) => Response.text(s"Welcome to the ZIO party! ${name}") }, - ).toHttpApp + ) // Composing all the HttpApps together val app: HttpApp[Any, Response] = user @@ basicAuth("admin", "admin") diff --git a/zio-http-example/src/main/scala/example/ClientServer.scala b/zio-http-example/src/main/scala/example/ClientServer.scala index c3531d6699..9a1f9e9eba 100644 --- a/zio-http-example/src/main/scala/example/ClientServer.scala +++ b/zio-http-example/src/main/scala/example/ClientServer.scala @@ -7,10 +7,10 @@ import zio.http._ object ClientServer extends ZIOAppDefault { val url = URL.decode("http://localhost:8080/hello").toOption.get - val app = Routes( + val app = HttpApp( Method.GET / "hello" -> handler(Response.text("hello")), Method.GET / "" -> handler(ZClient.request(Request.get(url))), - ).sandbox.toHttpApp + ).sandbox val run = Server.serve(app).provide(Server.default, Client.default, Scope.default).exitCode diff --git a/zio-http-example/src/main/scala/example/CookieServerSide.scala b/zio-http-example/src/main/scala/example/CookieServerSide.scala index 0347eff2e3..a596422b6e 100644 --- a/zio-http-example/src/main/scala/example/CookieServerSide.scala +++ b/zio-http-example/src/main/scala/example/CookieServerSide.scala @@ -13,14 +13,14 @@ object CookieServerSide extends ZIOAppDefault { private val cookie = Cookie.Response("key", "value", maxAge = Some(5 days)) val res = Response.ok.addCookie(cookie) - private val app = Routes( + private val app = HttpApp( Method.GET / "cookie" -> handler(Response.ok.addCookie(cookie.copy(path = Some(Path.root / "cookie"), isHttpOnly = true))), Method.GET / "secure-cookie" -> handler(Response.ok.addCookie(cookie.copy(isSecure = true, path = Some(Path.root / "secure-cookie")))), Method.GET / "cookie" / "remove" -> handler(res.addCookie(Cookie.clear("key"))), - ).toHttpApp + ) // Run it like any simple app val run = diff --git a/zio-http-example/src/main/scala/example/EndpointExamples.scala b/zio-http-example/src/main/scala/example/EndpointExamples.scala index 0d40c12427..ba226bd67d 100644 --- a/zio-http-example/src/main/scala/example/EndpointExamples.scala +++ b/zio-http-example/src/main/scala/example/EndpointExamples.scala @@ -39,9 +39,9 @@ object EndpointExamples extends ZIOAppDefault { val openAPI = OpenAPIGen.fromEndpoints(title = "Endpoint Example", version = "1.0", getUser, getUserPosts) - val routes = Routes(getUserRoute, getUserPostsRoute) ++ SwaggerUI.routes("docs" / "openapi", openAPI) + val routes = HttpApp(getUserRoute, getUserPostsRoute) ++ SwaggerUI.app("docs" / "openapi", openAPI) - val app = routes.toHttpApp // (auth.implement(_ => ZIO.unit)(_ => ZIO.unit)) + val app = routes // (auth.implement(_ => ZIO.unit)(_ => ZIO.unit)) val request = Request.get(url = URL.decode("/users/1").toOption.get) diff --git a/zio-http-example/src/main/scala/example/FileStreaming.scala b/zio-http-example/src/main/scala/example/FileStreaming.scala index dbfd6b9f93..b4aebc57bb 100644 --- a/zio-http-example/src/main/scala/example/FileStreaming.scala +++ b/zio-http-example/src/main/scala/example/FileStreaming.scala @@ -12,7 +12,7 @@ import zio.http._ object FileStreaming extends ZIOAppDefault { // Create HTTP route - val app = Routes( + val app = HttpApp( Method.GET / "health" -> Handler.ok, // Read the file as ZStream @@ -24,7 +24,7 @@ object FileStreaming extends ZIOAppDefault { // Adds content-length header and does not use Chunked transfer encoding Method.GET / "video" -> Handler.fromFile(new File("src/main/resources/TestVideoFile.mp4")), Method.GET / "text" -> Handler.fromFile(new File("src/main/resources/TestFile.txt")), - ).sandbox.toHttpApp + ).sandbox // Run it like any simple app val run = diff --git a/zio-http-example/src/main/scala/example/HelloWorld.scala b/zio-http-example/src/main/scala/example/HelloWorld.scala index ce8c0ec955..43904881c1 100644 --- a/zio-http-example/src/main/scala/example/HelloWorld.scala +++ b/zio-http-example/src/main/scala/example/HelloWorld.scala @@ -12,7 +12,7 @@ object HelloWorld extends ZIOAppDefault { Method.GET / "json" -> handler(Response.json("""{"greetings": "Hello World!"}""")) // Create HTTP route - val app = Routes(textRoute, jsonRoute).toHttpApp + val app = HttpApp(textRoute, jsonRoute) // Run it like any simple app override val run = Server.serve(app).provide(Server.default) diff --git a/zio-http-example/src/main/scala/example/HelloWorldAdvanced.scala b/zio-http-example/src/main/scala/example/HelloWorldAdvanced.scala index e424db9201..325cf715be 100644 --- a/zio-http-example/src/main/scala/example/HelloWorldAdvanced.scala +++ b/zio-http-example/src/main/scala/example/HelloWorldAdvanced.scala @@ -13,15 +13,15 @@ object HelloWorldAdvanced extends ZIOAppDefault { val PORT = 0 val fooBar = - Routes( + HttpApp( Method.GET / "foo" -> Handler.from(Response.text("bar")), Method.GET / "bar" -> Handler.from(Response.text("foo")), - ).toHttpApp + ) - val app = Routes( + val app = HttpApp( Method.GET / "random" -> handler(Random.nextString(10).map(Response.text(_))), Method.GET / "utc" -> handler(Clock.currentDateTime.map(s => Response.text(s.toString))), - ).toHttpApp + ) val run = ZIOAppArgs.getArgs.flatMap { args => // Configure thread count using CLI diff --git a/zio-http-example/src/main/scala/example/HelloWorldWithCORS.scala b/zio-http-example/src/main/scala/example/HelloWorldWithCORS.scala index 476d380c22..d0e88f0e1f 100644 --- a/zio-http-example/src/main/scala/example/HelloWorldWithCORS.scala +++ b/zio-http-example/src/main/scala/example/HelloWorldWithCORS.scala @@ -19,12 +19,12 @@ object HelloWorldWithCORS extends ZIOAppDefault { ) val backend: HttpApp[Any, Response] = - Routes( + HttpApp( Method.GET / "json" -> handler(Response.json("""{"message": "Hello World!"}""")), - ).toHttpApp @@ cors(config) + ) @@ cors(config) val frontend: HttpApp[Any, Response] = - Routes( + HttpApp( Method.GET / PathCodec.empty -> handler( Response.html( html( @@ -38,7 +38,7 @@ object HelloWorldWithCORS extends ZIOAppDefault { ), ), ), - ).toHttpApp + ) val frontEndServer = Server.serve(frontend).provide(Server.defaultWithPort(3000)) val backendServer = Server.serve(backend).provide(Server.defaultWithPort(8080)) diff --git a/zio-http-example/src/main/scala/example/HelloWorldWithMetrics.scala b/zio-http-example/src/main/scala/example/HelloWorldWithMetrics.scala index e71e12b022..834222fa67 100644 --- a/zio-http-example/src/main/scala/example/HelloWorldWithMetrics.scala +++ b/zio-http-example/src/main/scala/example/HelloWorldWithMetrics.scala @@ -10,19 +10,19 @@ import zio.http._ object HelloWorldWithMetrics extends ZIOAppDefault { val backend: HttpApp[Any, Response] = - Routes( + HttpApp( Method.GET / "json" -> handler((req: Request) => ZIO.succeed(Response.json("""{"message": "Hello World!"}""")) @@ Metric .counter("x_custom_header_total") .contramap[Any](_ => if (req.headers.contains("X-Custom-Header")) 1L else 0L), ), Method.GET / "forbidden" -> handler(ZIO.succeed(Response.forbidden)), - ).toHttpApp @@ Middleware.metrics() + ) @@ Middleware.metrics() val metrics: HttpApp[PrometheusPublisher, Response] = - Routes( + HttpApp( Method.GET / "metrics" -> handler(ZIO.serviceWithZIO[PrometheusPublisher](_.get.map(Response.text))), - ).toHttpApp + ) val run = Server diff --git a/zio-http-testkit/src/main/scala/zio/http/TestClient.scala b/zio-http-testkit/src/main/scala/zio/http/TestClient.scala index 7d359395f6..6205332459 100644 --- a/zio-http-testkit/src/main/scala/zio/http/TestClient.scala +++ b/zio-http-testkit/src/main/scala/zio/http/TestClient.scala @@ -10,10 +10,25 @@ import zio._ * Server */ final case class TestClient( - behavior: Ref[Routes[Any, Response]], + behavior: Ref[HttpApp[Any, Response]], serverSocketBehavior: Ref[WebSocketApp[Any]], ) extends ZClient.Driver[Any, Throwable] { + /** + * Adds an HttpApp to the TestClient + * @param app + * The HttpApp to be added to the TestClient + * + * @example + * {{{ + * TestClient.addHttpApp(HttpApp.empty) + * }}} + */ + def addHttpApp( + app: HttpApp[Any, Response], + ): ZIO[Any, Nothing, Unit] = + behavior.update(_ ++ app) + /** * Adds an exact 1-1 behavior * @param expectedRequest @@ -84,11 +99,12 @@ final case class TestClient( * }}} */ def addRoutes[R]( - routes: Routes[R, Response], + route: Route[R, Response], + routes: Route[R, Response]*, ): ZIO[R, Nothing, Unit] = for { r <- ZIO.environment[R] - provided = routes.provideEnvironment(r) + provided = HttpApp.fromIterable(route +: routes).provideEnvironment(r) _ <- behavior.update(_ ++ provided) } yield () @@ -158,6 +174,9 @@ final case class TestClient( object TestClient { + def addApp(app: HttpApp[Any, Response]): ZIO[TestClient, Nothing, Unit] = + ZIO.serviceWithZIO[TestClient](_.addHttpApp(app)) + /** * Adds an exact 1-1 behavior * @param request @@ -211,9 +230,10 @@ object TestClient { * }}} */ def addRoutes[R]( - routes: Routes[R, Response], + route: Route[R, Response], + routes: Route[R, Response]*, ): ZIO[R with TestClient, Nothing, Unit] = - ZIO.serviceWithZIO[TestClient](_.addRoutes(routes)) + ZIO.serviceWithZIO[TestClient](_.addRoutes(route, routes: _*)) def installSocketApp( app: WebSocketApp[Any], @@ -223,7 +243,7 @@ object TestClient { val layer: ZLayer[Any, Nothing, TestClient & Client] = ZLayer.scopedEnvironment { for { - behavior <- Ref.make[Routes[Any, Response]](Routes.empty) + behavior <- Ref.make[HttpApp[Any, Response]](HttpApp.empty) socketBehavior <- Ref.make[WebSocketApp[Any]](WebSocketApp.unit) driver = TestClient(behavior, socketBehavior) } yield ZEnvironment[TestClient, Client](driver, ZClient.fromDriver(driver)) diff --git a/zio-http-testkit/src/main/scala/zio/http/TestServer.scala b/zio-http-testkit/src/main/scala/zio/http/TestServer.scala index 11e88eba24..c991464bbe 100644 --- a/zio-http-testkit/src/main/scala/zio/http/TestServer.scala +++ b/zio-http-testkit/src/main/scala/zio/http/TestServer.scala @@ -68,7 +68,7 @@ final case class TestServer(driver: Driver, bindPort: Int) extends Server { ): ZIO[R, Nothing, Unit] = for { r <- ZIO.environment[R] - provided = route.provideEnvironment(r) + provided = route.provideEnvironment(r) app: HttpApp[Any, Response] = provided.toHttpApp _ <- driver.addApp(app, r) } yield () @@ -91,7 +91,7 @@ final case class TestServer(driver: Driver, bindPort: Int) extends Server { ): ZIO[R, Nothing, Unit] = for { r <- ZIO.environment[R] - provided = routes.provideEnvironment(r) + provided = routes.provideEnvironment(r) app: HttpApp[Any, Response] = provided.toHttpApp _ <- driver.addApp(app, r) } yield () diff --git a/zio-http-testkit/src/test/scala/zio/http/TestClientSpec.scala b/zio-http-testkit/src/test/scala/zio/http/TestClientSpec.scala index 07e198e551..1d817d1d96 100644 --- a/zio-http-testkit/src/test/scala/zio/http/TestClientSpec.scala +++ b/zio-http-testkit/src/test/scala/zio/http/TestClientSpec.scala @@ -59,8 +59,8 @@ object TestClientSpec extends ZIOHttpSpec { test("addRoutes") { for { client <- ZIO.service[Client] - _ <- TestClient.addRoutes { - Routes( + _ <- TestClient.addApp { + HttpApp( Method.GET / trailing -> handler { Response.text("fallback") }, Method.GET / "hello" / "world" -> handler { Response.text("Hey there!") }, ) diff --git a/zio-http/jvm/src/main/scala/zio/http/netty/server/NettyDriver.scala b/zio-http/jvm/src/main/scala/zio/http/netty/server/NettyDriver.scala index f6d3b48c7b..2e99fcd21e 100644 --- a/zio-http/jvm/src/main/scala/zio/http/netty/server/NettyDriver.scala +++ b/zio-http/jvm/src/main/scala/zio/http/netty/server/NettyDriver.scala @@ -18,11 +18,14 @@ package zio.http.netty.server import java.net.InetSocketAddress import java.util.concurrent.atomic.AtomicReference + import zio._ + import zio.http.Driver.StartResult import zio.http.netty._ import zio.http.netty.client.NettyClientDriver import zio.http.{ClientDriver, Driver, HttpApp, Response, Server} + import io.netty.bootstrap.ServerBootstrap import io.netty.channel._ import io.netty.util.ResourceLeakDetector diff --git a/zio-http/jvm/src/main/scala/zio/http/netty/server/ServerInboundHandler.scala b/zio-http/jvm/src/main/scala/zio/http/netty/server/ServerInboundHandler.scala index 85b73519e8..0e30f0aadf 100644 --- a/zio-http/jvm/src/main/scala/zio/http/netty/server/ServerInboundHandler.scala +++ b/zio-http/jvm/src/main/scala/zio/http/netty/server/ServerInboundHandler.scala @@ -49,8 +49,8 @@ private[zio] final case class ServerInboundHandler( implicit private val unsafe: Unsafe = Unsafe.unsafe - private var app: HttpApp[Any, Response] = _ - private var env: ZEnvironment[Any] = _ + private var app: HttpApp[Any, Response] = _ + private var env: ZEnvironment[Any] = _ val inFlightRequests: LongAdder = new LongAdder() val readClientCert = config.sslConfig.exists(_.includeClientCert) diff --git a/zio-http/jvm/src/test/scala/zio/http/ClientStreamingSpec.scala b/zio-http/jvm/src/test/scala/zio/http/ClientStreamingSpec.scala index 99fb876e88..6d57883092 100644 --- a/zio-http/jvm/src/test/scala/zio/http/ClientStreamingSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/ClientStreamingSpec.scala @@ -32,7 +32,7 @@ import zio.http.netty.NettyConfig.LeakDetectionLevel object ClientStreamingSpec extends HttpRunnableSpec { def extractStatus(response: Response): Status = response.status - val app = Routes( + val app = HttpApp( Method.GET / "simple-get" -> handler(Response.text("simple response")), Method.GET / "streaming-get" -> @@ -52,7 +52,7 @@ object ClientStreamingSpec extends HttpRunnableSpec { } }, ), - ).sandbox.toHttpApp + ).sandbox // TODO: test failure cases diff --git a/zio-http/jvm/src/test/scala/zio/http/DynamicAppTest.scala b/zio-http/jvm/src/test/scala/zio/http/DynamicAppTest.scala index aef46a1dfa..1b5f28e658 100644 --- a/zio-http/jvm/src/test/scala/zio/http/DynamicAppTest.scala +++ b/zio-http/jvm/src/test/scala/zio/http/DynamicAppTest.scala @@ -27,14 +27,14 @@ object DynamicAppTest extends ZIOHttpSpec { def extractStatus(response: Response): Status = response.status val httpApp1: HttpApp[Any, Response] = - Routes( + HttpApp( Method.GET / "good" -> Handler.ok, - ).sandbox.toHttpApp + ).sandbox val httpApp2: HttpApp[Any, Response] = - Routes( + HttpApp( Method.GET / "better" -> handler(Response.status(Status.Created)), - ).sandbox.toHttpApp + ).sandbox val layer = ZLayer.make[Client & Server & Scope]( diff --git a/zio-http/jvm/src/test/scala/zio/http/FlashSpec.scala b/zio-http/jvm/src/test/scala/zio/http/FlashSpec.scala index 07428924d9..f20f709ba2 100644 --- a/zio-http/jvm/src/test/scala/zio/http/FlashSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/FlashSpec.scala @@ -118,7 +118,7 @@ object FlashSpec extends ZIOHttpSpec { } yield Response.html(html) } - val app = Routes(routeUserSave, routeConfirm).toHttpApp + val app = HttpApp(routeUserSave, routeConfirm) for { response1 <- app.runZIO(Request.post(URL(routeUserSavePath.format(()).toOption.get), Body.empty)) diff --git a/zio-http/jvm/src/test/scala/zio/http/RequestStreamingServerSpec.scala b/zio-http/jvm/src/test/scala/zio/http/RequestStreamingServerSpec.scala index 0ef14642da..2c23fd0262 100644 --- a/zio-http/jvm/src/test/scala/zio/http/RequestStreamingServerSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/RequestStreamingServerSpec.scala @@ -72,7 +72,7 @@ object RequestStreamingServerSpec extends HttpRunnableSpec { assertZIO(res)(equalTo(Status.InternalServerError)) }, suite("streaming request passed to client")({ - val app = Routes( + val app = HttpApp( Method.POST / "1" -> handler { (req: Request) => val host = req.headers.get(Header.Host).get val newRequest = @@ -88,7 +88,7 @@ object RequestStreamingServerSpec extends HttpRunnableSpec { Response.text(body.length.toString) } }, - ).sandbox.toHttpApp + ).sandbox val sizes = Chunk(0, 8192, 1024 * 1024) sizes.map { size => test(s"with body length $size") { diff --git a/zio-http/jvm/src/test/scala/zio/http/ResponseCompressionSpec.scala b/zio-http/jvm/src/test/scala/zio/http/ResponseCompressionSpec.scala index fd8a0a429f..2e359e64be 100644 --- a/zio-http/jvm/src/test/scala/zio/http/ResponseCompressionSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/ResponseCompressionSpec.scala @@ -30,12 +30,12 @@ import zio.http.netty.NettyConfig object ResponseCompressionSpec extends ZIOHttpSpec { private val text: HttpApp[Any, Response] = - Routes( + HttpApp( Method.GET / "text" -> handler(Response.text("Hello World!\n")), - ).toHttpApp + ) private val stream = - Routes( + HttpApp( Method.GET / "stream-chunked" -> handler( Response( @@ -71,7 +71,7 @@ object ResponseCompressionSpec extends ZIOHttpSpec { ), ), ), - ).toHttpApp + ) private val app = text ++ stream private lazy val serverConfig: Server.Config = Server.Config.default.port(0).responseCompression() diff --git a/zio-http/jvm/src/test/scala/zio/http/SSLSpec.scala b/zio-http/jvm/src/test/scala/zio/http/SSLSpec.scala index e8945232de..cb19d3e5c6 100644 --- a/zio-http/jvm/src/test/scala/zio/http/SSLSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/SSLSpec.scala @@ -33,9 +33,9 @@ object SSLSpec extends ZIOHttpSpec { val payload = Gen.alphaNumericStringBounded(10000, 20000) - val app: HttpApp[Any, Response] = Routes( + val app: HttpApp[Any, Response] = HttpApp( Method.GET / "success" -> handler(Response.ok), - ).sandbox.toHttpApp + ).sandbox val httpUrl = URL.decode("http://localhost:8073/success").toOption.get diff --git a/zio-http/jvm/src/test/scala/zio/http/ServerSpec.scala b/zio-http/jvm/src/test/scala/zio/http/ServerSpec.scala index f9a3b0ff3c..d71d029d40 100644 --- a/zio-http/jvm/src/test/scala/zio/http/ServerSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/ServerSpec.scala @@ -128,7 +128,7 @@ object ServerSpec extends HttpRunnableSpec { test("data") { val dataStream = ZStream.repeat("A").take(MaxSize.toLong) val app = - Routes(RoutePattern.any -> handler((_: Path, req: Request) => Response(body = req.body))).toHttpApp + HttpApp(RoutePattern.any -> handler((_: Path, req: Request) => Response(body = req.body))) val res = app.deploy.body.mapZIO(_.asChunk.map(_.length)).run(body = Body.fromCharSequenceStreamChunked(dataStream)) assertZIO(res)(equalTo(MaxSize)) @@ -151,12 +151,12 @@ object ServerSpec extends HttpRunnableSpec { val body = "some-text" val bodyAsStream = ZStream.fromChunk(Chunk.fromArray(body.getBytes)) - val app = Routes( + val app = HttpApp( RoutePattern.any -> handler { (_: Path, req: Request) => req.body.asString.map(body => Response.text(body)) }, - ).sandbox.toHttpApp.deploy + ).sandbox.deploy def roundTrip[R, E <: Throwable]( app: HttpApp[R, Response], @@ -233,7 +233,7 @@ object ServerSpec extends HttpRunnableSpec { }, ) + suite("proxy") { - val server = Routes( + val server = HttpApp( Method.ANY / "proxy" / trailing -> handler { (path: Path, req: Request) => val url = URL.decode(s"http://localhost:$port/$path").toOption.get @@ -251,7 +251,7 @@ object ServerSpec extends HttpRunnableSpec { Response.text(s"Received ${method} query on ${path}") }, - ).sandbox.toHttpApp + ).sandbox test("should be able to directly return other request") { for { diff --git a/zio-http/jvm/src/test/scala/zio/http/StaticServerSpec.scala b/zio-http/jvm/src/test/scala/zio/http/StaticServerSpec.scala index 0f72ec729f..71203845f2 100644 --- a/zio-http/jvm/src/test/scala/zio/http/StaticServerSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/StaticServerSpec.scala @@ -27,25 +27,25 @@ import zio.http.internal.{DynamicServer, HttpGen, HttpRunnableSpec, serverTestLa object StaticServerSpec extends HttpRunnableSpec { - private val staticApp = Routes( + private val staticApp = HttpApp( Method.GET / "success" -> handler(Response.ok), Method.GET / "failure" -> handler(ZIO.fail(new RuntimeException("FAILURE"))), Method.GET / "die" -> handler(ZIO.die(new RuntimeException("DIE"))), Method.GET / "get%2Fsuccess" -> handler(Response.ok), - ).sandbox.toHttpApp + ).sandbox // Use this route to test anything that doesn't require ZIO related computations. - private val nonZIO = Routes( + private val nonZIO = HttpApp( Method.ANY / "ExitSuccess" -> handler(Exit.succeed(Response.ok)), Method.ANY / "ExitFailure" -> handler(Exit.fail(new RuntimeException("FAILURE"))), Method.ANY / "throwable" -> handlerTODO("Throw inside Handler"), - ).sandbox.toHttpApp + ).sandbox - private val staticAppWithCors = Routes( + private val staticAppWithCors = HttpApp( Method.GET / "success-cors" -> handler(Response.ok.addHeader(Header.Vary("test1", "test2"))), - ).toHttpApp @@ cors(CorsConfig(allowedMethods = AccessControlAllowMethods(Method.GET, Method.POST))) + ) @@ cors(CorsConfig(allowedMethods = AccessControlAllowMethods(Method.GET, Method.POST))) - private val combined = nonZIO ++ staticApp ++ staticAppWithCors + private val combined: HttpApp[Any, Response] = nonZIO ++ staticApp ++ staticAppWithCors private val app = serve { combined } diff --git a/zio-http/jvm/src/test/scala/zio/http/endpoint/EndpointSpec.scala b/zio-http/jvm/src/test/scala/zio/http/endpoint/EndpointSpec.scala index 182a18be36..5f231e72b5 100644 --- a/zio-http/jvm/src/test/scala/zio/http/endpoint/EndpointSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/endpoint/EndpointSpec.scala @@ -28,13 +28,13 @@ import zio.http._ object EndpointSpec extends ZIOHttpSpec { def spec = suite("EndpointSpec")() - def testEndpoint[R](service: Routes[R, Nothing])( + def testEndpoint[R](service: HttpApp[R, Nothing])( url: String, expected: String, ): ZIO[R, Response, TestResult] = testEndpointWithHeaders(service)(url, headers = List.empty, expected) - def testEndpointWithHeaders[R](service: Routes[R, Nothing])( + def testEndpointWithHeaders[R](service: HttpApp[R, Nothing])( url: String, headers: List[(String, String)], expected: String, @@ -43,7 +43,7 @@ object EndpointSpec extends ZIOHttpSpec { .get(url = URL.decode(url).toOption.get) .addHeaders(headers.foldLeft(Headers.empty) { case (hs, (k, v)) => hs ++ Headers(k, v) }) for { - response <- service.toHttpApp.runZIO(request) + response <- service.runZIO(request) body <- response.body.asString.orDie } yield assertTrue(body == "\"" + expected + "\"") // TODO: Real JSON Encoding } diff --git a/zio-http/jvm/src/test/scala/zio/http/endpoint/NotFoundSpec.scala b/zio-http/jvm/src/test/scala/zio/http/endpoint/NotFoundSpec.scala index 58269277dd..3440b617a0 100644 --- a/zio-http/jvm/src/test/scala/zio/http/endpoint/NotFoundSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/endpoint/NotFoundSpec.scala @@ -40,7 +40,7 @@ object NotFoundSpec extends ZIOHttpSpec { test("on wrong path") { check(Gen.int) { userId => val testRoutes = test404( - Routes( + HttpApp( Endpoint(GET / "users" / int("userId")) .out[String] .implement { @@ -65,7 +65,7 @@ object NotFoundSpec extends ZIOHttpSpec { test("on wrong method") { check(Gen.int, Gen.int, Gen.alphaNumericString) { (userId, postId, name) => val testRoutes = test404( - Routes( + HttpApp( Endpoint(GET / "users" / int("userId")) .out[String] .implement { @@ -89,13 +89,13 @@ object NotFoundSpec extends ZIOHttpSpec { }, ) - def test404[R](service: Routes[R, Nothing])( + def test404[R](service: HttpApp[R, Nothing])( url: String, method: Method, ): ZIO[R, Response, TestResult] = { val request = Request(method = method, url = URL.decode(url).toOption.get) for { - response <- service.toHttpApp.runZIO(request) + response <- service.runZIO(request) result = response.status == Status.NotFound } yield assertTrue(result) } diff --git a/zio-http/jvm/src/test/scala/zio/http/endpoint/QueryParameterSpec.scala b/zio-http/jvm/src/test/scala/zio/http/endpoint/QueryParameterSpec.scala index 3e7990e465..ea82190c04 100644 --- a/zio-http/jvm/src/test/scala/zio/http/endpoint/QueryParameterSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/endpoint/QueryParameterSpec.scala @@ -31,7 +31,7 @@ object QueryParameterSpec extends ZIOHttpSpec { test("simple request with query parameter") { check(Gen.int, Gen.int, Gen.alphaNumericString) { (userId, postId, username) => val testRoutes = testEndpoint( - Routes( + HttpApp( Endpoint(GET / "users" / int("userId")) .out[String] .implement { @@ -59,7 +59,7 @@ object QueryParameterSpec extends ZIOHttpSpec { test("optional query parameter") { check(Gen.int, Gen.alphaNumericString) { (userId, details) => val testRoutes = testEndpoint( - Routes( + HttpApp( Endpoint(GET / "users" / int("userId")) .query(query("details").optional) .out[String] @@ -78,7 +78,7 @@ object QueryParameterSpec extends ZIOHttpSpec { test("multiple optional query parameters") { check(Gen.int, Gen.alphaNumericString, Gen.alphaNumericString) { (userId, key, value) => val testRoutes = testEndpoint( - Routes( + HttpApp( Endpoint(GET / "users" / int("userId")) .query(query("key").optional) .query(query("value").optional) @@ -99,7 +99,7 @@ object QueryParameterSpec extends ZIOHttpSpec { test("query parameters with multiple values") { check(Gen.int, Gen.listOfN(3)(Gen.alphaNumericString)) { (userId, keys) => val testRoutes = testEndpoint( - Routes( + HttpApp( Endpoint(GET / "users" / int("userId")) .query(queryAll("key")) .out[String] @@ -128,7 +128,7 @@ object QueryParameterSpec extends ZIOHttpSpec { test("optional query parameters with multiple values") { check(Gen.int, Gen.listOfN(3)(Gen.alphaNumericString)) { (userId, keys) => val testRoutes = testEndpoint( - Routes( + HttpApp( Endpoint(GET / "users" / int("userId")) .query(queryAll("key").optional) .out[String] @@ -158,7 +158,7 @@ object QueryParameterSpec extends ZIOHttpSpec { check(Gen.int, Gen.listOfN(3)(Gen.alphaNumericString), Gen.listOfN(2)(Gen.alphaNumericString)) { (userId, keys, values) => val testRoutes = testEndpoint( - Routes( + HttpApp( Endpoint(GET / "users" / int("userId")) .query(queryAll("key") & queryAll("value")) .out[String] @@ -183,7 +183,7 @@ object QueryParameterSpec extends ZIOHttpSpec { test("mix of multi value and single value query parameters") { check(Gen.int, Gen.listOfN(2)(Gen.alphaNumericString), Gen.alphaNumericString) { (userId, multi, single) => val testRoutes = testEndpoint( - Routes( + HttpApp( Endpoint(GET / "users" / int("userId")) .query(queryAll("multi") & query("single")) .out[String] @@ -204,7 +204,7 @@ object QueryParameterSpec extends ZIOHttpSpec { test("either of two multi value query parameters") { check(Gen.int, Gen.listOfN(2)(Gen.alphaNumericString), Gen.listOfN(2)(Gen.boolean)) { (userId, left, right) => val testRoutes = testEndpoint( - Routes( + HttpApp( Endpoint(GET / "users" / int("userId")) .query(queryAll("left") | queryAllBool("right")) .out[String] @@ -234,7 +234,7 @@ object QueryParameterSpec extends ZIOHttpSpec { check(Gen.int, Gen.listOfN(2)(Gen.alphaNumericString), Gen.listOfN(2)(Gen.alphaNumericString)) { (userId, left, right) => val testRoutes = testEndpoint( - Routes( + HttpApp( Endpoint(GET / "users" / int("userId")) .query(queryAll("left") | queryAll("right")) .out[String] @@ -263,7 +263,7 @@ object QueryParameterSpec extends ZIOHttpSpec { test("either of multi value or single value query parameter") { check(Gen.int, Gen.listOfN(2)(Gen.alphaNumericString), Gen.alphaNumericString) { (userId, left, right) => val testRoutes = testEndpoint( - Routes( + HttpApp( Endpoint(GET / "users" / int("userId")) .query(queryAll("left") | query("right")) .out[String] @@ -291,7 +291,7 @@ object QueryParameterSpec extends ZIOHttpSpec { }, test("query parameters keys without values for multi value query") { val testRoutes = testEndpoint( - Routes( + HttpApp( Endpoint(GET / "users") .query(queryAllInt("ints")) .out[String] @@ -309,7 +309,7 @@ object QueryParameterSpec extends ZIOHttpSpec { ) }, test("no specified query parameters for multi value query") { - val testRoutes = Routes( + val testRoutes = HttpApp( Endpoint(GET / "users") .query(queryAllInt("ints")) .out[String] @@ -320,13 +320,13 @@ object QueryParameterSpec extends ZIOHttpSpec { }, ) - testRoutes.toHttpApp + testRoutes .runZIO(Request.get("/users")) .map(resp => assertTrue(resp.status == Status.BadRequest)) }, test("multiple query parameter values to single value query parameter codec") { val testRoutes = - Routes( + HttpApp( Endpoint(GET / "users") .query(queryInt("ints")) .out[String] @@ -337,7 +337,7 @@ object QueryParameterSpec extends ZIOHttpSpec { }, ) - testRoutes.toHttpApp + testRoutes .runZIO(Request.get(URL.decode("/users?ints=1&ints=2").toOption.get)) .map(resp => assertTrue(resp.status == Status.BadRequest)) }, diff --git a/zio-http/jvm/src/test/scala/zio/http/endpoint/RequestSpec.scala b/zio-http/jvm/src/test/scala/zio/http/endpoint/RequestSpec.scala index fb9b353ee1..17e70e5d13 100644 --- a/zio-http/jvm/src/test/scala/zio/http/endpoint/RequestSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/endpoint/RequestSpec.scala @@ -36,7 +36,7 @@ object RequestSpec extends ZIOHttpSpec { test("simple request with header") { check(Gen.int, Gen.int, Gen.uuid) { (userId, postId, correlationId) => val testRoutes = testEndpointWithHeaders( - Routes( + HttpApp( Endpoint(GET / "users" / int("userId")) .header(HeaderCodec.name[java.util.UUID]("X-Correlation-ID")) .out[String] @@ -188,7 +188,7 @@ object RequestSpec extends ZIOHttpSpec { test("out of order api") { check(Gen.int, Gen.int, Gen.alphaNumericString, Gen.int(1, Int.MaxValue)) { (userId, postId, name, age) => val testRoutes = testEndpoint( - Routes( + HttpApp( Endpoint(GET / "users" / int("userId")) .out[String] .implement { @@ -217,7 +217,7 @@ object RequestSpec extends ZIOHttpSpec { test("fallback") { check(Gen.int, Gen.alphaNumericString) { (userId, username) => val testRoutes = testEndpoint( - Routes( + HttpApp( Endpoint(GET / "users") .query(queryInt("userId") | query("userId")) .out[String] @@ -356,7 +356,7 @@ object RequestSpec extends ZIOHttpSpec { } val testRoutes = testEndpoint( - Routes( + HttpApp( broadUsers, broadUsersId, boardUsersPosts, diff --git a/zio-http/jvm/src/test/scala/zio/http/endpoint/RoundtripSpec.scala b/zio-http/jvm/src/test/scala/zio/http/endpoint/RoundtripSpec.scala index b5d12d8957..cc3809e462 100644 --- a/zio-http/jvm/src/test/scala/zio/http/endpoint/RoundtripSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/endpoint/RoundtripSpec.scala @@ -79,7 +79,7 @@ object RoundtripSpec extends ZIOHttpSpec { def testEndpoint[P, In, Err, Out]( endpoint: Endpoint[P, In, Err, Out, EndpointMiddleware.None.type], - route: Routes[Any, Nothing], + route: HttpApp[Any, Nothing], in: In, out: Out, ): ZIO[Client with Server with Scope, Err, TestResult] = @@ -87,12 +87,12 @@ object RoundtripSpec extends ZIOHttpSpec { def testEndpointZIO[P, In, Err, Out]( endpoint: Endpoint[P, In, Err, Out, EndpointMiddleware.None.type], - route: Routes[Any, Nothing], + route: HttpApp[Any, Nothing], in: In, outF: Out => ZIO[Any, Err, TestResult], ): zio.ZIO[Server with Client with Scope, Err, TestResult] = for { - port <- Server.install(route.toHttpApp @@ Middleware.requestLogging()) + port <- Server.install(route @@ Middleware.requestLogging()) client <- ZIO.service[Client] executor = makeExecutor(client, port) out <- executor(endpoint.apply(in)) @@ -100,13 +100,13 @@ object RoundtripSpec extends ZIOHttpSpec { } yield result def testEndpointCustomRequestZIO[P, In, Err, Out]( - route: Routes[Any, Nothing], + route: HttpApp[Any, Nothing], in: Request, outF: Response => ZIO[Any, Err, TestResult], ): zio.ZIO[Server with Client with Scope, Err, TestResult] = ZIO.scoped[Client with Server] { for { - port <- Server.install(route.toHttpApp @@ Middleware.requestLogging()) + port <- Server.install(route @@ Middleware.requestLogging()) client <- ZIO.service[Client] out <- client.request(in.updateURL(_.host("localhost").port(port))).orDie result <- outF(out) @@ -115,7 +115,7 @@ object RoundtripSpec extends ZIOHttpSpec { def testEndpointError[P, In, Err, Out]( endpoint: Endpoint[P, In, Err, Out, EndpointMiddleware.None.type], - route: Routes[Any, Nothing], + route: HttpApp[Any, Nothing], in: In, err: Err, ): ZIO[Client with Server with Scope, Out, TestResult] = @@ -123,12 +123,12 @@ object RoundtripSpec extends ZIOHttpSpec { def testEndpointErrorZIO[P, In, Err, Out]( endpoint: Endpoint[P, In, Err, Out, EndpointMiddleware.None.type], - route: Routes[Any, Nothing], + route: HttpApp[Any, Nothing], in: In, errorF: Err => ZIO[Any, Nothing, TestResult], ): ZIO[Client with Server with Scope, Out, TestResult] = for { - port <- Server.install(route.toHttpApp) + port <- Server.install(route) executorLayer = ZLayer(ZIO.service[Client].map(makeExecutor(_, port))) out <- ZIO .service[EndpointExecutor[Unit]] @@ -155,7 +155,7 @@ object RoundtripSpec extends ZIOHttpSpec { testEndpoint( usersPostAPI, - Routes(usersPostHandler), + HttpApp(usersPostHandler), (10, 20), Post(20, "title", "body", 10), ) @@ -175,7 +175,7 @@ object RoundtripSpec extends ZIOHttpSpec { testEndpoint( usersPostAPI, - Routes(usersPostHandler), + HttpApp(usersPostHandler), (10, 20, Header.Accept(MediaType.parseCustomMediaType("application/protobuf").get)), Post(20, "title", "body", 10), ) && assertZIO(TestConsole.output)(contains("ContentType: application/protobuf\n")) @@ -196,7 +196,7 @@ object RoundtripSpec extends ZIOHttpSpec { testEndpoint( usersPostAPI, - Routes(usersPostHandler), + HttpApp(usersPostHandler), (10, 20, Header.Accept(MediaType.parseCustomMediaType("application/protobuf").get)), Post(20, "title", "body", 10), ) && assertZIO(TestConsole.output)(contains("ContentType: application/protobuf\n")) @@ -218,18 +218,18 @@ object RoundtripSpec extends ZIOHttpSpec { testEndpoint( api, - Routes(handler), + HttpApp(handler), (10, 20, None, Some("x")), Post(10, "-", "x", 20), ) && testEndpoint( api, - Routes(handler), + HttpApp(handler), (10, 20, None, None), Post(10, "-", "-", 20), ) && testEndpoint( api, - Routes(handler), + HttpApp(handler), (10, 20, Some("x"), Some("y")), Post(10, "x", "y", 20), ) @@ -274,7 +274,7 @@ object RoundtripSpec extends ZIOHttpSpec { testEndpoint( api, - Routes(route), + HttpApp(route), (11, Post(1, "title", "body", 111)), "userId: 11, post: Post(1,title,body,111)", ) @@ -290,7 +290,7 @@ object RoundtripSpec extends ZIOHttpSpec { Random.nextBytes(1024 * 1024).flatMap { bytes => testEndpoint( api, - Routes(route), + HttpApp(route), ZStream.fromChunk(bytes).rechunk(1024), 1024 * 1024L, ) @@ -306,7 +306,7 @@ object RoundtripSpec extends ZIOHttpSpec { testEndpointZIO( api, - Routes(route), + HttpApp(route), 1024 * 1024, (stream: ZStream[Any, Nothing, Byte]) => stream.runCount.map(c => assert(c)(equalTo(1024L * 1024L))), ) @@ -326,7 +326,7 @@ object RoundtripSpec extends ZIOHttpSpec { testEndpoint( api, - Routes(route), + HttpApp(route), ("name", 10, Post(1, "title", "body", 111)), "name: name, value: 10, post: Post(1,title,body,111)", ) @@ -339,7 +339,7 @@ object RoundtripSpec extends ZIOHttpSpec { testEndpointError( api, - Routes(route), + HttpApp(route), (), "42", ) @@ -494,7 +494,7 @@ object RoundtripSpec extends ZIOHttpSpec { Random.nextBytes(1024 * 1024).flatMap { bytes => testEndpoint( api, - Routes(route), + HttpApp(route), ("xyz", 100, ZStream.fromChunk(bytes).rechunk(1024)), s"name: xyz, value: 100, count: ${1024 * 1024}", ) @@ -519,7 +519,7 @@ object RoundtripSpec extends ZIOHttpSpec { .nextBytes(1024 * 1024) .flatMap { bytes => testEndpointCustomRequestZIO( - Routes(route), + HttpApp(route), Request.post( "/test", Body.fromMultipartForm( diff --git a/zio-http/jvm/src/test/scala/zio/http/endpoint/openapi/SwaggerUISpec.scala b/zio-http/jvm/src/test/scala/zio/http/endpoint/openapi/SwaggerUISpec.scala index a7bfbb7f61..12d9e3b11f 100644 --- a/zio-http/jvm/src/test/scala/zio/http/endpoint/openapi/SwaggerUISpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/endpoint/openapi/SwaggerUISpec.scala @@ -34,7 +34,7 @@ object SwaggerUISpec extends ZIOSpecDefault { OpenAPIGen.fromEndpoints(title = "Another Endpoint Example", version = "2.0", getUser, getUserPosts) val routes = - Routes(getUserRoute, getUserPostsRoute) ++ SwaggerUI.routes("docs" / "openapi", openAPIv1, openAPIv2) + HttpApp(getUserRoute, getUserPostsRoute) ++ SwaggerUI.app("docs" / "openapi", openAPIv1, openAPIv2) val response = routes.apply(Request(method = Method.GET, url = url"/docs/openapi")) diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/AuthSpec.scala b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/AuthSpec.scala index 98696ccb8c..032d43a1cc 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/AuthSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/AuthSpec.scala @@ -80,11 +80,11 @@ object AuthSpec extends ZIOHttpSpec with HttpAppTestExtensions { }, test("Extract username via context with Routes") { val app = { - Routes( + HttpApp( Method.GET / "context" -> basicAuthContextM -> Handler.fromFunction[(AuthContext, Request)] { case (c: AuthContext, _) => Response.text(c.value) }, ) - }.toHttpApp + } assertZIO( app .runZIO(Request.get(URL.root / "context").copy(headers = successBasicHeader)) @@ -106,7 +106,7 @@ object AuthSpec extends ZIOHttpSpec with HttpAppTestExtensions { assertZIO(app.runZIO(Request.get(URL.empty).copy(headers = failureBasicHeader)))(isSome) }, test("Provide for multiple routes") { - val secureRoutes = Routes( + val secureRoutes = HttpApp( Method.GET / "a" -> handler((_: Request) => ZIO.serviceWith[AuthContext](ctx => Response.text(ctx.value))), Method.GET / "b" / int("id") -> handler((id: Int, _: Request) => ZIO.serviceWith[AuthContext](ctx => Response.text(s"for id: $id: ${ctx.value}")), @@ -114,9 +114,10 @@ object AuthSpec extends ZIOHttpSpec with HttpAppTestExtensions { Method.GET / "c" / string("name") -> handler((name: String, _: Request) => ZIO.serviceWith[AuthContext](ctx => Response.text(s"for name: $name: ${ctx.value}")), ), + // Needs version of @@ that removes the context from the environment ) @@ basicAuthContextM // Just a prove that the aspect can require an environment. Does nothing. - val app = secureRoutes.toHttpApp + val app = secureRoutes for { s1 <- app.runZIO(Request.get(URL(Root / "a")).copy(headers = successBasicHeader)) s1Body <- s1.body.asString.debug("s1Body") @@ -143,9 +144,9 @@ object AuthSpec extends ZIOHttpSpec with HttpAppTestExtensions { assertZIO(app.runZIO(Request.get(URL.empty).copy(headers = failureBearerHeader)))(isSome) }, test("Does not affect fallback apps") { - val app1 = Routes(Method.GET / "a" -> Handler.ok).toHttpApp - val app2 = Routes(Method.GET / "b" -> Handler.ok).toHttpApp - val app3 = Routes(Method.GET / "c" -> Handler.ok).toHttpApp + val app1 = HttpApp(Method.GET / "a" -> Handler.ok) + val app2 = HttpApp(Method.GET / "b" -> Handler.ok) + val app3 = HttpApp(Method.GET / "c" -> Handler.ok) val app = app1 ++ app2 @@ bearerAuthM ++ app3 for { s1 <- app.runZIO(Request.get(URL(Root / "a")).copy(headers = failureBearerHeader)) @@ -169,9 +170,9 @@ object AuthSpec extends ZIOHttpSpec with HttpAppTestExtensions { assertZIO(app.runZIO(Request.get(URL.empty).copy(headers = failureBearerHeader)))(isSome) }, test("Does not affect fallback apps") { - val app1 = Routes(Method.GET / "a" -> Handler.ok).toHttpApp - val app2 = Routes(Method.GET / "b" -> Handler.ok).toHttpApp - val app3 = Routes(Method.GET / "c" -> Handler.ok).toHttpApp + val app1 = HttpApp(Method.GET / "a" -> Handler.ok) + val app2 = HttpApp(Method.GET / "b" -> Handler.ok) + val app3 = HttpApp(Method.GET / "c" -> Handler.ok) val app = app1 ++ app2 @@ bearerAuthZIOM ++ app3 for { s1 <- app.runZIO(Request.get(URL(Root / "a")).copy(headers = failureBearerHeader)) diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/CorsSpec.scala b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/CorsSpec.scala index 1755ec1d2a..312e4b76c0 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/CorsSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/CorsSpec.scala @@ -27,13 +27,13 @@ import zio.http.internal.HttpAppTestExtensions object CorsSpec extends ZIOHttpSpec with HttpAppTestExtensions { def extractStatus(response: Response): Status = response.status - val app = Routes( + val app = HttpApp( Method.GET / "success" -> handler(Response.ok), Method.GET / "failure" -> handler(ZIO.fail("failure")), Method.GET / "die" -> handler(ZIO.dieMessage("die")), ).handleErrorCause { cause => Response(Status.InternalServerError, body = Body.fromString(cause.prettyPrint)) - }.toHttpApp @@ cors(CorsConfig(allowedMethods = AccessControlAllowMethods(Method.GET))) + } @@ cors(CorsConfig(allowedMethods = AccessControlAllowMethods(Method.GET))) override def spec = suite("CorsSpec")( test("OPTIONS request") { diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/MetricsSpec.scala b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/MetricsSpec.scala index a2badedaa9..5c44953fd1 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/MetricsSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/MetricsSpec.scala @@ -29,12 +29,12 @@ object MetricsSpec extends ZIOHttpSpec with HttpAppTestExtensions { override def spec: Spec[TestEnvironment with Scope, Any] = suite("MetricsSpec")( test("http_requests_total & http_errors_total") { - val app = Routes( + val app = HttpApp( Method.GET / "ok" -> Handler.ok, Method.GET / "error" -> Handler.internalServerError, Method.GET / "fail" -> Handler.fail(Response.status(Status.Forbidden)), Method.GET / "defect" -> Handler.die(new Throwable("boom")), - ).toHttpApp @@ metrics( + ) @@ metrics( extraLabels = Set(MetricLabel("test", "http_requests_total & http_errors_total")), ) @@ -105,9 +105,9 @@ object MetricsSpec extends ZIOHttpSpec with HttpAppTestExtensions { for { promise <- Promise.make[Nothing, Unit] - app = Routes( + app = HttpApp( Method.ANY / PathCodec.trailing -> (Handler.fromZIO(promise.succeed(())) *> Handler.ok.delay(10.seconds)), - ).toHttpApp @@ metrics(extraLabels = Set(MetricLabel("test", "http_concurrent_requests_total"))) + ) @@ metrics(extraLabels = Set(MetricLabel("test", "http_concurrent_requests_total"))) before <- gauge.value _ <- app.runZIO(Request.get(url = URL(Root / "slow"))).fork _ <- promise.await diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/RequestLoggingSpec.scala b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/RequestLoggingSpec.scala index 4f9773c10d..caf2b985a0 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/RequestLoggingSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/RequestLoggingSpec.scala @@ -25,12 +25,12 @@ import zio.http.internal.HttpAppTestExtensions object RequestLoggingSpec extends ZIOHttpSpec with HttpAppTestExtensions { - private val app = Routes( + private val app = HttpApp( Method.GET / "ok" -> Handler.ok, Method.GET / "error" -> Handler.internalServerError, Method.GET / "fail" -> Handler.fail(Response.status(Status.Forbidden)), Method.GET / "defect" -> Handler.die(new Throwable("boom")), - ).sandbox.toHttpApp + ).sandbox override def spec: Spec[TestEnvironment with Scope, Any] = suite("RequestLoggingSpec")( diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/WebSpec.scala b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/WebSpec.scala index 25cb09fc7a..347e345a99 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/WebSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/WebSpec.scala @@ -29,9 +29,9 @@ object WebSpec extends ZIOHttpSpec with HttpAppTestExtensions { self => def extractStatus(response: Response): Status = response.status private val app = - Routes( + HttpApp( Method.GET / "health" -> handler(ZIO.succeed(Response.ok).delay(1 second)), - ).toHttpApp + ) private val midA = Middleware.addHeader("X-Custom", "A") private val midB = Middleware.addHeader("X-Custom", "B") @@ -216,11 +216,11 @@ object WebSpec extends ZIOHttpSpec with HttpAppTestExtensions { self => ), ) checkAll(urls) { case (url, expected) => - val app = Routes( + val app = HttpApp( Method.ANY / PathCodec.trailing -> handler { (_: Path, req: Request) => Response.text(req.url.encode) }, - ).toHttpApp @@ dropTrailingSlash(onlyIfNoQueryParams = true) + ) @@ dropTrailingSlash(onlyIfNoQueryParams = true) for { url <- ZIO.fromEither(URL.decode(url)) response <- app.runZIO(Request.get(url = url)) diff --git a/zio-http/jvm/src/test/scala/zio/http/netty/NettyStreamBodySpec.scala b/zio-http/jvm/src/test/scala/zio/http/netty/NettyStreamBodySpec.scala index 6ed0e643e8..89b34a8c2f 100644 --- a/zio-http/jvm/src/test/scala/zio/http/netty/NettyStreamBodySpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/netty/NettyStreamBodySpec.scala @@ -14,7 +14,7 @@ import zio.http.netty.NettyConfig.LeakDetectionLevel object NettyStreamBodySpec extends HttpRunnableSpec { def app(streams: Iterator[ZStream[Any, Throwable, Byte]], len: Long) = - Routes( + HttpApp( Method.GET / "with-content-length" -> handler( http.Response( @@ -22,7 +22,7 @@ object NettyStreamBodySpec extends HttpRunnableSpec { body = Body.fromStream(streams.next(), len), ), ), - ).sandbox.toHttpApp + ).sandbox private def server( streams: Iterator[ZStream[Any, Throwable, Byte]], diff --git a/zio-http/jvm/src/test/scala/zio/http/netty/client/NettyConnectionPoolSpec.scala b/zio-http/jvm/src/test/scala/zio/http/netty/client/NettyConnectionPoolSpec.scala index 82e602f4da..2649765462 100644 --- a/zio-http/jvm/src/test/scala/zio/http/netty/client/NettyConnectionPoolSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/netty/client/NettyConnectionPoolSpec.scala @@ -30,11 +30,11 @@ import zio.http.netty.NettyConfig object NettyConnectionPoolSpec extends HttpRunnableSpec { - private val app = Routes( + private val app = HttpApp( Method.POST / "streaming" -> handler((req: Request) => Response(body = Body.fromStreamChunked(req.body.asStream))), Method.GET / "slow" -> handler(ZIO.sleep(1.hour).as(Response.text("done"))), Method.ANY / trailing -> handler((_: Path, req: Request) => req.body.asString.map(Response.text(_))), - ).sandbox.toHttpApp + ).sandbox private val connectionCloseHeader = Headers(Header.Connection.Close) private val keepAliveHeader = Headers(Header.Connection.KeepAlive) diff --git a/zio-http/shared/src/main/scala-2/zio/http/HttpAppVersionSpecific.scala b/zio-http/shared/src/main/scala-2/zio/http/HttpAppVersionSpecific.scala new file mode 100644 index 0000000000..02babc74ff --- /dev/null +++ b/zio-http/shared/src/main/scala-2/zio/http/HttpAppVersionSpecific.scala @@ -0,0 +1,14 @@ +package zio.http + +import zio.Tag + +trait HttpAppVersionSpecific { + private[http] class ApplyContextAspect[-Env, +Err, Env0](private val self: HttpApp[Env, Err]) { + def apply[Env1, Env2 <: Env, Ctx: Tag](aspect: HandlerAspect[Env1, Ctx])(implicit + ev: Env0 with Ctx <:< Env, + tag: Tag[Env0], + tag1: Tag[Env1], + ): HttpApp[Env0 with Env1, Err] = self.transform(_.@@[Env0](aspect)) + } + +} diff --git a/zio-http/shared/src/main/scala-3/zio/http/HttpAppVersionSpecific.scala b/zio-http/shared/src/main/scala-3/zio/http/HttpAppVersionSpecific.scala new file mode 100644 index 0000000000..8d545ca3c8 --- /dev/null +++ b/zio-http/shared/src/main/scala-3/zio/http/HttpAppVersionSpecific.scala @@ -0,0 +1,10 @@ +package zio.http + +trait HttpAppVersionSpecific { + private[http] class ApplyContextAspect[-Env, +Err, Env0](private val self: HttpApp[Env, Err]) { + transparent inline def apply[Env1, Env2 <: Env, Ctx](aspect: HandlerAspect[Env1, Ctx])(implicit + ev: Env0 with Ctx <:< Env, + ): HttpApp[Env0 with Env1, Err] = self.transform(_.@@[Env0](aspect)) + } + +} diff --git a/zio-http/shared/src/main/scala/zio/http/HandlerAspect.scala b/zio-http/shared/src/main/scala/zio/http/HandlerAspect.scala index aaf19db1dd..2cdd998a9f 100644 --- a/zio-http/shared/src/main/scala/zio/http/HandlerAspect.scala +++ b/zio-http/shared/src/main/scala/zio/http/HandlerAspect.scala @@ -90,6 +90,7 @@ final case class HandlerAspect[-Env, +CtxOut]( * Applies middleware to the specified handler, which may ignore the context * produced by this middleware. */ + @deprecated("Transform your Routes to HttpApp and use the overloaded apply for HttpApp.") def apply[Env1 <: Env, Err]( routes: Routes[Env1, Err], ): Routes[Env1, Err] = @@ -106,6 +107,26 @@ final case class HandlerAspect[-Env, +CtxOut]( } } + /** + * Applies middleware to the specified handler, which may ignore the context + * produced by this middleware. + */ + override def apply[Env1 <: Env, Err]( + routes: HttpApp[Env1, Err], + ): HttpApp[Env1, Err] = + routes.transform[Env1] { handler => + if (self == HandlerAspect.identity) handler + else { + for { + tuple <- protocol.incomingHandler + (state, (request, ctxOut)) = tuple + either <- Handler.fromZIO(handler(request)).either + response <- Handler.fromZIO(protocol.outgoingHandler((state, either.merge))) + response <- if (either.isLeft) Handler.fail(response) else Handler.succeed(response) + } yield response + } + } + /** * Applies middleware to the specified handler, which must process the context * produced by this middleware. diff --git a/zio-http/shared/src/main/scala/zio/http/HttpApp.scala b/zio-http/shared/src/main/scala/zio/http/HttpApp.scala index df414ce739..03056a8d30 100644 --- a/zio-http/shared/src/main/scala/zio/http/HttpApp.scala +++ b/zio-http/shared/src/main/scala/zio/http/HttpApp.scala @@ -18,6 +18,9 @@ package zio.http import zio._ +import zio.http.HttpApp.ApplyContextAspect +import zio.http.codec.PathCodec + /** * An HTTP application is a collection of routes, all of whose errors have been * handled through conversion into HTTP responses. @@ -29,12 +32,20 @@ final case class HttpApp[-Env, +Err](routes: Chunk[zio.http.Route[Env, Err]]) /*extends PartialFunction[Request, ZIO[Env, Response, Response]]*/ { self => private var _tree: HttpApp.Tree[_] = null.asInstanceOf[HttpApp.Tree[_]] - /** - * Applies the specified route aspect to every route in the HTTP application. - */ def @@[Env1 <: Env](aspect: Middleware[Env1]): HttpApp[Env1, Err] = aspect(self) + def @@[Env0](aspect: HandlerAspect[Env0, Unit]): HttpApp[Env with Env0, Err] = + aspect(self) + + def @@[Env0, Ctx <: Env]( + aspect: HandlerAspect[Env0, Ctx], + )(implicit tag: Tag[Ctx]): HttpApp[Env0, Err] = + self.transform(_ @@ aspect) + + def @@[Env0]: ApplyContextAspect[Env, Err, Env0] = + new ApplyContextAspect[Env, Err, Env0](self) + /** * Combines this HTTP application with the specified HTTP application. In case * of route conflicts, the routes in this HTTP application take precedence @@ -43,6 +54,18 @@ final case class HttpApp[-Env, +Err](routes: Chunk[zio.http.Route[Env, Err]]) def ++[Env1 <: Env, Err1 >: Err](that: HttpApp[Env1, Err1]): HttpApp[Env1, Err1] = copy(routes = routes ++ that.routes) + /** + * Prepend the specified route to this HttpApp + */ + def +:[Env1 <: Env, Err1 >: Err](route: zio.http.Route[Env1, Err1]): HttpApp[Env1, Err1] = + copy(routes = route +: routes) + + /** + * Appends the specified route to this HttpApp + */ + def :+[Env1 <: Env, Err1 >: Err](route: zio.http.Route[Env1, Err1]): HttpApp[Env1, Err1] = + copy(routes = routes :+ route) + /** * Executes the HTTP application with the specified request input, returning * an effect that will either succeed or fail with a Response. @@ -50,6 +73,81 @@ final case class HttpApp[-Env, +Err](routes: Chunk[zio.http.Route[Env, Err]]) def apply(request: Request)(implicit ev: Err <:< Response): ZIO[Env, Response, Response] = runZIO(request) + /** + * Handles all typed errors in the routes by converting them into responses. + * This method can be used to convert routes that do not handle their errors + * into ones that do handle their errors. + */ + def handleError(f: Err => Response)(implicit trace: Trace): HttpApp[Env, Nothing] = + new HttpApp(routes.map(_.handleError(f))) + + def handleErrorZIO(f: Err => ZIO[Any, Nothing, Response])(implicit trace: Trace): HttpApp[Env, Nothing] = + new HttpApp(routes.map(_.handleErrorZIO(f))) + + /** + * Handles all typed errors, as well as all non-recoverable errors, by + * converting them into responses. This method can be used to convert routes + * that do not handle their errors into ones that do handle their errors. + */ + def handleErrorCause(f: Cause[Err] => Response)(implicit trace: Trace): HttpApp[Env, Nothing] = + new HttpApp(routes.map(_.handleErrorCause(f))) + + /** + * Handles all typed errors, as well as all non-recoverable errors, by + * converting them into a ZIO effect that produces the response. This method + * can be used to convert routes that do not handle their errors into ones + * that do handle their errors. + */ + def handleErrorCauseZIO(f: Cause[Err] => ZIO[Any, Nothing, Response])(implicit trace: Trace): HttpApp[Env, Nothing] = + new HttpApp(routes.map(_.handleErrorCauseZIO(f))) + + /** + * Allows the transformation of the Err type through an Effectful program + * allowing one to build up a HttpApp in Stages delegates to the Route + */ + def mapErrorZIO[Err1](fxn: Err => ZIO[Any, Err1, Response])(implicit trace: Trace): HttpApp[Env, Err1] = + new HttpApp(routes.map(_.mapErrorZIO(fxn))) + + /** + * Allows the transformation of the Err type through a function allowing one + * to build up a HttpApp in Stages delegates to the Route + */ + def mapError[Err1](fxn: Err => Err1): HttpApp[Env, Err1] = + new HttpApp(routes.map(_.mapError(fxn))) + + def nest(prefix: PathCodec[Unit])(implicit trace: Trace, ev: Err <:< Response): HttpApp[Env, Err] = + new HttpApp(self.routes.map(_.nest(prefix))) + + /** + * Handles all typed errors in the routes by converting them into responses, + * taking into account the request that caused the error. This method can be + * used to convert routes that do not handle their errors into ones that do + * handle their errors. + */ + def handleErrorRequest(f: (Err, Request) => Response)(implicit trace: Trace): HttpApp[Env, Nothing] = + new HttpApp(routes.map(_.handleErrorRequest(f))) + + /** + * Handles all typed errors in the routes by converting them into responses, + * taking into account the request that caused the error. This method can be + * used to convert routes that do not handle their errors into ones that do + * handle their errors. + */ + def handleErrorRequestCause(f: (Request, Cause[Err]) => Response)(implicit trace: Trace): HttpApp[Env, Nothing] = + new HttpApp(routes.map(_.handleErrorRequestCause(f))) + + /** + * Handles all typed errors, as well as all non-recoverable errors, by + * converting them into a ZIO effect that produces the response, taking into + * account the request that caused the error. This method can be used to + * convert routes that do not handle their errors into ones that do handle + * their errors. + */ + def handleErrorRequestCauseZIO(f: (Request, Cause[Err]) => ZIO[Any, Nothing, Response])(implicit + trace: Trace, + ): HttpApp[Env, Nothing] = + new HttpApp(routes.map(_.handleErrorRequestCauseZIO(f))) + /** * Checks to see if the HTTP application may be defined at the specified * request input. Note that it is still possible for an HTTP application to @@ -67,6 +165,35 @@ final case class HttpApp[-Env, +Err](routes: Chunk[zio.http.Route[Env, Err]]) def provideEnvironment(env: ZEnvironment[Env]): HttpApp[Any, Err] = copy(routes = routes.map(_.provideEnvironment(env))) + def run(request: Request)(implicit trace: Trace): ZIO[Env, Either[Err, Response], Response] = { + + class RouteFailure[+Err](val err: Cause[Err]) extends Throwable(null, null, true, false) { + override def getMessage: String = err.unified.headOption.fold("")(_.message) + + override def getStackTrace(): Array[StackTraceElement] = + err.unified.headOption.fold[Chunk[StackTraceElement]](Chunk.empty)(_.trace).toArray + + override def getCause(): Throwable = + err.find { case Cause.Die(throwable, _) => throwable } + .orElse(err.find { case Cause.Fail(value: Throwable, _) => value }) + .orNull + + override def toString = + err.prettyPrint + } + var routeFailure: RouteFailure[Err] = null + + handleErrorCauseZIO { cause => + routeFailure = new RouteFailure(cause) + ZIO.refailCause(Cause.die(routeFailure)) + } + .apply(request) + .mapErrorCause { + case Cause.Die(value: RouteFailure[_], _) if value == routeFailure => routeFailure.err.map(Left(_)) + case cause => cause.map(Right(_)) + } + } + def run( method: Method = Method.GET, path: Path = Path.root, @@ -81,6 +208,14 @@ final case class HttpApp[-Env, +Err](routes: Chunk[zio.http.Route[Env, Err]]) def runZIO(request: Request)(implicit ev: Err <:< Response): ZIO[Env, Nothing, Response] = toHandler(ev)(request) + /** + * Returns new routes that automatically translate all failures into + * responses, using best-effort heuristics to determine the appropriate HTTP + * status code, and attaching error details using the HTTP header `Warning`. + */ + def sandbox(implicit trace: Trace): HttpApp[Env, Nothing] = + HttpApp(routes.map(_.sandbox)) + /** * Returns a new HTTP application whose requests will be timed out after the * specified duration elapses. @@ -112,6 +247,15 @@ final case class HttpApp[-Env, +Err](routes: Chunk[zio.http.Route[Env, Err]]) .merge } + /** + * Returns new new HttpApp whose handlers are transformed by the specified + * function. + */ + def transform[Env1]( + f: Handler[Env, Response, Request, Response] => Handler[Env1, Response, Request, Response], + ): HttpApp[Env1, Err] = + new HttpApp(routes.map(_.transform(f))) + /** * Accesses the underlying tree that provides fast dispatch to handlers. */ @@ -123,7 +267,7 @@ final case class HttpApp[-Env, +Err](routes: Chunk[zio.http.Route[Env, Err]]) } } -object HttpApp { +object HttpApp extends HttpAppVersionSpecific { /** * An HTTP application that does not handle any routes. @@ -136,6 +280,12 @@ object HttpApp { def apply[Env, Err](routes: Routes[Env, Err]): HttpApp[Env, Err] = HttpApp(routes.routes) + def fromIterable[Env, Err](routes: Iterable[Route[Env, Err]]): HttpApp[Env, Err] = + HttpApp(Chunk.fromIterable(routes)) + + def singleton[Env, Err](h: Handler[Env, Err, (Path, Request), Response])(implicit trace: Trace): HttpApp[Env, Err] = + HttpApp(Route.route(RoutePattern.any)(h)) + private[http] final case class Tree[-Env](tree: RoutePattern.Tree[RequestHandler[Env, Response]]) { self => final def ++[Env1 <: Env](that: Tree[Env1]): Tree[Env1] = Tree(self.tree ++ that.tree) diff --git a/zio-http/shared/src/main/scala/zio/http/Middleware.scala b/zio-http/shared/src/main/scala/zio/http/Middleware.scala index 4b499ef248..c0c0803cb6 100644 --- a/zio-http/shared/src/main/scala/zio/http/Middleware.scala +++ b/zio-http/shared/src/main/scala/zio/http/Middleware.scala @@ -16,8 +16,10 @@ package zio.http import java.io.File + import zio._ import zio.metrics._ + import zio.http.codec.{PathCodec, SegmentCodec} import zio.http.endpoint.EndpointMiddleware.None.Err @@ -346,11 +348,11 @@ object Middleware extends HandlerAspects { acc || stop } - override def apply[Env1 <: Any, Err](routes: Routes[Env1, Err]): Routes[Env1, Err] = { + override def apply[Env1 <: Any, Err](routes: HttpApp[Env1, Err]): HttpApp[Env1, Err] = { val mountpoint = Method.GET / path.segments.map(PathCodec.literal).reduceLeftOption(_ / _).getOrElse(PathCodec.empty) val pattern = mountpoint / trailing - val other = Routes( + val other = HttpApp( pattern -> Handler .identity[Request] .flatMap { request => @@ -369,6 +371,9 @@ object Middleware extends HandlerAspects { ) routes ++ other } + + override def apply[Env1 <: Any, Err](routes: Routes[Env1, Err]): Routes[Env1, Err] = + Routes.fromIterable(apply(HttpApp(routes)).routes) } /** diff --git a/zio-http/shared/src/main/scala/zio/http/Route.scala b/zio-http/shared/src/main/scala/zio/http/Route.scala index 44a6a19df7..5946fe05d6 100644 --- a/zio-http/shared/src/main/scala/zio/http/Route.scala +++ b/zio-http/shared/src/main/scala/zio/http/Route.scala @@ -284,7 +284,7 @@ sealed trait Route[-Env, +Err] { self => * the request, or else this method will fail fatally. */ final def run(request: Request)(implicit trace: Trace): ZIO[Env, Either[Err, Response], Response] = - Routes(self).run(request) + HttpApp(self).run(request) /** * Returns a route that automatically translates all failures into responses, diff --git a/zio-http/shared/src/main/scala/zio/http/Routes.scala b/zio-http/shared/src/main/scala/zio/http/Routes.scala index 63d0548464..7cef1197e6 100644 --- a/zio-http/shared/src/main/scala/zio/http/Routes.scala +++ b/zio-http/shared/src/main/scala/zio/http/Routes.scala @@ -174,11 +174,6 @@ final class Routes[-Env, +Err] private (val routes: Chunk[zio.http.Route[Env, Er .orElse(err.find { case Cause.Fail(value: Throwable, _) => value }) .orNull - def fillSuppressed()(implicit unsafe: Unsafe): Unit = - if (getSuppressed().length == 0) { - err.unified.iterator.drop(1).foreach(unified => addSuppressed(unified.toThrowable)) - } - override def toString = err.prettyPrint } @@ -231,6 +226,7 @@ object Routes extends RoutesVersionSpecific { /** * Constructs new routes from a varargs of individual routes. */ + @deprecated("Use HttpApp.apply instead. Will be removed in the next release.") def apply[Env, Err](route: zio.http.Route[Env, Err], routes: zio.http.Route[Env, Err]*): Routes[Env, Err] = new Routes(Chunk(route) ++ Chunk.fromIterable(routes)) @@ -242,6 +238,7 @@ object Routes extends RoutesVersionSpecific { /** * Constructs new routes from an iterable of individual routes. */ + @deprecated("Use HttpApp.fromIterable instead. Will be removed in the next release.") def fromIterable[Env, Err](iterable: Iterable[Route[Env, Err]]): Routes[Env, Err] = new Routes(Chunk.fromIterable(iterable)) @@ -249,6 +246,7 @@ object Routes extends RoutesVersionSpecific { * Constructs a singleton route from a handler that handles all possible * methods and paths. You would only use this method for testing. */ + @deprecated("Use HttpApp.singleton instead. Will be removed in the next release.") def singleton[Env, Err](h: Handler[Env, Err, (Path, Request), Response])(implicit trace: Trace): Routes[Env, Err] = Routes(Route.route(RoutePattern.any)(h)) } diff --git a/zio-http/shared/src/main/scala/zio/http/codec/PathCodec.scala b/zio-http/shared/src/main/scala/zio/http/codec/PathCodec.scala index a268b3eb2d..ed7ced7ad1 100644 --- a/zio-http/shared/src/main/scala/zio/http/codec/PathCodec.scala +++ b/zio-http/shared/src/main/scala/zio/http/codec/PathCodec.scala @@ -49,9 +49,9 @@ sealed trait PathCodec[A] { self => final def /[B](that: PathCodec[B])(implicit combiner: Combiner[A, B]): PathCodec[combiner.Out] = self ++ that - final def /[Env](routes: Routes[Env, Response])(implicit + final def /[Env](routes: HttpApp[Env, Response])(implicit ev: PathCodec[A] <:< PathCodec[Unit], - ): Routes[Env, Response] = + ): HttpApp[Env, Response] = routes.nest(ev(self)) final def annotate(metaData: MetaData[A]): PathCodec[A] = { diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala index 4d39183234..29792d7835 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala @@ -217,7 +217,7 @@ final case class Endpoint[PathInput, Input, Err, Output, Middleware <: EndpointM } log.as(response) } - case None => + case None => Handler.failCause(cause) } } diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/openapi/SwaggerUI.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/openapi/SwaggerUI.scala index 215b1786d8..bc10d1c36d 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/openapi/SwaggerUI.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/openapi/SwaggerUI.scala @@ -28,10 +28,33 @@ object SwaggerUI { * of the OpenAPI specification and is url encoded. */ //format: on + @deprecated("Use app instead. Will be removed with the next release.") def routes(path: PathCodec[Unit], api: OpenAPI, apis: OpenAPI*): Routes[Any, Response] = { routes(path, DefaultSwaggerUIVersion, api, apis: _*) } + /** + * Creates routes for serving the Swagger UI at the given path. + * + * Example: + * {{{ + * val routes: Routes[Any, Response] = ??? + * val openAPIv1: OpenAPI = ??? + * val openAPIv2: OpenAPI = ??? + * val swaggerUIRoutes = SwaggerUI.routes("docs" / "openapi", openAPIv1, openAPIv2) + * val routesWithSwagger = routes ++ swaggerUIRoutes + * }}} + * + * With this middleware in place, a request to `https://www.domain.com/[path]` + * would serve the Swagger UI. The different OpenAPI specifications are served + * at `https://www.domain.com/[path]/[title].json`. Where `title` is the title + * of the OpenAPI specification and is url encoded. + */ + // format: on + def app(path: PathCodec[Unit], api: OpenAPI, apis: OpenAPI*): HttpApp[Any, Response] = { + app(path, DefaultSwaggerUIVersion, api, apis: _*) + } + //format: off /** * Creates a middleware for serving the Swagger UI at the given path and with @@ -52,6 +75,7 @@ object SwaggerUI { * of the OpenAPI specification and is url encoded. */ //format: on + @deprecated("Use app instead. Will be removed with the next release.") def routes(path: PathCodec[Unit], version: String, api: OpenAPI, apis: OpenAPI*): Routes[Any, Response] = { import zio.http.template._ val basePath = Method.GET / path @@ -101,4 +125,73 @@ object SwaggerUI { } Routes.fromIterable(jsonRoutes) :+ uiRoute } + + /** + * Creates a middleware for serving the Swagger UI at the given path and with + * the given swagger ui version. + * + * Example: + * {{{ + * val routes: Routes[Any, Response] = ??? + * val openAPIv1: OpenAPI = ??? + * val openAPIv2: OpenAPI = ??? + * val swaggerUIRoutes = SwaggerUI.routes("docs" / "openapi", openAPIv1, openAPIv2) + * val routesWithSwagger = routes ++ swaggerUIRoutes + * }}} + * + * With this middleware in place, a request to `https://www.domain.com/[path]` + * would serve the Swagger UI. The different OpenAPI specifications are served + * at `https://www.domain.com/[path]/[title].json`. Where `title` is the title + * of the OpenAPI specification and is url encoded. + */ + // format: on + def app(path: PathCodec[Unit], version: String, api: OpenAPI, apis: OpenAPI*): HttpApp[Any, Response] = { + import zio.http.template._ + val basePath = Method.GET / path + val jsonRoutes = (api +: apis).map { api => + basePath / s"${URLEncoder.encode(api.info.title, Charsets.Utf8.name())}.json" -> handler { (_: Request) => + Response.json(api.toJson) + } + } + val jsonPaths = jsonRoutes.map(_.routePattern.pathCodec.render) + val jsonTitles = (api +: apis).map(_.info.title) + val jsonUrls = jsonTitles.zip(jsonPaths).map { case (title, path) => s"""{url: "$path", name: "$title"}""" } + val uiRoute = basePath -> handler { (_: Request) => + Response.html( + html( + head( + meta(charsetAttr := "utf-8"), + meta(nameAttr := "viewport", contentAttr := "width=device-width, initial-scale=1"), + meta(nameAttr := "description", contentAttr := "SwaggerUI"), + title("SwaggerUI"), + link(relAttr := "stylesheet", href := s"https://unpkg.com/swagger-ui-dist@$version/swagger-ui.css"), + link( + relAttr := "icon", + typeAttr := "image/png", + href := s"https://unpkg.com/swagger-ui-dist@$version/favicon-32x32.png", + ), + ), + body( + div(id := "swagger-ui"), + script(srcAttr := s"https://unpkg.com/swagger-ui-dist@$version/swagger-ui-bundle.js"), + script(srcAttr := s"https://unpkg.com/swagger-ui-dist@$version/swagger-ui-standalone-preset.js"), + Dom.raw(s"""""".stripMargin), + ), + ), + ) + } + HttpApp.fromIterable(jsonRoutes) :+ uiRoute + } } From da912a0f41911f0ce9c86ee360390d004600fce0 Mon Sep 17 00:00:00 2001 From: Nabil Abdel-Hafeez <7283535+987Nabil@users.noreply.github.com> Date: Tue, 23 Apr 2024 18:15:43 +0200 Subject: [PATCH 3/3] Remove HttpApp from internal API and examples --- README.md | 2 +- docs/overview.md | 86 +++---- docs/reference/body.md | 4 +- docs/reference/cookies.md | 12 +- docs/reference/endpoint.md | 2 +- docs/reference/flash.md | 18 +- docs/reference/handler.md | 34 +-- docs/reference/handler_aspect.md | 4 +- docs/reference/headers.md | 21 +- docs/reference/middleware.md | 28 +-- docs/reference/path_codec.md | 14 +- docs/reference/request.md | 8 +- docs/reference/response.md | 24 +- docs/reference/routes.md | 38 +--- docs/reference/server.md | 42 ++-- docs/reference/socket/socket.md | 2 +- .../http/benchmarks/EndpointBenchmark.scala | 55 +++-- .../zhttp.benchmarks/HttpCollectEval.scala | 4 +- .../zhttp.benchmarks/HttpCombineEval.scala | 2 +- .../ServerInboundHandlerBenchmark.scala | 2 +- .../scala/zio/http/endpoint/cli/CliSpec.scala | 6 +- .../scala/example/AuthenticationServer.scala | 4 +- .../src/main/scala/example/BasicAuth.scala | 4 +- .../src/main/scala/example/ClientServer.scala | 2 +- .../main/scala/example/ConcreteEntity.scala | 4 +- .../main/scala/example/CookieServerSide.scala | 2 +- .../main/scala/example/EndpointExamples.scala | 2 +- .../main/scala/example/FileStreaming.scala | 2 +- .../main/scala/example/GracefulShutdown.scala | 4 +- .../src/main/scala/example/HelloWorld.scala | 2 +- .../scala/example/HelloWorldAdvanced.scala | 4 +- .../scala/example/HelloWorldWithCORS.scala | 8 +- .../scala/example/HelloWorldWithMetrics.scala | 8 +- .../example/HelloWorldWithMiddlewares.scala | 4 +- .../main/scala/example/HtmlTemplating.scala | 4 +- .../main/scala/example/HttpsHelloWorld.scala | 4 +- .../src/main/scala/example/Interrupt.scala | 2 +- .../scala/example/MultipartFormData.scala | 4 +- .../example/MultipartFormDataStreaming.scala | 4 +- .../example/PlainTextBenchmarkServer.scala | 10 +- .../main/scala/example/RequestStreaming.scala | 2 +- .../src/main/scala/example/SSEServer.scala | 4 +- .../scala/example/ServeOnAnyOpenPort.scala | 2 +- .../example/ServerConfigurationExample.scala | 2 +- .../example/ServerResponseCompression.scala | 2 +- .../example/ServerSentEventEndpoint.scala | 4 +- .../src/main/scala/example/SignCookies.scala | 2 +- .../example/SimpleEffectBenchmarkServer.scala | 4 +- .../src/main/scala/example/StaticFiles.scala | 4 +- .../src/main/scala/example/StaticServer.scala | 2 +- .../scala/example/StreamingResponse.scala | 4 +- .../scala/example/WebSocketAdvanced.scala | 4 +- .../main/scala/example/WebSocketEcho.scala | 4 +- ...equestBodyJsonDeserializationExample.scala | 4 +- ...ResponseBodyJsonSerializationExample.scala | 4 +- .../endpoint/BooksEndpointExample.scala | 2 +- .../scala/example/endpoint/CliExamples.scala | 2 +- .../example/endpoint/EndpointWithError.scala | 4 +- ...ndpointWithMultipleErrorsUsingEither.scala | 4 +- .../EndpointWithMultipleUnifiedErrors.scala | 4 +- .../src/main/scala/zio/http/TestClient.scala | 16 +- .../src/main/scala/zio/http/TestServer.scala | 10 +- .../scala/zio/http/SocketContractSpec.scala | 2 +- .../test/scala/zio/http/TestClientSpec.scala | 4 +- .../zio/http/netty/server/NettyDriver.scala | 8 +- .../netty/server/ServerInboundHandler.scala | 4 +- .../scala/zio/http/netty/server/package.scala | 2 +- .../test/scala/zio/http/ClientProxySpec.scala | 11 +- .../src/test/scala/zio/http/ClientSpec.scala | 18 +- .../scala/zio/http/ClientStreamingSpec.scala | 2 +- .../test/scala/zio/http/ContentTypeSpec.scala | 16 +- .../src/test/scala/zio/http/DualSSLSpec.scala | 4 +- .../test/scala/zio/http/DynamicAppTest.scala | 8 +- .../src/test/scala/zio/http/FlashSpec.scala | 2 +- .../test/scala/zio/http/KeepAliveSpec.scala | 14 +- .../http/LogAnnotationMiddlewareSpec.scala | 3 - .../scala/zio/http/MultipartMixedSpec.scala | 2 +- .../zio/http/NettyMaxHeaderLengthSpec.scala | 7 +- .../http/NettyMaxInitialLineLengthSpec.scala | 7 +- .../zio/http/RequestStreamingServerSpec.scala | 11 +- .../zio/http/ResponseCompressionSpec.scala | 6 +- .../src/test/scala/zio/http/RouteSpec.scala | 14 +- ...eSpec.scala => RoutesMiddlewareSpec.scala} | 2 +- .../{HttpAppSpec.scala => RoutesSpec.scala} | 10 +- .../jvm/src/test/scala/zio/http/SSLSpec.scala | 2 +- .../src/test/scala/zio/http/ServerSpec.scala | 120 +++++----- .../test/scala/zio/http/ServerStartSpec.scala | 4 +- .../scala/zio/http/StaticFileServerSpec.scala | 16 +- .../scala/zio/http/StaticServerSpec.scala | 8 +- .../src/test/scala/zio/http/StatusSpec.scala | 2 +- .../test/scala/zio/http/WebSocketConfig.scala | 2 +- .../test/scala/zio/http/WebSocketSpec.scala | 10 +- .../scala/zio/http/ZClientAspectSpec.scala | 16 +- .../zio/http/endpoint/BadRequestSpec.scala | 18 +- .../zio/http/endpoint/CustomErrorSpec.scala | 23 +- .../zio/http/endpoint/EndpointSpec.scala | 4 +- .../zio/http/endpoint/MultipartSpec.scala | 8 +- .../zio/http/endpoint/NotFoundSpec.scala | 6 +- .../http/endpoint/QueryParameterSpec.scala | 26 +-- .../scala/zio/http/endpoint/RequestSpec.scala | 51 +++-- .../zio/http/endpoint/RoundtripSpec.scala | 56 +++-- .../http/endpoint/openapi/SwaggerUISpec.scala | 2 +- .../zio/http/internal/DynamicServer.scala | 18 +- .../http/internal/HttpAppTestExtensions.scala | 2 +- .../zio/http/internal/HttpRunnableSpec.scala | 10 +- .../http/internal/middlewares/AuthSpec.scala | 16 +- .../http/internal/middlewares/CorsSpec.scala | 2 +- .../internal/middlewares/MetricsSpec.scala | 14 +- .../middlewares/RequestLoggingSpec.scala | 2 +- .../http/internal/middlewares/WebSpec.scala | 20 +- .../zio/http/netty/NettyStreamBodySpec.scala | 2 +- .../client/NettyConnectionPoolSpec.scala | 2 +- .../zio/http/HttpAppVersionSpecific.scala | 14 -- ...a => RoutesCompanionVersionSpecific.scala} | 2 +- .../zio/http/HttpAppVersionSpecific.scala | 10 - ...a => RoutesCompanionVersionSpecific.scala} | 2 +- .../src/main/scala/zio/http/Driver.scala | 2 +- .../src/main/scala/zio/http/Handler.scala | 18 +- .../main/scala/zio/http/HandlerAspect.scala | 23 +- .../src/main/scala/zio/http/HttpApp.scala | 210 +++--------------- .../src/main/scala/zio/http/Middleware.scala | 12 +- .../src/main/scala/zio/http/Route.scala | 7 +- .../src/main/scala/zio/http/Routes.scala | 192 ++++++++++------ .../src/main/scala/zio/http/Server.scala | 31 ++- .../src/main/scala/zio/http/Status.scala | 5 +- .../main/scala/zio/http/WebSocketApp.scala | 6 +- .../main/scala/zio/http/codec/PathCodec.scala | 4 +- .../zio/http/endpoint/openapi/SwaggerUI.scala | 92 -------- .../http/multipart/mixed/MultipartMixed.scala | 25 +-- 129 files changed, 796 insertions(+), 1054 deletions(-) rename zio-http/jvm/src/test/scala/zio/http/{HttpAppMiddlewareSpec.scala => RoutesMiddlewareSpec.scala} (97%) rename zio-http/jvm/src/test/scala/zio/http/{HttpAppSpec.scala => RoutesSpec.scala} (88%) delete mode 100644 zio-http/shared/src/main/scala-2/zio/http/HttpAppVersionSpecific.scala rename zio-http/shared/src/main/scala-2/zio/http/{RoutesVersionSpecific.scala => RoutesCompanionVersionSpecific.scala} (90%) delete mode 100644 zio-http/shared/src/main/scala-3/zio/http/HttpAppVersionSpecific.scala rename zio-http/shared/src/main/scala-3/zio/http/{RoutesVersionSpecific.scala => RoutesCompanionVersionSpecific.scala} (89%) diff --git a/README.md b/README.md index 2149e49844..0e8069274c 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ import zio.http._ object GreetingServer extends ZIOAppDefault { val routes = - HttpApp( + Routes( Method.GET / "greet" -> handler { (req: Request) => val name = req.queryParamToOrElse("name", "World") Response.text(s"Hello $name!") diff --git a/docs/overview.md b/docs/overview.md index 812ee56dc0..79e4cf642d 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -11,10 +11,9 @@ ZIO HTTP has powerful functional domains that help in creating, modifying, and c The core concepts of ZIO HTTP are: -- `HttpApp` - A collection of `Routes`s that are ready to be served. All errors are handled through conversion into HTTP responses. -- `Routes` - A collection of `Route`s. -- `Route` - A single route that can be matched against an http `Request` and produce a `Response`. It comprises a `RoutePattern` and a `Handler`: - 1. `RoutePattern` - A pattern that can be matched against an http request. It is a combination of `Method` and `PathCodec` which can be used to match the method and path of the request. +- `Routes` - A collection of `Route`s. If the error type of the routes is `Response`, then they can be served. +- `Route` - A single route that can be matched against a http `Request` and produce a `Response`. It comprises a `RoutePattern` and a `Handler`: + 1. `RoutePattern` - A pattern that can be matched against a http request. It is a combination of `Method` and `PathCodec` which can be used to match the method and path of the request. 2. `Handler` - A function that can convert a `Request` into a `Response`. Let's see each of these concepts inside a simple example: @@ -32,11 +31,11 @@ object ExampleServer extends ZIOAppDefault { val greetRoute: Route[Any, Nothing] = // The whole Method.GET / "greet" is a RoutePattern Method.GET / "greet" -> - // The handler is a function that takes a Request and returns a Response - handler { (req: Request) => - val name = req.queryParamToOrElse("name", "World") - Response.text(s"Hello $name!") - } + // The handler is a function that takes a Request and returns a Response + handler { (req: Request) => + val name = req.queryParamToOrElse("name", "World") + Response.text(s"Hello $name!") + } // A route that matches POST requests to /echo // It doesn't require any service from the ZIO environment @@ -46,27 +45,25 @@ object ExampleServer extends ZIOAppDefault { req.body.asString.map(Response.text(_)) } - // The HttpApp that doesn't require any service from the ZIO environment, + // The Routes that don't require any service from the ZIO environment, // so the first type parameter is Any. - // All the errors are handled - val app: HttpApp[Any] = + // All the errors are handled by turning them into a Response. + val routes: Routes[Any, Response] = // List of all the routes Routes(greetRoute, echoRoute) - // Handle all unhandled errors - .handleError(e => Response.internalServerError(e.getMessage)) - // Convert the routes to an HttpApp - .toHttpApp - - // Serving the app using the default server layer on port 8080 - def run = Server.serve(app).provide(Server.default) + // Handle all unhandled errors + .handleError(e => Response.internalServerError(e.getMessage)) + + // Serving the routes using the default server layer on port 8080 + def run = Server.serve(routes).provide(Server.default) } ``` -### 1. HttpApp +### 1.Routes -The `HttpApp` provides input-dependent routing to different `Handler` values. +The `Routes` provides input-dependent routing to different `Handler` values. -The `Handler`, `Route` and `Routes` can always be transformed to a `HttpApp` value using the `.toHttpApp` method, in which case the HTTP application will handle incomming routes. Before converting to `HttpApp`, we should handle all unhandled errors, e.g.: +The `Handler` and `Route` can be transformed to `Routes` by the `.toRoutes` method. To serve the routes, all errors should be handled by converting them into a `Response` using for example the `.handleError` method. ```scala mdoc:invisible import zio.http._ @@ -78,23 +75,12 @@ val echoRoute: Route[Any, Throwable] = Route.notFound ```scala mdoc:silent import zio.http._ -val app: HttpApp[Any] = +val app: Routes[Any, Response] = Routes(greetRoute, echoRoute) .handleError(e => Response.internalServerError(e.getMessage)) - .toHttpApp -``` - -### 2. Routes - -For handling routes, ZIO HTTP has a [`Routes`](reference/routes.md) value, which allows us to aggregate a collection of individual routes. Behind the scenes, ZIO HTTP builds an efficient prefix-tree whenever needed to optimize dispatch. - -The `Routes` is a collection of `Route` values. It can be created using its default constructor: - -```scala mdoc:silent -val routes = Routes(greetRoute, echoRoute) ``` -### 3. Route +### 2. Route Each `Route` is a combination of a [`RoutePattern`](reference/route_pattern.md) and a [`Handler`](reference/handler.md). The `RoutePattern` is a combination of a `Method` and a [`PathCodec`](reference/path_codec.md) that can be used to match the method and path of the request. The `Handler` is a function that can convert a `Request` into a `Response`. @@ -113,12 +99,12 @@ val routes = Routes( To learn more about routes, see the [Routes](reference/routes.md) page. -### 4. Handler +### 3. Handler The `Handler` describes the transformation from an incoming `Request` to an outgoing `Response`: ```scala mdoc:compile-only -val helloHanlder = +val helloHandler = handler { (_: Request) => Response.text("Hello World!") } @@ -165,18 +151,18 @@ import zio._ import zio.http._ object CounterExample extends ZIOAppDefault { - val app: HttpApp[Ref[Int]] = + val routes: Routes[Ref[Int], Response] = Routes( Method.GET / "count" / int("n") -> - handler { (n: Int, _: Request) => - for { - ref <- ZIO.service[Ref[Int]] - res <- ref.updateAndGet(_ + n) - } yield Response.text(s"Counter: $res") - }, - ).toHttpApp - - def run = Server.serve(app).provide(Server.default, ZLayer.fromZIO(Ref.make(0))) + handler { (n: Int, _: Request) => + for { + ref <- ZIO.service[Ref[Int]] + res <- ref.updateAndGet(_ + n) + } yield Response.text(s"Counter: $res") + }, + ) + + def run = Server.serve(routes).provide(Server.default, ZLayer.fromZIO(Ref.make(0))) } ``` @@ -227,10 +213,10 @@ import zio.http._ import zio._ object HelloWorld extends ZIOAppDefault { - val app = Handler.ok.toHttpApp + val routes = Handler.ok.toRoutes override def run = - Server.serve(app).provide(Server.defaultWithPort(8090)) + Server.serve(routes).provide(Server.defaultWithPort(8090)) } ``` @@ -257,4 +243,4 @@ object ClientExample extends ZIOAppDefault { } ``` -In the above example, we obtained the `Client` service from the environment and sent a `GET` request to the server. Finally, to run the client app, we provided the default `Client` and `Scope` services to the app. For more information about the client, see the [Client](reference/client.md) page. \ No newline at end of file +In the above example, we obtained the `Client` service from the environment and sent a `GET` request to the server. Finally, to run the client app, we provided the default `Client` and `Scope` services to the app. For more information about the client, see the [Client](reference/client.md) page. diff --git a/docs/reference/body.md b/docs/reference/body.md index e1a94d6c0a..929cac3e97 100644 --- a/docs/reference/body.md +++ b/docs/reference/body.md @@ -20,8 +20,8 @@ import zio._ import zio.http._ object HelloExample extends ZIOAppDefault { - val app: HttpApp[Any, Response] = - HttpApp( + val app: Routes[Any, Response] = + Routes( Method.GET / "hello" -> handler { req: Request => for { diff --git a/docs/reference/cookies.md b/docs/reference/cookies.md index cff5746e5b..6a0b1d53df 100644 --- a/docs/reference/cookies.md +++ b/docs/reference/cookies.md @@ -69,7 +69,7 @@ Let's write a simple example to see how it works: import zio.http._ object ResponseCookieExample extends ZIOAppDefault { - val httpApp = HttpApp( + val routes = Routes( Method.GET / "cookie" -> handler { Response.ok.addCookie( Cookie.Response(name = "user_id", content = "user123", maxAge = Some(5.days)) @@ -77,7 +77,7 @@ object ResponseCookieExample extends ZIOAppDefault { }, ) - def run = Server.serve(httpApp).provide(Server.default) + def run = Server.serve(routes).provide(Server.default) } ``` @@ -150,7 +150,7 @@ The cookies can be signed with a signature: ```scala mdoc:silent:nest val cookie = Cookie.Response("key", "hello", maxAge = Some(5.days)) val app = - HttpApp( + Routes( Method.GET / "cookie" -> handler { Response.ok.addCookie(cookie.sign("secret")) } @@ -164,7 +164,7 @@ To sign all the cookies in your routes, we can use `signCookies` middleware: ```scala mdoc:silent:nest import Middleware.signCookies -val app = HttpApp( +val app = Routes( Method.GET / "cookie" -> handler(Response.ok.addCookie(cookie)), Method.GET / "secure-cookie" -> handler(Response.ok.addCookie(cookie.copy(isSecure = true))) ) @@ -205,7 +205,7 @@ From HTTP requests, a single cookie can be retrieved with `Request#cookie`: ```scala mdoc:compile-only private val app4 = - HttpApp( + Routes( Method.GET / "cookie" -> handler { (req: Request) => val cookieContent = req.cookie("sessionId").map(_.content) Response.text(s"cookie content: $cookieContent") @@ -219,7 +219,7 @@ In HTTP requests, cookies are stored in the `Header.cookie` header: ```scala mdoc:compile-only private val app3 = - HttpApp( + Routes( Method.GET / "cookie" -> handler { (req: Request) => Response.text( req.header(Header.Cookie) diff --git a/docs/reference/endpoint.md b/docs/reference/endpoint.md index fa3176f378..34a7e6f554 100644 --- a/docs/reference/endpoint.md +++ b/docs/reference/endpoint.md @@ -268,7 +268,7 @@ object EndpointWithMultipleOutputTypes extends ZIOAppDefault { else Left(Quiz("What is the boiling point of water in Celsius?", 2)), ) }) - .toHttpApp).provide(Server.default, Scope.default) + .toRoutes).provide(Server.default, Scope.default) } ``` diff --git a/docs/reference/flash.md b/docs/reference/flash.md index 034914588a..eae8c561fd 100644 --- a/docs/reference/flash.md +++ b/docs/reference/flash.md @@ -157,7 +157,7 @@ object NotificationWithoutFlash extends ZIOAppDefault { } - def run = Server.serve(HttpApp(saveUserRoute, homeRoute)) + def run = Server.serve(Routes(saveUserRoute, homeRoute)) .provide(Server.default, ZLayer(Ref.make(List.empty[User]))) } ``` @@ -350,7 +350,7 @@ val getUsersRoute: Route[Ref[List[User]] with Flash.Backend, Nothing] = } yield Response.html(html ++ usersHTML) } - val app = HttpApp(saveUserRoute, getUsersRoute, homeRoute) + val app = Routes(saveUserRoute, getUsersRoute, homeRoute) def run = Server.serve(app).provide(Server.default, Flash.Backend.inMemory, ZLayer(Ref.make(List.empty[User]))) } @@ -520,7 +520,7 @@ object ui { } object SetGetBothFlashExample extends ZIOAppDefault { - val httpApp = HttpApp( + val routes = Routes( Method.GET / "set-flash" -> handler { val setBoth: Flash.Setter[(String, String)] = Flash.setNotice("The form was submitted successfully!") ++ @@ -538,7 +538,7 @@ object SetGetBothFlashExample extends ZIOAppDefault { }, ).sandbox - def run = Server.serve(httpApp).provide(Server.default, Flash.Backend.inMemory) + def run = Server.serve(routes).provide(Server.default, Flash.Backend.inMemory) } ``` @@ -555,7 +555,7 @@ import zio._ import zio.http._ object CookieBasedFlashExample extends ZIOAppDefault { - val httpApp = HttpApp( + val routes = Routes( Method.GET / "set-flash" -> handler { Response .seeOther(URL.root / "get-flash") @@ -570,7 +570,7 @@ object CookieBasedFlashExample extends ZIOAppDefault { }, ).sandbox - def run = Server.serve(httpApp).provide(Server.default) + def run = Server.serve(routes).provide(Server.default) } ``` @@ -609,7 +609,7 @@ import zio.http._ import zio.http.template._ object FlashBackendExample extends ZIOAppDefault { - val httpApp = HttpApp( + val routes = Routes( Method.GET / "set-flash" -> handler { for { flashBackend <- ZIO.service[Flash.Backend] @@ -627,7 +627,7 @@ object FlashBackendExample extends ZIOAppDefault { }, ).sandbox - def run = Server.serve(httpApp).provide(Server.default, Flash.Backend.inMemory) + def run = Server.serve(routes).provide(Server.default, Flash.Backend.inMemory) } ``` @@ -649,7 +649,7 @@ HTTP/1.1 200 OK content-type: text/plain content-length: 28 -The form was submitted successfully!⏎ +The form was submitted successfully! ``` :::note diff --git a/docs/reference/handler.md b/docs/reference/handler.md index d6d67eb22b..187cd5fa6b 100644 --- a/docs/reference/handler.md +++ b/docs/reference/handler.md @@ -39,7 +39,7 @@ Let's look at some examples of creating handlers, using the `handler` smart cons import zio._ import zio.http._ -HttpApp( +Routes( // 1. A simple handler that returns a "Hello, World!" response Method.GET / "hello" -> @@ -202,7 +202,7 @@ Let's try an example: import zio.http._ import zio.stream._ -HttpApp( +Routes( Method.GET / "stream" -> Handler .fromStream( @@ -239,7 +239,7 @@ Now, let's try another example, this time using `fromStreamChunked`: import zio.http._ import zio.stream._ -HttpApp( +Routes( Method.GET / "stream" -> Handler .fromStreamChunked( @@ -273,7 +273,7 @@ import zio.http._ import zio.stream._ import zio.http.template._ -HttpApp( +Routes( Method.GET / "html" -> Handler.html( @@ -320,7 +320,7 @@ ZIP HTTP has a simple built-in template which is useful for creating simple HTML import zio.http._ import zio.http.template._ -HttpApp( +Routes( Method.GET / "hello" -> Handler.template("Hello world!")( html( @@ -396,7 +396,7 @@ import zio.http._ import zio.stream._ import zio.schema.codec.JsonCodec.zioJsonBinaryCodec -HttpApp( +Routes( Method.POST / "bounded-body-consumer" -> handler { (request: Request) => Handler @@ -418,7 +418,7 @@ The following example shows how to create a handler that takes an `Int` and `Req import zio.json._ import zio.http._ -HttpApp( +Routes( Method.GET / "users" / int("userId") -> Handler.fromFunction[(Int, Request)] { case (userId: Int, request: Request) => Response.json( @@ -455,7 +455,7 @@ Let's see an example: import zio.http._ import java.io.File -HttpApp( +Routes( Method.GET / "video" -> Handler.fromFile(new File("src/main/resources/TestVideoFile.mp4")), Method.GET / "text" -> @@ -472,7 +472,7 @@ Here is an example: ```scala mdoc:compile-only import zio.http._ -HttpApp( +Routes( Method.GET / "static" / trailing -> handler { // Path extractor val pathExtractor: Handler[Any, Nothing, (Path, Request), Path] = @@ -513,7 +513,7 @@ The following example shows how to create an echo server using the `Handler.webS import zio.http._ import zio.http.ChannelEvent._ -HttpApp( +Routes( Method.GET / "websocket" -> handler { Handler.webSocket { channel => @@ -548,7 +548,7 @@ Let's try an example: ```scala mdoc:compile-only import zio.http._ -HttpApp( +Routes( Method.GET / "stacktrace" -> handler { for { @@ -575,7 +575,7 @@ To attach a handler aspect to a handler, we use the `@@` operator. For instance, ```scala mdoc:compile-only import zio.http._ -HttpApp( +Routes( Method.GET / "echo" -> handler { req: Request => Handler.fromBody(req.body) }.flatten @@ HandlerAspect.requestLogging() @@ -602,7 +602,7 @@ Let's see an example: import zio.http._ import java.nio.file._ -HttpApp( +Routes( Method.GET / "file" -> Handler.fromFile(Paths.get("file.txt").toFile).sandbox, ) @@ -612,9 +612,9 @@ In this example, the type of the handler before applying the `sandbox` operator Without the `sandbox` operator, the compiler would complain about the unhandled `Throwable` error. -### Converting a `Handler` to an `HttpApp` +### Converting a `Handler` to an `Routes` -The `Handler#toHttpApp` operator, converts a handler to an `HttpApp` to be served by the `Server`. The following example, shows an HTTP application that serves a simple "Hello, World!" response for all types of incoming requests: +The `Handler#toRoutes` operator, converts a handler to an `Routes` to be served by the `Server`. The following example, shows an HTTP application that serves a simple "Hello, World!" response for all types of incoming requests: ```scala mdoc:compile-only import zio._ @@ -623,7 +623,7 @@ import zio.http._ object HelloWorldServer extends ZIOAppDefault { def run = Server - .serve(Handler.fromResponse(Response.text("Hello, world!")).toHttpApp) + .serve(Handler.fromResponse(Response.text("Hello, world!")).toRoutes) .provide(Server.default) } ``` @@ -714,7 +714,7 @@ The are similar to the `ZIO` ones, but they are specialized for the `Handler` ty The first type parameter of the `Handler` is the environment type. This means that a `Handler` can require an environment to run, like a `ZIO` effect. When we create a `Handler`, we can get access to the environment using `ZIO.service*` methods, and finally, we can provide the environment using `Handler#provide*` methods. :::note -Please note that in most cases, we are not required to provide the environment of the handler in the middle of the routes definition. It is usually done at the end when we are creating the `HttpApp` using the `Server#serve` method. +Please note that in most cases, we are not required to provide the environment of the handler in the middle of the routes definition. It is usually done at the end when we are creating the `Routes` using the `Server#serve` method. ::: :::note diff --git a/docs/reference/handler_aspect.md b/docs/reference/handler_aspect.md index 21e41209cd..bed8cd66bf 100644 --- a/docs/reference/handler_aspect.md +++ b/docs/reference/handler_aspect.md @@ -125,10 +125,10 @@ val statsMiddleware: Middleware[Ref[Map[String, Long]]] = } ``` -After attaching these two handler aspects to our `HttpApp`, we have to provide the initial state for the `Ref[Map[String, Long]]` to the whole application's environment: +After attaching these two handler aspects to our `Routes`, we have to provide the initial state for the `Ref[Map[String, Long]]` to the whole application's environment: ```scala -Server.serve(app @@ counterMiddleware @@ statsMiddleware) +Server.serve(routes @@ counterMiddleware @@ statsMiddleware) .provide( Server.default, ZLayer.fromZIO(Ref.make(Map.empty[String, Long])) diff --git a/docs/reference/headers.md b/docs/reference/headers.md index addfd0980c..ba20c954be 100644 --- a/docs/reference/headers.md +++ b/docs/reference/headers.md @@ -168,7 +168,7 @@ Response( ```scala mdoc import Middleware.addHeader -HttpApp(Method.GET / "hello" -> Handler.ok) @@ addHeader(Header.ContentLength(0L)) +Routes(Method.GET / "hello" -> Handler.ok) @@ addHeader(Header.ContentLength(0L)) ``` ### Reading Headers from Request @@ -176,7 +176,7 @@ HttpApp(Method.GET / "hello" -> Handler.ok) @@ addHeader(Header.ContentLength(0L On the Server-side you can read Request headers as given below: ```scala mdoc -HttpApp( +Routes( Method.GET / "streamOrNot" -> handler { (req: Request) => Response.text(req.headers.map(_.toString).mkString("\n")) } @@ -195,33 +195,32 @@ import zio.stream._ object SimpleResponseDispatcher extends ZIOAppDefault { override def run = - // Starting the server (for more advanced startup configuration checkout `HelloWorldAdvanced`) - Server.serve(app).provide(Server.default) + // Starting the server (for more advanced startup configuration checkout `HelloWorldAdvanced`) + Server.serve(routes).provide(Server.default) // Create a message as a Chunk[Byte] val message = Chunk.fromArray("Hello world !\r\n".getBytes(Charsets.Http)) - // Use `Http.collect` to match on route - val app: HttpApp[Any, Response] = - HttpApp( + val routes: Routes[Any, Response] = + Routes( // Simple (non-stream) based route Method.GET / "health" -> handler(Response.ok), // From Request(req), the headers are accessible. - Method.GET / "streamOrNot" -> - handler { (req: Request) => + Method.GET / "streamOrNot" -> + handler { (req: Request) => // Checking if client is able to handle streaming response val acceptsStreaming: Boolean = req.header(Header.Accept).exists(_.mimeTypes.contains(Header.Accept.MediaTypeWithQFactor(MediaType.application.`octet-stream`, None))) if (acceptsStreaming) Response( status = Status.Ok, body = Body.fromStream(ZStream.fromChunk(message), message.length.toLong), // Encoding content using a ZStream - ) + ) else { // Adding a custom header to Response Response(status = Status.Accepted, body = Body.fromChunk(message)).addHeader("X-MY-HEADER", "test") } } - ).sandbox + ).sandbox } ``` diff --git a/docs/reference/middleware.md b/docs/reference/middleware.md index cd8565c00c..cb7c0b5e0c 100644 --- a/docs/reference/middleware.md +++ b/docs/reference/middleware.md @@ -7,7 +7,7 @@ A middleware helps in addressing common crosscutting concerns without duplicatin ## Definition -Middleware can be conceptualized as a functional component that accepts a `Routes` and produces a new `Routes`. The defined trait, `Middleware`, is parameterized by a contravariant type `UpperEnv` which denotes it can access the environment of the `HttpApp`: +Middleware can be conceptualized as a functional component that accepts a `Routes` and produces a new `Routes`. The defined trait, `Middleware`, is parameterized by a contravariant type `UpperEnv` which denotes it can access the environment of the `Routes`: ```scala trait Middleware[-UpperEnv] { self => @@ -15,16 +15,16 @@ trait Middleware[-UpperEnv] { self => } ``` -This abstraction allows middleware to engage with the `HttpApp` environment, and also the ability to tweak existing routes or add/remove routes as needed. +This abstraction allows middleware to engage with the `Routes` environment, and also the ability to tweak existing routes or add/remove routes as needed. ## Usage -The `@@` operator attaches middleware to routes and HTTP applications. The example below shows a middleware attached to an `HttpApp`: +The `@@` operator attaches middleware to routes and HTTP applications. The example below shows a middleware attached to an `Routes`: ```scala mdoc:compile-only import zio.http._ -val app = HttpApp( +val app = Routes( Method.GET / string("name") -> handler { (name: String, req: Request) => Response.text(s"Hello $name") } @@ -39,23 +39,23 @@ Logically the code above translates to `Middleware.debug(app)`, which transforms We can attach multiple middlewares by chaining them using more `@@` operators: ```scala -val resultApp = httpApp @@ f1 @@ f2 @@ f3 +val resultApp = routes @@ f1 @@ f2 @@ f3 ``` -In the above code, when a new request comes in, it will first go through the `f3`'s incoming handler, then `f2`, then `f1`, and finally the `httpApp`, when the response is going out, it will go through the `f1`'s outgoing handler, then `f2`, then `f3`, and finally will be sent out. So **the order of the middlewares matters** and if we change the order of the middlewares, the application's behavior may change. +In the above code, when a new request comes in, it will first go through the `f3`'s incoming handler, then `f2`, then `f1`, and finally the `routes`, when the response is going out, it will go through the `f1`'s outgoing handler, then `f2`, then `f3`, and finally will be sent out. So **the order of the middlewares matters** and if we change the order of the middlewares, the application's behavior may change. ## Composing Middlewares Middleware can be combined using the `++` operator: ```scala -httpApp @@ (f1 ++ f2 ++ f3) +routes @@ (f1 ++ f2 ++ f3) ``` The `f1 ++ f2 ++ f3` applies from left to right with `f1` first followed by others, like this: ```scala -f3(f2(f1(httpApp))) +f3(f2(f1(routes))) ``` ## Motivation @@ -70,7 +70,7 @@ Consider the following example where we have two endpoints: * **GET /users** - Get all users ```scala -val routes = HttpApp( +val routes = Routes( Method.GET / "users" / int("id") -> handler { (id: Int, req: Request) => // core business logic @@ -152,7 +152,7 @@ val composedMiddlewares = Middleware.basicAuth("user","pw") ++ And then we can attach our composed bundle of middlewares to an Http using `@@` ```scala - val routes = HttpApp( + val routes = Routes( Method.GET / "users" / int("id") -> handler { (id: Int, req: Request) => // core business logic @@ -267,7 +267,7 @@ import utils._ printSource("zio-http-example/src/main/scala/example/HelloWorldWithMetrics.scala") ``` -Another important thing to note is that the `metrics` middleware only attaches to the `HttpApp` or `Routes`, so if we want to track some custom metrics particular to a handler, we can use the `ZIO#@@` operator to attach a metric of type `ZIOAspect` to the ZIO effect that is returned by the handler. For example, if we want to track the number of requests that have a custom header `X-Custom-Header` in the `/json` route, we can attach a counter metric to the ZIO effect that is returned by the handler using the `@@` operator. +Another important thing to note is that the `metrics` middleware only attaches to the `Routes` or `Routes`, so if we want to track some custom metrics particular to a handler, we can use the `ZIO#@@` operator to attach a metric of type `ZIOAspect` to the ZIO effect that is returned by the handler. For example, if we want to track the number of requests that have a custom header `X-Custom-Header` in the `/json` route, we can attach a counter metric to the ZIO effect that is returned by the handler using the `@@` operator. ### Timeout Middleware @@ -275,11 +275,11 @@ The `Middleware.timeout` middleware is used to set a timeout for the HTTP reques ```scala mdoc:invisible import zio.http._ -val httpApp: HttpApp[Any, Response] = Handler.ok.toHttpApp +val routes: Routes[Any, Response] = Handler.ok.toRoutes ``` ```scala mdoc:compile-only -httpApp @@ Middleware.timeout(5.seconds) +routes @@ Middleware.timeout(5.seconds) ``` ### Log Annotation Middleware @@ -312,7 +312,7 @@ val correlationId = To see the correlation ID in the logs, we need to place the middleware after the request logging middleware: ```scala mdoc:silent -httpApp @@ Middleware.requestLogging() @@ correlationId +routes @@ Middleware.requestLogging() @@ correlationId ``` Now, if we call one of the routes with the `X-Correlation-ID` header, we should see the correlation ID in the logs: diff --git a/docs/reference/path_codec.md b/docs/reference/path_codec.md index ad5d1e8bb9..d0521d1549 100644 --- a/docs/reference/path_codec.md +++ b/docs/reference/path_codec.md @@ -174,8 +174,8 @@ object Main extends ZIOAppDefault { val userId: PathCodec[UserId] = int("user-id").transformOrFailLeft(UserId.apply)(_.value) - val httpApp: HttpApp[Any, Response] = - HttpApp( + val routes: Routes[Any, Response] = + Routes( Method.GET / "users" / userId -> Handler.fromFunctionHandler[(UserId, Request)] { case (userId: UserId, request: Request) => Handler.text(userId.value.toString) @@ -190,7 +190,7 @@ object Main extends ZIOAppDefault { } } - def run = Server.serve(httpApp).provide(Server.default) + def run = Server.serve(routes).provide(Server.default) } ``` @@ -213,15 +213,15 @@ object TrailingExample extends ZIOAppDefault { Handler.notFound } yield http - val app = - HttpApp( + val routes = + Routes( Method.GET / "static" / trailing -> Handler.fromFunctionHandler[(Path, Request)] { case (path: Path, _: Request) => staticFileHandler(path).contramap[(Path, Request)](_._2) }, - ).sandbox @@ HandlerAspect.requestLogging() + ).sandbox @@ HandlerAspect.requestLogging() - val run = Server.serve(app).provide(Server.default) + val run = Server.serve(routes).provide(Server.default) } ``` diff --git a/docs/reference/request.md b/docs/reference/request.md index 71d727ff2b..ded9b0e066 100644 --- a/docs/reference/request.md +++ b/docs/reference/request.md @@ -13,7 +13,7 @@ To access the incoming request, we can use a `Handler` which takes a `Request` a import zio._ import zio.http._ -HttpApp( +Routes( Method.POST / "echo" -> handler { (req: Request) => req.body.asString(Charsets.Utf8).map(Response.text(_)).sandbox @@ -148,7 +148,7 @@ import zio.http._ object QueryParamExample extends ZIOAppDefault { val app = - HttpApp( + Routes( Method.GET / "search" -> handler { (req: Request) => val queries = req.queryParam("q") queries match { @@ -171,7 +171,7 @@ The typed version of `Request#queryParam` is `Request#queryParamTo` which takes import zio.http._ object TypedQueryParamExample extends ZIOAppDefault { val app = - HttpApp( + Routes( Method.GET / "search" -> Handler.fromFunctionHandler { (req: Request) => val response: ZIO[Any, QueryParamsError, Response] = ZIO.fromEither(req.queryParamTo[Int]("age")) @@ -204,7 +204,7 @@ import zio.http._ object QueryParamsExample extends ZIOAppDefault { val app = - HttpApp( + Routes( Method.GET / "search" -> handler { (req: Request) => val queries = req.queryParams("q") if (queries.nonEmpty) { diff --git a/docs/reference/response.md b/docs/reference/response.md index 4282cc2d8c..d86a3e3608 100644 --- a/docs/reference/response.md +++ b/docs/reference/response.md @@ -19,15 +19,15 @@ import zio._ import zio.http._ object HelloWorldExample extends ZIOAppDefault { - val app: HttpApp[Any, Response] = - HttpApp( + val routes: Routes[Any, Response] = + Routes( Method.GET / "text" -> handler { Response.text("Hello World!") }, - ) + ) - override val run = Server.serve(app).provide(Server.default) + override val run = Server.serve(routes).provide(Server.default) } ``` @@ -246,7 +246,7 @@ object ServerSentExample extends ZIOAppDefault { ) val app = - HttpApp( + Routes( Method.GET / "events" -> handler { Response.fromServerSentEvents(stream) }, @@ -295,8 +295,8 @@ import zio.http._ object WebsocketExample extends ZIOAppDefault { - val app: HttpApp[Any, Response] = { - HttpApp( + val routes: Routes[Any, Response] = { + Routes( Method.GET / "echo" -> handler { Response.fromSocketApp( WebSocketApp( @@ -304,18 +304,18 @@ object WebsocketExample extends ZIOAppDefault { channel.receiveAll { case ChannelEvent.Read(message) => channel.send(ChannelEvent.read(message)) - case other => + case other => ZIO.debug(other) } }, - ), - ) + ), + ) }, - ) + ) } def run = - Server.serve(app).provide(Server.default) + Server.serve(routes).provide(Server.default) } ``` diff --git a/docs/reference/routes.md b/docs/reference/routes.md index e5f6a850d9..bc74461a6f 100644 --- a/docs/reference/routes.md +++ b/docs/reference/routes.md @@ -10,7 +10,7 @@ Let's see an example of a simple `Routes` that has two routes: ```scala mdoc:compile-only import zio.http._ -HttpApp( +Routes( Method.GET / "hello" -> Handler.text("hello"), Method.GET / "health-check" -> Handler.ok, ) @@ -23,7 +23,7 @@ To build empty routes we have `Routes.empty` constructor: ```scala mdoc:silent import zio.http._ -val routes1 = HttpApp.empty +val routes1 = Routes.empty ``` We can build routes with the `Routes.apply` constructor, which takes varargs of individual `Route` values: @@ -40,7 +40,7 @@ object Routes { Example: ```scala mdoc:compile-only -HttpApp( +Routes( Method.GET / "hello" -> Handler.text("hello"), Method.GET / "health-check" -> Handler.ok, Method.POST / "echo" -> @@ -61,7 +61,7 @@ Using the `/` operator of `Method`, we can construct route patterns, which can t ```scala mdoc:silent val routes2 = - HttpApp( + Routes( Method.GET / "hello" -> Handler.ok, Method.GET / "goodbye" -> Handler.ok ) @@ -83,17 +83,17 @@ import zio.http.codec.PathCodec._ val routes = literal("nest1") / - HttpApp.fromIterable( + Routes.fromIterable( Chunk( Method.GET / "foo" -> Handler.text("foo"), Method.GET / "bar" -> Handler.text("bar"), ) ++ Chunk( - literal("nest2") / HttpApp( + literal("nest2") / Routes( Method.GET / "baz" -> Handler.text("baz"), Method.GET / "qux" -> Handler.text("qux"), ), - literal("nest2") / HttpApp( + literal("nest2") / Routes( Method.GET / "quux" -> Handler.text("quux"), Method.GET / "corge" -> Handler.text("corge"), ), @@ -195,34 +195,18 @@ trait Routes[-Env, +Err] { } ``` -## Converting `Routes` to `HttpApp` - -`HttpApp[-R]` represents a fully-specified HTTP application that can be executed by the server. - -When we are done building a collection of routes, our next step is typically to convert these routes into an HTTP application using the `Routes#toHttpApp` method, which we can then execute with the server. - -Routes may have handled or unhandled errors. If the error type of `Routes[Env, Err]` is equal to or a subtype of `Response`, we call this a route where all errors are handled. Otherwise, it's a route where some errors are unhandled. - -For instance, a route of type `Route[Env, Throwable]` has not handled its errors by converting them into responses. Consequently, such unfinished routes cannot be converted into HTTP applications. We must first handle errors using the `handleError` or `handleErrorCause` methods. - -By handling our errors, we ensure that clients interacting with our API will not encounter strange or unexpected responses, but will always be able to interact effectively with our web service, even in exceptional cases. - -:::note -If we aim to automatically convert our failures into suitable responses, without revealing details about the specific nature of the errors, we can utilize `Routes#sandbox`. After addressing our errors in this manner, we can proceed to convert our routes into an HTTP application. -::: - ## Running an App -ZIO HTTP server needs an `HttpApp[R]` for running. We can use `Server.serve()` method to bootstrap the server with -an `HttpApp[R]`: +ZIO HTTP server needs `Routes[Env, Response]` for running, so routes that have a `Response` as the error type. +We can use `Server.serve()` method to bootstrap the server with an instance of `Routes[Env, Response]`.: ```scala mdoc:compile-only import zio._ import zio.http._ object HelloWorld extends ZIOAppDefault { - val app: HttpApp[Any, Response] = Handler.ok.toHttpApp + val routes: Routes[Any, Response] = Handler.ok.toRoutes - override def run = Server.serve(app).provide(Server.default) + override def run = Server.serve(routes).provide(Server.default) } ``` diff --git a/docs/reference/server.md b/docs/reference/server.md index f8724e1ae2..80efe5ec2d 100644 --- a/docs/reference/server.md +++ b/docs/reference/server.md @@ -9,35 +9,35 @@ This section describes, ZIO HTTP Server and different configurations you can pro ## Starting a Server with Default Configurations -Assuming we have written an `HttpApp`: +Assuming we have written a `Routes`: ```scala mdoc:silent import zio.http._ import zio._ -def app: HttpApp[Any, Response] = - HttpApp( - Method.GET / "hello" -> +val routes: Routes[Any, Response] = + Routes( + Method.GET / "hello" -> handler(Response.text("Hello, World!")) - ) + ) ``` We can serve it using the `Server.serve` method: ```scala mdoc:silent -Server.serve(app).provide(Server.default) +Server.serve(routes).provide(Server.default) ``` By default, it will start the server on port `8080`. A quick shortcut to only customize the port is `Server.defaultWithPort`: ```scala mdoc:compile-only -Server.serve(app).provide(Server.defaultWithPort(8081)) +Server.serve(routes).provide(Server.defaultWithPort(8081)) ``` Or to customize more properties of the _default configuration_: ```scala mdoc:compile-only -Server.serve(app).provide( +Server.serve(routes).provide( Server.defaultWith( _.port(8081).enableRequestStreaming ) @@ -45,7 +45,7 @@ Server.serve(app).provide( ``` :::note -Sometimes we may want to have more control over installation of the http application into the server. In such cases, we may want to use the `Server.install` method. This method only installs the `HttpApp` into the server, and the lifecycle of the server can be managed separately. +Sometimes we may want to have more control over installation of the http application into the server. In such cases, we may want to use the `Server.install` method. This method only installs the `Routes` into the server, and the lifecycle of the server can be managed separately. ::: ## Starting a Server with Custom Configurations @@ -54,7 +54,7 @@ The `live` layer expects a `Server.Config` holding the custom configuration for ```scala mdoc:compile-only Server - .serve(app) + .serve(routes) .provide( ZLayer.succeed(Server.Config.default.port(8081)), Server.live @@ -159,10 +159,10 @@ import zio._ import zio.http._ object KeepAliveExample extends ZIOAppDefault { - val httpApp = handler(Response.text("Hello World!")).toHttpApp + val routes = handler(Response.text("Hello World!")).toRoutes override val run = - Server.serve(httpApp).provide(Server.default) + Server.serve(routes).provide(Server.default) } ``` @@ -175,7 +175,7 @@ content-type: text/plain content-length: 12 connection: close -Hello World!⏎ +Hello World! ``` However, with the `Connection: Keep-Alive` header, the client can request that the connection remain open after the initial request, allowing for subsequent requests to be sent over the same connection without needing to establish a new one each time. @@ -224,7 +224,7 @@ The `configured` layer loads the server configuration using the application's _Z ```scala mdoc:compile-only Server - .serve(app) + .serve(routes) .provide( Server.configured() ) @@ -233,9 +233,9 @@ Server For example, to load the server configuration from the hocon file, we should add the `zio-config-typesafe` dependency to our `build.sbt` file: ```scala -libraryDependencies += "dev.zio" %% "zio-config" % "", -libraryDependencies += "dev.zio" %% "zio-config-magnolia" % "", -libraryDependencies += "dev.zio" %% "zio-config-typesafe" % "", +libraryDependencies += "dev.zio" %% "zio-config" % "" +libraryDependencies += "dev.zio" %% "zio-config-magnolia" % "" +libraryDependencies += "dev.zio" %% "zio-config-typesafe" % "" ``` And put the `application.conf` file in the `src/main/resources` directory: @@ -278,7 +278,7 @@ object EchoServerWithDecompression extends ZIOAppDefault { .serve( handler { (req: Request) => req.body.asString.map(Response.text) - }.sandbox.toHttpApp, + }.sandbox.toRoutes, ) .provide(Server.live, ZLayer.succeed(Server.Config.default.requestDecompression(true))) } @@ -405,8 +405,8 @@ import zio.stream.{ZSink, ZStream} object RequestStreamingServerExample extends ZIOAppDefault { def logBytes = (b: Byte) => ZIO.log(s"received byte: $b") - private val app: HttpApp[Any, Response] = - HttpApp( + private val routes: Routes[Any, Response] = + Routes( Method.POST / "upload-stream" / "simple" -> handler { (req: Request) => for { count <- req.body.asStream.tap(logBytes).run(ZSink.count) @@ -442,7 +442,7 @@ object RequestStreamingServerExample extends ZIOAppDefault { override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = Server - .serve(app) + .serve(routes) .provide( ZLayer.succeed(Server.Config.default.enableRequestStreaming), Server.live, diff --git a/docs/reference/socket/socket.md b/docs/reference/socket/socket.md index c6c3bdd937..1685da9805 100644 --- a/docs/reference/socket/socket.md +++ b/docs/reference/socket/socket.md @@ -18,7 +18,7 @@ val socket = Handler.webSocket { channel => } } -val http = HttpApp( +val http = Routes( Method.GET / "subscriptions" -> handler(socket.toResponse) ) ``` diff --git a/zio-http-benchmarks/src/main/scala-2.13/zio/http/benchmarks/EndpointBenchmark.scala b/zio-http-benchmarks/src/main/scala-2.13/zio/http/benchmarks/EndpointBenchmark.scala index 9155cdf095..21f76759e8 100644 --- a/zio-http-benchmarks/src/main/scala-2.13/zio/http/benchmarks/EndpointBenchmark.scala +++ b/zio-http-benchmarks/src/main/scala-2.13/zio/http/benchmarks/EndpointBenchmark.scala @@ -101,10 +101,10 @@ class EndpointBenchmark { } } - val apiHttpApp = handledUsersPosts.toHttpApp + val apiRoutes = handledUsersPosts.toRoutes // Collect DSL - val collectHttpApp = HttpApp( + val collectdRoutes = Routes( Method.GET / "users" / int("userId") / "posts" / int("postId") -> handler { (userIdInt: Int, postIdInt: Int, req: Request) => val query = req.url.queryParam("query").get @@ -171,13 +171,13 @@ class EndpointBenchmark { @Benchmark def benchmarkSmallDataZioApi(): Unit = unsafeRun { - apiHttpApp.runZIO(smallDataRequest).repeatN(REPEAT_N) + apiRoutes.runZIO(smallDataRequest).repeatN(REPEAT_N) } @Benchmark def benchmarkSmallDataZioCollect(): Unit = unsafeRun { - collectHttpApp.runZIO(smallDataRequest).repeatN(REPEAT_N) + collectdRoutes.runZIO(smallDataRequest).repeatN(REPEAT_N) } // @Benchmark @@ -208,7 +208,7 @@ class EndpointBenchmark { // API DSL - val deepPathHttpApp = Endpoint( + val deepPathRoutes = Endpoint( Method.GET / "first" / int("id1") / "second" / int("id2") / "third" / int( @@ -219,11 +219,11 @@ class EndpointBenchmark { ) .out[Unit] .implement(Handler.unit) - .toHttpApp + .toRoutes // Collect DSL - val deepPathCollectHttpApp = HttpApp( + val deepPathCollectHttpApp = Routes( Method.GET / "first" / int("id1") / "second" / int("id2") / "third" / int("id3") / "fourth" / int( "id4", ) / "fifth" / int("id5") / "sixth" / int("id6") / "seventh" / int("id7") -> @@ -281,7 +281,7 @@ class EndpointBenchmark { @Benchmark def benchmarkDeepPathZioApi(): Unit = unsafeRun { - deepPathHttpApp.runZIO(deepPathRequest).repeatN(REPEAT_N) + deepPathRoutes.runZIO(deepPathRequest).repeatN(REPEAT_N) } @Benchmark @@ -418,7 +418,7 @@ class EndpointBenchmark { .implement(Handler.unit) val broadApiApp = - HttpApp( + Routes( broadUsers, broadUsersId, boardUsersPosts, @@ -439,56 +439,53 @@ class EndpointBenchmark { // Collect DSL - val broadCollectApp = HttpApp( + val broadCollectApp = Routes( Method.GET / "users" / int("userId") / "posts" / int("postId") / "comments" / int("commentId") -> handler { - (userId: Int, postId: Int, commentId: Int, request: Request) => + (_: Int, _: Int, _: Int, _: Request) => Response() }, Method.GET / "users" / int("userId") / "posts" / int("postId") / "comments" -> handler { - (userId: Int, postId: Int, req: Request) => + (_: Int, _: Int, _: Request) => Response() }, - Method.GET / "users" / int("userId") / "posts" / int("postId") -> handler { - (userId: Int, postId: Int, req: Request) => - Response() + Method.GET / "users" / int("userId") / "posts" / int("postId") -> handler { (_: Int, _: Int, _: Request) => + Response() }, - Method.GET / "users" / int("userId") / "posts" -> handler { (userId: Int, req: Request) => + Method.GET / "users" / int("userId") / "posts" -> handler { (_: Int, _: Request) => Response() }, - Method.GET / "users" / int("userId") -> handler { (userId: Int, req: Request) => + Method.GET / "users" / int("userId") -> handler { (_: Int, _: Request) => Response() }, Method.GET / "users" -> handler(Response()), - Method.GET / "posts" / int("postId") / "comments" / int("commentId") -> handler { - (postId: Int, commentId: Int, req: Request) => - Response() + Method.GET / "posts" / int("postId") / "comments" / int("commentId") -> handler { (_: Int, _: Int, _: Request) => + Response() }, - Method.GET / "posts" / int("postId") / "comments" -> handler { (postId: Int, req: Request) => + Method.GET / "posts" / int("postId") / "comments" -> handler { (_: Int, _: Request) => Response() }, - Method.GET / "posts" / int("postId") -> handler { (postId: Int, req: Request) => + Method.GET / "posts" / int("postId") -> handler { (_: Int, _: Request) => Response() }, Method.GET / "posts" -> handler(Response()), - Method.GET / "comments" / int("commentId") -> handler { (commentId: Int, req: Request) => + Method.GET / "comments" / int("commentId") -> handler { (_: Int, _: Request) => Response() }, Method.GET / "comments" -> handler(Response()), - Method.GET / "users" / int("userId") / "comments" -> handler { (userId: Int, req: Request) => + Method.GET / "users" / int("userId") / "comments" -> handler { (_: Int, _: Request) => Response() }, - Method.GET / "users" / int("userId") / "comments" / int("commentId") -> handler { - (userId: Int, commentId: Int, req: Request) => - Response() + Method.GET / "users" / int("userId") / "comments" / int("commentId") -> handler { (_: Int, _: Int, _: Request) => + Response() }, Method.GET / "users" / int("userId") / "posts" / int("postId") / "comments" / int("commentId") / "replies" / int( "replyId", - ) -> handler { (userId: Int, postId: Int, commentId: Int, replyId: Int, req: Request) => + ) -> handler { (_: Int, _: Int, _: Int, _: Int, _: Request) => Response() }, Method.GET / "users" / int("userId") / "posts" / int("postId") / "comments" / int( "commentId", - ) / "replies" -> handler { (userId: Int, postId: Int, commentId: Int, req: Request) => + ) / "replies" -> handler { (_: Int, _: Int, _: Int, _: Request) => Response() }, ) diff --git a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCollectEval.scala b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCollectEval.scala index 5fe7e8362f..ceb15bcf25 100644 --- a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCollectEval.scala +++ b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCollectEval.scala @@ -15,8 +15,8 @@ class HttpCollectEval { private val MAX = 10000 private val req = Request() private val res = Response.ok - private val app = HttpApp.singleton(handler(res)) - private val http = HttpApp(Route.route(Method.ANY / "text")(handler(res))) + private val app = Routes.singleton(handler(res)) + private val http = Routes(Route.route(Method.ANY / "text")(handler(res))) private val base: PartialFunction[Int, Int] = { case 0 => 1 } private val baseTotal: Int => Int = _ => 1 diff --git a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCombineEval.scala b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCombineEval.scala index e65989a71d..884eebca4d 100644 --- a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCombineEval.scala +++ b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/HttpCombineEval.scala @@ -15,7 +15,7 @@ class HttpCombineEval { private val req = Request.get("/foo") private val res = Response.ok private val MAX = 1000 - private val app = HttpApp(Method.GET / "" -> handler(res)) + private val app = Routes(Method.GET / "" -> handler(res)) private val spec = (0 to MAX).foldLeft(app)((a, _) => a ++ app) @Benchmark diff --git a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ServerInboundHandlerBenchmark.scala b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ServerInboundHandlerBenchmark.scala index 247b9171e4..9e54956dc5 100644 --- a/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ServerInboundHandlerBenchmark.scala +++ b/zio-http-benchmarks/src/main/scala/zhttp.benchmarks/ServerInboundHandlerBenchmark.scala @@ -59,7 +59,7 @@ class ServerInboundHandlerBenchmark { private def shutdownRoute(shutdownSignal: Promise[Nothing, Unit]) = Route.route(Method.GET / shutdownEndpoint)(handler(shutdownSignal.succeed(()).as(shutdownResponse))) private def http(shutdownSignal: Promise[Nothing, Unit]) = - HttpApp(testRoute, arrayRoute, chunkRoute, shutdownRoute(shutdownSignal)) + Routes(testRoute, arrayRoute, chunkRoute, shutdownRoute(shutdownSignal)) @Setup(Level.Trial) def setup(): Unit = { diff --git a/zio-http-cli/src/test/scala/zio/http/endpoint/cli/CliSpec.scala b/zio-http-cli/src/test/scala/zio/http/endpoint/cli/CliSpec.scala index ba86dd84f9..2a3418ff94 100644 --- a/zio-http-cli/src/test/scala/zio/http/endpoint/cli/CliSpec.scala +++ b/zio-http-cli/src/test/scala/zio/http/endpoint/cli/CliSpec.scala @@ -46,11 +46,11 @@ object CliSpec extends ZIOSpecDefault { val testClient: ZLayer[Any, Nothing, TestClient & Client] = ZLayer.scopedEnvironment { for { - behavior <- Ref.make[HttpApp[Any, Response]](HttpApp.empty) + behavior <- Ref.make[Routes[Any, Response]](Routes.empty) socketBehavior <- Ref.make[WebSocketApp[Any]](WebSocketApp(Handler.unit)) driver = TestClient(behavior, socketBehavior) - _ <- driver.addHttpApp { - HttpApp( + _ <- driver.addRoutes { + Routes( Method.GET / "fromURL" -> handler(Response.text("342.76")), Method.GET / trailing -> handler { (_: Path, request: Request) => val headers = request.headers diff --git a/zio-http-example/src/main/scala/example/AuthenticationServer.scala b/zio-http-example/src/main/scala/example/AuthenticationServer.scala index 2958dc2871..7bf79206b5 100644 --- a/zio-http-example/src/main/scala/example/AuthenticationServer.scala +++ b/zio-http-example/src/main/scala/example/AuthenticationServer.scala @@ -43,8 +43,8 @@ object AuthenticationServer extends ZIOAppDefault { } }) - def app: HttpApp[Any, Response] = - HttpApp( + def app: Routes[Any, Response] = + Routes( // A route that is accessible only via a jwt token Method.GET / "profile" / "me" -> handler { (_: Request) => ZIO.serviceWith[String](name => Response.text(s"Welcome $name!")) diff --git a/zio-http-example/src/main/scala/example/BasicAuth.scala b/zio-http-example/src/main/scala/example/BasicAuth.scala index 632feba915..3f3bc9f4ad 100644 --- a/zio-http-example/src/main/scala/example/BasicAuth.scala +++ b/zio-http-example/src/main/scala/example/BasicAuth.scala @@ -9,7 +9,7 @@ import zio.http.codec.PathCodec.string object BasicAuth extends ZIOAppDefault { // Http app that requires a JWT claim - val user: HttpApp[Any, Response] = HttpApp( + val user: Routes[Any, Response] = Routes( Method.GET / "user" / string("name") / "greet" -> handler { (name: String, _: Request) => Response.text(s"Welcome to the ZIO party! ${name}") @@ -17,7 +17,7 @@ object BasicAuth extends ZIOAppDefault { ) // Composing all the HttpApps together - val app: HttpApp[Any, Response] = user @@ basicAuth("admin", "admin") + val app: Routes[Any, Response] = user @@ basicAuth("admin", "admin") // Run it like any simple app val run = Server.serve(app).provide(Server.default) diff --git a/zio-http-example/src/main/scala/example/ClientServer.scala b/zio-http-example/src/main/scala/example/ClientServer.scala index 9a1f9e9eba..c38ada0635 100644 --- a/zio-http-example/src/main/scala/example/ClientServer.scala +++ b/zio-http-example/src/main/scala/example/ClientServer.scala @@ -7,7 +7,7 @@ import zio.http._ object ClientServer extends ZIOAppDefault { val url = URL.decode("http://localhost:8080/hello").toOption.get - val app = HttpApp( + val app = Routes( Method.GET / "hello" -> handler(Response.text("hello")), Method.GET / "" -> handler(ZClient.request(Request.get(url))), ).sandbox diff --git a/zio-http-example/src/main/scala/example/ConcreteEntity.scala b/zio-http-example/src/main/scala/example/ConcreteEntity.scala index 4c7b3edfac..5e2aab6303 100644 --- a/zio-http-example/src/main/scala/example/ConcreteEntity.scala +++ b/zio-http-example/src/main/scala/example/ConcreteEntity.scala @@ -19,11 +19,11 @@ object ConcreteEntity extends ZIOAppDefault { UserCreated(2) } - val app: HttpApp[Any, Response] = + val app: Routes[Any, Response] = user .contramap[Request](req => CreateUser(req.path.encode)) // Http[Any, Nothing, Request, UserCreated] .map(userCreated => Response.text(userCreated.id.toString)) // Http[Any, Nothing, Request, Response] - .toHttpApp + .toRoutes // Run it like any simple app val run = diff --git a/zio-http-example/src/main/scala/example/CookieServerSide.scala b/zio-http-example/src/main/scala/example/CookieServerSide.scala index a596422b6e..0edbddc484 100644 --- a/zio-http-example/src/main/scala/example/CookieServerSide.scala +++ b/zio-http-example/src/main/scala/example/CookieServerSide.scala @@ -13,7 +13,7 @@ object CookieServerSide extends ZIOAppDefault { private val cookie = Cookie.Response("key", "value", maxAge = Some(5 days)) val res = Response.ok.addCookie(cookie) - private val app = HttpApp( + private val app = Routes( Method.GET / "cookie" -> handler(Response.ok.addCookie(cookie.copy(path = Some(Path.root / "cookie"), isHttpOnly = true))), Method.GET / "secure-cookie" -> diff --git a/zio-http-example/src/main/scala/example/EndpointExamples.scala b/zio-http-example/src/main/scala/example/EndpointExamples.scala index ba226bd67d..490cc99bf0 100644 --- a/zio-http-example/src/main/scala/example/EndpointExamples.scala +++ b/zio-http-example/src/main/scala/example/EndpointExamples.scala @@ -39,7 +39,7 @@ object EndpointExamples extends ZIOAppDefault { val openAPI = OpenAPIGen.fromEndpoints(title = "Endpoint Example", version = "1.0", getUser, getUserPosts) - val routes = HttpApp(getUserRoute, getUserPostsRoute) ++ SwaggerUI.app("docs" / "openapi", openAPI) + val routes = Routes(getUserRoute, getUserPostsRoute) ++ SwaggerUI.routes("docs" / "openapi", openAPI) val app = routes // (auth.implement(_ => ZIO.unit)(_ => ZIO.unit)) diff --git a/zio-http-example/src/main/scala/example/FileStreaming.scala b/zio-http-example/src/main/scala/example/FileStreaming.scala index b4aebc57bb..9226ae402a 100644 --- a/zio-http-example/src/main/scala/example/FileStreaming.scala +++ b/zio-http-example/src/main/scala/example/FileStreaming.scala @@ -12,7 +12,7 @@ import zio.http._ object FileStreaming extends ZIOAppDefault { // Create HTTP route - val app = HttpApp( + val app = Routes( Method.GET / "health" -> Handler.ok, // Read the file as ZStream diff --git a/zio-http-example/src/main/scala/example/GracefulShutdown.scala b/zio-http-example/src/main/scala/example/GracefulShutdown.scala index a949c9b90d..d473f89c98 100644 --- a/zio-http-example/src/main/scala/example/GracefulShutdown.scala +++ b/zio-http-example/src/main/scala/example/GracefulShutdown.scala @@ -22,12 +22,12 @@ import zio.http._ object GracefulShutdown extends ZIOAppDefault { - val app: HttpApp[Any, Response] = Handler + val app: Routes[Any, Response] = Handler .fromFunctionZIO[Request] { _ => ZIO.sleep(10.seconds).debug("request handler delay done").as(Response.text("done")) } .sandbox - .toHttpApp + .toRoutes override def run: ZIO[Any, Throwable, Unit] = (for { diff --git a/zio-http-example/src/main/scala/example/HelloWorld.scala b/zio-http-example/src/main/scala/example/HelloWorld.scala index 43904881c1..ac51eb00e0 100644 --- a/zio-http-example/src/main/scala/example/HelloWorld.scala +++ b/zio-http-example/src/main/scala/example/HelloWorld.scala @@ -12,7 +12,7 @@ object HelloWorld extends ZIOAppDefault { Method.GET / "json" -> handler(Response.json("""{"greetings": "Hello World!"}""")) // Create HTTP route - val app = HttpApp(textRoute, jsonRoute) + val app = Routes(textRoute, jsonRoute) // Run it like any simple app override val run = Server.serve(app).provide(Server.default) diff --git a/zio-http-example/src/main/scala/example/HelloWorldAdvanced.scala b/zio-http-example/src/main/scala/example/HelloWorldAdvanced.scala index 325cf715be..c033117c13 100644 --- a/zio-http-example/src/main/scala/example/HelloWorldAdvanced.scala +++ b/zio-http-example/src/main/scala/example/HelloWorldAdvanced.scala @@ -13,12 +13,12 @@ object HelloWorldAdvanced extends ZIOAppDefault { val PORT = 0 val fooBar = - HttpApp( + Routes( Method.GET / "foo" -> Handler.from(Response.text("bar")), Method.GET / "bar" -> Handler.from(Response.text("foo")), ) - val app = HttpApp( + val app = Routes( Method.GET / "random" -> handler(Random.nextString(10).map(Response.text(_))), Method.GET / "utc" -> handler(Clock.currentDateTime.map(s => Response.text(s.toString))), ) diff --git a/zio-http-example/src/main/scala/example/HelloWorldWithCORS.scala b/zio-http-example/src/main/scala/example/HelloWorldWithCORS.scala index d0e88f0e1f..87fa39a258 100644 --- a/zio-http-example/src/main/scala/example/HelloWorldWithCORS.scala +++ b/zio-http-example/src/main/scala/example/HelloWorldWithCORS.scala @@ -18,13 +18,13 @@ object HelloWorldWithCORS extends ZIOAppDefault { }, ) - val backend: HttpApp[Any, Response] = - HttpApp( + val backend: Routes[Any, Response] = + Routes( Method.GET / "json" -> handler(Response.json("""{"message": "Hello World!"}""")), ) @@ cors(config) - val frontend: HttpApp[Any, Response] = - HttpApp( + val frontend: Routes[Any, Response] = + Routes( Method.GET / PathCodec.empty -> handler( Response.html( html( diff --git a/zio-http-example/src/main/scala/example/HelloWorldWithMetrics.scala b/zio-http-example/src/main/scala/example/HelloWorldWithMetrics.scala index 834222fa67..0ba824f884 100644 --- a/zio-http-example/src/main/scala/example/HelloWorldWithMetrics.scala +++ b/zio-http-example/src/main/scala/example/HelloWorldWithMetrics.scala @@ -9,8 +9,8 @@ import zio.http._ object HelloWorldWithMetrics extends ZIOAppDefault { - val backend: HttpApp[Any, Response] = - HttpApp( + val backend: Routes[Any, Response] = + Routes( Method.GET / "json" -> handler((req: Request) => ZIO.succeed(Response.json("""{"message": "Hello World!"}""")) @@ Metric .counter("x_custom_header_total") @@ -19,8 +19,8 @@ object HelloWorldWithMetrics extends ZIOAppDefault { Method.GET / "forbidden" -> handler(ZIO.succeed(Response.forbidden)), ) @@ Middleware.metrics() - val metrics: HttpApp[PrometheusPublisher, Response] = - HttpApp( + val metrics: Routes[PrometheusPublisher, Response] = + Routes( Method.GET / "metrics" -> handler(ZIO.serviceWithZIO[PrometheusPublisher](_.get.map(Response.text))), ) diff --git a/zio-http-example/src/main/scala/example/HelloWorldWithMiddlewares.scala b/zio-http-example/src/main/scala/example/HelloWorldWithMiddlewares.scala index 4d01d2cf7c..b3054adadc 100644 --- a/zio-http-example/src/main/scala/example/HelloWorldWithMiddlewares.scala +++ b/zio-http-example/src/main/scala/example/HelloWorldWithMiddlewares.scala @@ -8,12 +8,12 @@ import zio.http._ object HelloWorldWithMiddlewares extends ZIOAppDefault { - val app: HttpApp[Any, Response] = Routes( + val app: Routes[Any, Response] = Routes( // this will return result instantly Method.GET / "text" -> handler(ZIO.succeed(Response.text("Hello World!"))), // this will return result after 5 seconds, so with 3 seconds timeout it will fail Method.GET / "long-running" -> handler(ZIO.succeed(Response.text("Hello World!")).delay(5 seconds)), - ).toHttpApp + ) val serverTime = Middleware.patchZIO(_ => for { diff --git a/zio-http-example/src/main/scala/example/HtmlTemplating.scala b/zio-http-example/src/main/scala/example/HtmlTemplating.scala index 216d12642c..5a2273bd98 100644 --- a/zio-http-example/src/main/scala/example/HtmlTemplating.scala +++ b/zio-http-example/src/main/scala/example/HtmlTemplating.scala @@ -8,7 +8,7 @@ object HtmlTemplating extends ZIOAppDefault { // Importing everything from `zio.html` import zio.http.template._ - def app: HttpApp[Any, Response] = { + def app: Routes[Any, Response] = { // Html response takes in a `Html` instance. Handler.html { @@ -45,7 +45,7 @@ object HtmlTemplating extends ZIOAppDefault { ), ) } - }.toHttpApp + }.toRoutes def run = Server.serve(app).provide(Server.default) } diff --git a/zio-http-example/src/main/scala/example/HttpsHelloWorld.scala b/zio-http-example/src/main/scala/example/HttpsHelloWorld.scala index dc99f5a24d..0c0918045d 100644 --- a/zio-http-example/src/main/scala/example/HttpsHelloWorld.scala +++ b/zio-http-example/src/main/scala/example/HttpsHelloWorld.scala @@ -6,10 +6,10 @@ import zio.http._ object HttpsHelloWorld extends ZIOAppDefault { // Create HTTP route - val app: HttpApp[Any, Response] = Routes( + val app: Routes[Any, Response] = Routes( Method.GET / "text" -> handler(Response.text("Hello World!")), Method.GET / "json" -> handler(Response.json("""{"greetings": "Hello World!"}""")), - ).toHttpApp + ) /** * In this example, a private key and certificate are loaded from resources. diff --git a/zio-http-example/src/main/scala/example/Interrupt.scala b/zio-http-example/src/main/scala/example/Interrupt.scala index cb753b59b7..29f3bec3b8 100644 --- a/zio-http-example/src/main/scala/example/Interrupt.scala +++ b/zio-http-example/src/main/scala/example/Interrupt.scala @@ -16,7 +16,7 @@ object MyServer extends ZIOAppDefault { handler(ZIO.sleep(10.seconds).map(_ => Response.text("done")).onExit { e => Console.printLine(e).exit }), - ).toHttpApp + ) def run = Server.serve(app).provide(Server.default) diff --git a/zio-http-example/src/main/scala/example/MultipartFormData.scala b/zio-http-example/src/main/scala/example/MultipartFormData.scala index 8beb8018cb..e3f4963342 100644 --- a/zio-http-example/src/main/scala/example/MultipartFormData.scala +++ b/zio-http-example/src/main/scala/example/MultipartFormData.scala @@ -6,7 +6,7 @@ import zio.http._ object MultipartFormData extends ZIOAppDefault { - private val app: HttpApp[Any, Response] = + private val app: Routes[Any, Response] = Routes( Method.POST / "upload" -> handler { (req: Request) => @@ -39,7 +39,7 @@ object MultipartFormData extends ZIOAppDefault { } yield response else ZIO.succeed(Response(status = Status.NotFound)) }, - ).sandbox.toHttpApp + ).sandbox private def program: ZIO[Client with Server with Scope, Throwable, Unit] = for { diff --git a/zio-http-example/src/main/scala/example/MultipartFormDataStreaming.scala b/zio-http-example/src/main/scala/example/MultipartFormDataStreaming.scala index 53ae425d43..5c2fd39c8c 100644 --- a/zio-http-example/src/main/scala/example/MultipartFormDataStreaming.scala +++ b/zio-http-example/src/main/scala/example/MultipartFormDataStreaming.scala @@ -8,7 +8,7 @@ import zio.http._ object MultipartFormDataStreaming extends ZIOAppDefault { - private val app: HttpApp[Any, Response] = + private val app: Routes[Any, Response] = Routes( Method.POST / "upload-simple" -> handler { (req: Request) => for { @@ -61,7 +61,7 @@ object MultipartFormDataStreaming extends ZIOAppDefault { } yield Response.text(count.toString) else ZIO.succeed(Response(status = Status.NotFound)) }, - ).sandbox.toHttpApp @@ Middleware.debug + ).sandbox @@ Middleware.debug private def program: ZIO[Server, Throwable, Unit] = for { diff --git a/zio-http-example/src/main/scala/example/PlainTextBenchmarkServer.scala b/zio-http-example/src/main/scala/example/PlainTextBenchmarkServer.scala index c352ed68f8..c76af9aba7 100644 --- a/zio-http-example/src/main/scala/example/PlainTextBenchmarkServer.scala +++ b/zio-http-example/src/main/scala/example/PlainTextBenchmarkServer.scala @@ -29,13 +29,13 @@ object PlainTextBenchmarkServer extends ZIOAppDefault { // .serverTime .addHeader(Header.Server(STATIC_SERVER_NAME)) - private def plainTextApp(response: Response): HttpApp[Any, Response] = - Routes(Method.GET / plaintextPath -> Handler.fromResponse(response)).toHttpApp + private def plainTextApp(response: Response): Routes[Any, Response] = + Routes(Method.GET / plaintextPath -> Handler.fromResponse(response)) - private def jsonApp(json: Response): HttpApp[Any, Response] = - Routes(Method.GET / jsonPath -> Handler.fromResponse(json)).toHttpApp + private def jsonApp(json: Response): Routes[Any, Response] = + Routes(Method.GET / jsonPath -> Handler.fromResponse(json)) - val app: HttpApp[Any, Response] = plainTextApp(frozenPlainTextResponse) ++ jsonApp(frozenJsonResponse) + val app: Routes[Any, Response] = plainTextApp(frozenPlainTextResponse) ++ jsonApp(frozenJsonResponse) private val config = Server.Config.default .port(8080) diff --git a/zio-http-example/src/main/scala/example/RequestStreaming.scala b/zio-http-example/src/main/scala/example/RequestStreaming.scala index bd8166a29c..919c8ea466 100644 --- a/zio-http-example/src/main/scala/example/RequestStreaming.scala +++ b/zio-http-example/src/main/scala/example/RequestStreaming.scala @@ -17,7 +17,7 @@ object RequestStreaming extends ZIOAppDefault { val data = Body.fromStreamChunked(stream) Response(body = data) - }).toHttpApp + }) // Run it like any simple app val run: UIO[ExitCode] = diff --git a/zio-http-example/src/main/scala/example/SSEServer.scala b/zio-http-example/src/main/scala/example/SSEServer.scala index 4b4dc86c54..1ada6129d8 100644 --- a/zio-http-example/src/main/scala/example/SSEServer.scala +++ b/zio-http-example/src/main/scala/example/SSEServer.scala @@ -14,11 +14,11 @@ object SSEServer extends ZIOAppDefault { val stream: ZStream[Any, Nothing, ServerSentEvent] = ZStream.repeatWithSchedule(ServerSentEvent(ISO_LOCAL_TIME.format(LocalDateTime.now)), Schedule.spaced(1.second)) - val app: HttpApp[Any, Response] = + val app: Routes[Any, Response] = Routes( Method.GET / "sse" -> handler(Response.fromServerSentEvents(stream)), - ).toHttpApp + ) val run: URIO[Any, ExitCode] = { Server.serve(app).provide(Server.default).exitCode diff --git a/zio-http-example/src/main/scala/example/ServeOnAnyOpenPort.scala b/zio-http-example/src/main/scala/example/ServeOnAnyOpenPort.scala index cfea8ad2f2..3a201f29b0 100644 --- a/zio-http-example/src/main/scala/example/ServeOnAnyOpenPort.scala +++ b/zio-http-example/src/main/scala/example/ServeOnAnyOpenPort.scala @@ -8,7 +8,7 @@ object ServeOnAnyOpenPort extends ZIOAppDefault { val httpApp = Routes( Method.GET / "hello" -> handler(Response.text("Hello, World!")), - ).toHttpApp + ) val app = for { port <- Server.install(httpApp) diff --git a/zio-http-example/src/main/scala/example/ServerConfigurationExample.scala b/zio-http-example/src/main/scala/example/ServerConfigurationExample.scala index ec48754278..10f8e70486 100644 --- a/zio-http-example/src/main/scala/example/ServerConfigurationExample.scala +++ b/zio-http-example/src/main/scala/example/ServerConfigurationExample.scala @@ -9,7 +9,7 @@ import zio.http._ object ServerConfigurationExample extends ZIOAppDefault { val httpApp = Routes( Method.GET / "hello" -> handler(Response.text("Hello, World!")), - ).sandbox.toHttpApp + ).sandbox override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = Runtime.setConfigProvider(ConfigProvider.fromResourcePath()) diff --git a/zio-http-example/src/main/scala/example/ServerResponseCompression.scala b/zio-http-example/src/main/scala/example/ServerResponseCompression.scala index 668272e440..62f7baa3a4 100644 --- a/zio-http-example/src/main/scala/example/ServerResponseCompression.scala +++ b/zio-http-example/src/main/scala/example/ServerResponseCompression.scala @@ -7,7 +7,7 @@ import zio.http._ object ServerResponseCompression extends ZIOAppDefault { val httpApp = Routes( Method.GET / "hello" -> handler(Response.text("Hello, World!")), - ).sandbox.toHttpApp + ).sandbox val config = ZLayer.succeed( Server.Config.default.copy( diff --git a/zio-http-example/src/main/scala/example/ServerSentEventEndpoint.scala b/zio-http-example/src/main/scala/example/ServerSentEventEndpoint.scala index 742e40a76d..05eb0dc773 100644 --- a/zio-http-example/src/main/scala/example/ServerSentEventEndpoint.scala +++ b/zio-http-example/src/main/scala/example/ServerSentEventEndpoint.scala @@ -23,10 +23,10 @@ object ServerSentEventEndpoint extends ZIOAppDefault { val sseRoute = sseEndpoint.implement(Handler.succeed(stream)) - val app: HttpApp[Any, Response] = sseRoute.toHttpApp + val routes: Routes[Any, Response] = sseRoute.toRoutes override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = { - Server.serve(app).provide(Server.default).exitCode + Server.serve(routes).provide(Server.default).exitCode } } diff --git a/zio-http-example/src/main/scala/example/SignCookies.scala b/zio-http-example/src/main/scala/example/SignCookies.scala index 372d1c47a6..c5f9bb765c 100644 --- a/zio-http-example/src/main/scala/example/SignCookies.scala +++ b/zio-http-example/src/main/scala/example/SignCookies.scala @@ -15,7 +15,7 @@ object SignCookies extends ZIOAppDefault { private val app = Routes( Method.GET / "cookie" -> handler(Response.ok.addCookie(cookie.sign("secret"))), - ).toHttpApp + ) // Run it like any simple app val run = Server.serve(app).provide(Server.default) diff --git a/zio-http-example/src/main/scala/example/SimpleEffectBenchmarkServer.scala b/zio-http-example/src/main/scala/example/SimpleEffectBenchmarkServer.scala index 96d9a36d01..bd649069d0 100644 --- a/zio-http-example/src/main/scala/example/SimpleEffectBenchmarkServer.scala +++ b/zio-http-example/src/main/scala/example/SimpleEffectBenchmarkServer.scala @@ -16,7 +16,7 @@ object SimpleEffectBenchmarkServer extends ZIOAppDefault { private val STATIC_SERVER_NAME = "zio-http" - private val app: HttpApp[Any, Response] = Routes( + private val app: Routes[Any, Response] = Routes( Method.GET / "plaintext" -> handler( Response @@ -29,7 +29,7 @@ object SimpleEffectBenchmarkServer extends ZIOAppDefault { .json(jsonMessage) .addHeader(Header.Server(STATIC_SERVER_NAME)), ), - ).toHttpApp + ) private val config = Server.Config.default .port(8080) diff --git a/zio-http-example/src/main/scala/example/StaticFiles.scala b/zio-http-example/src/main/scala/example/StaticFiles.scala index f04f33c91f..13098c6e4c 100644 --- a/zio-http-example/src/main/scala/example/StaticFiles.scala +++ b/zio-http-example/src/main/scala/example/StaticFiles.scala @@ -11,7 +11,7 @@ object StaticFiles extends ZIOAppDefault { * "/static". For paths other than the resources directory, see * [[Middleware.serveDirectory]]. */ - val app = Routes.empty.toHttpApp @@ Middleware.serveResources(Path.empty / "static") + val routes = Routes.empty @@ Middleware.serveResources(Path.empty / "static") - override def run = Server.serve(app).provide(Server.default) + override def run = Server.serve(routes).provide(Server.default) } diff --git a/zio-http-example/src/main/scala/example/StaticServer.scala b/zio-http-example/src/main/scala/example/StaticServer.scala index 1c54f9d7a3..a5e0c20fa6 100644 --- a/zio-http-example/src/main/scala/example/StaticServer.scala +++ b/zio-http-example/src/main/scala/example/StaticServer.scala @@ -49,7 +49,7 @@ object StaticServer extends ZIOAppDefault { else Handler.notFound) } yield http }, - ).sandbox.toHttpApp + ).sandbox val run = Server.serve(app).provide(Server.default) diff --git a/zio-http-example/src/main/scala/example/StreamingResponse.scala b/zio-http-example/src/main/scala/example/StreamingResponse.scala index efdb38c2eb..15b6066cb9 100644 --- a/zio-http-example/src/main/scala/example/StreamingResponse.scala +++ b/zio-http-example/src/main/scala/example/StreamingResponse.scala @@ -16,7 +16,7 @@ object StreamingResponse extends ZIOAppDefault { // Create a message as a Chunk[Byte] def message = Chunk.fromArray("Hello world !\r\n".getBytes(Charsets.Http)) - def app: HttpApp[Any, Response] = Routes( + def app: Routes[Any, Response] = Routes( // Simple (non-stream) based route Method.GET / "health" -> handler(Response.ok), @@ -28,5 +28,5 @@ object StreamingResponse extends ZIOAppDefault { body = Body.fromStream(ZStream.fromChunk(message), message.length.toLong), // Encoding content using a ZStream ), ), - ).toHttpApp + ) } diff --git a/zio-http-example/src/main/scala/example/WebSocketAdvanced.scala b/zio-http-example/src/main/scala/example/WebSocketAdvanced.scala index be1b918e4a..ee5e9e97e9 100644 --- a/zio-http-example/src/main/scala/example/WebSocketAdvanced.scala +++ b/zio-http-example/src/main/scala/example/WebSocketAdvanced.scala @@ -48,13 +48,13 @@ object WebSocketAdvanced extends ZIOAppDefault { } } - val app: HttpApp[Any, Response] = + val app: Routes[Any, Response] = Routes( Method.GET / "greet" / string("name") -> handler { (name: String, _: Request) => Response.text(s"Greetings ${name}!") }, Method.GET / "subscriptions" -> handler(socketApp.toResponse), - ).toHttpApp + ) override val run = Server.serve(app).provide(Server.default) } diff --git a/zio-http-example/src/main/scala/example/WebSocketEcho.scala b/zio-http-example/src/main/scala/example/WebSocketEcho.scala index fa4217ab7c..d76d311435 100644 --- a/zio-http-example/src/main/scala/example/WebSocketEcho.scala +++ b/zio-http-example/src/main/scala/example/WebSocketEcho.scala @@ -21,13 +21,13 @@ object WebSocketEcho extends ZIOAppDefault { } } - private val app: HttpApp[Any, Response] = + private val app: Routes[Any, Response] = Routes( Method.GET / "greet" / string("name") -> handler { (name: String, _: Request) => Response.text(s"Greetings {$name}!") }, Method.GET / "subscriptions" -> handler(socketApp.toResponse), - ).toHttpApp + ) override val run = Server.serve(app).provide(Server.default) } diff --git a/zio-http-example/src/main/scala/example/codecs/RequestBodyJsonDeserializationExample.scala b/zio-http-example/src/main/scala/example/codecs/RequestBodyJsonDeserializationExample.scala index ef5235c676..c0fd1f9086 100644 --- a/zio-http-example/src/main/scala/example/codecs/RequestBodyJsonDeserializationExample.scala +++ b/zio-http-example/src/main/scala/example/codecs/RequestBodyJsonDeserializationExample.scala @@ -15,7 +15,7 @@ object RequestBodyJsonDeserializationExample extends ZIOAppDefault { implicit val schema: Schema[Book] = DeriveSchema.gen } - val app: Routes[Ref[List[Book]], Nothing] = + val routes: Routes[Ref[List[Book]], Nothing] = Routes( Method.POST / "books" -> handler { (req: Request) => @@ -33,5 +33,5 @@ object RequestBodyJsonDeserializationExample extends ZIOAppDefault { }, ) - def run = Server.serve(app.toHttpApp).provide(Server.default, ZLayer.fromZIO(Ref.make(List.empty[Book]))) + def run = Server.serve(routes).provide(Server.default, ZLayer.fromZIO(Ref.make(List.empty[Book]))) } diff --git a/zio-http-example/src/main/scala/example/codecs/ResponseBodyJsonSerializationExample.scala b/zio-http-example/src/main/scala/example/codecs/ResponseBodyJsonSerializationExample.scala index d42edffa87..5d9f5ec385 100644 --- a/zio-http-example/src/main/scala/example/codecs/ResponseBodyJsonSerializationExample.scala +++ b/zio-http-example/src/main/scala/example/codecs/ResponseBodyJsonSerializationExample.scala @@ -19,11 +19,11 @@ object ResponseBodyJsonSerializationExample extends ZIOAppDefault { val book2 = Book("Zionomicon", List("John A. De Goes", "Adam Fraser")) val book3 = Book("Effect-Oriented Programming", List("Bill Frasure", "Bruce Eckel", "James Ward")) - val app: Routes[Any, Nothing] = + val routes: Routes[Any, Nothing] = Routes( Method.GET / "users" -> handler(Response(body = Body.from(List(book1, book2, book3)))), ) - def run = Server.serve(app.toHttpApp).provide(Server.default) + def run = Server.serve(routes).provide(Server.default) } diff --git a/zio-http-example/src/main/scala/example/endpoint/BooksEndpointExample.scala b/zio-http-example/src/main/scala/example/endpoint/BooksEndpointExample.scala index 81159dee08..29ffab3f65 100644 --- a/zio-http-example/src/main/scala/example/endpoint/BooksEndpointExample.scala +++ b/zio-http-example/src/main/scala/example/endpoint/BooksEndpointExample.scala @@ -49,5 +49,5 @@ object BooksEndpointExample extends ZIOAppDefault { val swaggerRoutes = SwaggerUI.routes("docs" / "openapi", openAPI) val routes = Routes(booksRoute) ++ swaggerRoutes - def run = Server.serve(routes.toHttpApp).provide(Server.default) + def run = Server.serve(routes).provide(Server.default) } diff --git a/zio-http-example/src/main/scala/example/endpoint/CliExamples.scala b/zio-http-example/src/main/scala/example/endpoint/CliExamples.scala index a37a18613f..1029a15658 100644 --- a/zio-http-example/src/main/scala/example/endpoint/CliExamples.scala +++ b/zio-http-example/src/main/scala/example/endpoint/CliExamples.scala @@ -102,7 +102,7 @@ object TestCliServer extends zio.ZIOAppDefault with TestCliEndpoints { val routes = Routes(getUserRoute, getUserPostsRoute, createUserRoute) @@ Middleware.debug - val run = Server.serve(routes.toHttpApp).provide(Server.default) + val run = Server.serve(routes).provide(Server.default) } object TestCliClient extends zio.ZIOAppDefault with TestCliEndpoints { diff --git a/zio-http-example/src/main/scala/example/endpoint/EndpointWithError.scala b/zio-http-example/src/main/scala/example/endpoint/EndpointWithError.scala index b9d849f814..db4a1a050a 100644 --- a/zio-http-example/src/main/scala/example/endpoint/EndpointWithError.scala +++ b/zio-http-example/src/main/scala/example/endpoint/EndpointWithError.scala @@ -43,7 +43,7 @@ object EndpointWithError extends ZIOAppDefault { .mapError(err => NotFoundError(err, "The requested book was not found. Please try using a different ID.")) } - val app = endpoint.implement(getBookHandler).toHttpApp @@ Middleware.debug + val routes = endpoint.implement(getBookHandler).toRoutes @@ Middleware.debug - def run = Server.serve(app).provide(Server.default) + def run = Server.serve(routes).provide(Server.default) } diff --git a/zio-http-example/src/main/scala/example/endpoint/EndpointWithMultipleErrorsUsingEither.scala b/zio-http-example/src/main/scala/example/endpoint/EndpointWithMultipleErrorsUsingEither.scala index dbc2dbc4bd..4fe6e24ba5 100644 --- a/zio-http-example/src/main/scala/example/endpoint/EndpointWithMultipleErrorsUsingEither.scala +++ b/zio-http-example/src/main/scala/example/endpoint/EndpointWithMultipleErrorsUsingEither.scala @@ -56,7 +56,7 @@ object EndpointWithMultipleErrorsUsingEither extends ZIOAppDefault { ZIO.fail(Left(AuthenticationError("User is not authenticated", 123))) } - val app = endpoint.implement(getBookHandler).toHttpApp @@ Middleware.debug + val routes = endpoint.implement(getBookHandler).toRoutes @@ Middleware.debug - def run = Server.serve(app).provide(Server.default) + def run = Server.serve(routes).provide(Server.default) } diff --git a/zio-http-example/src/main/scala/example/endpoint/EndpointWithMultipleUnifiedErrors.scala b/zio-http-example/src/main/scala/example/endpoint/EndpointWithMultipleUnifiedErrors.scala index 2476662308..ff19d0543f 100644 --- a/zio-http-example/src/main/scala/example/endpoint/EndpointWithMultipleUnifiedErrors.scala +++ b/zio-http-example/src/main/scala/example/endpoint/EndpointWithMultipleUnifiedErrors.scala @@ -59,7 +59,7 @@ object EndpointWithMultipleUnifiedErrors extends ZIOAppDefault { ZIO.fail(AuthenticationError("User is not authenticated", 123)) } - val app = endpoint.implement(getBookHandler).toHttpApp @@ Middleware.debug + val routes = endpoint.implement(getBookHandler).toRoutes @@ Middleware.debug - def run = Server.serve(app).provide(Server.default) + def run = Server.serve(routes).provide(Server.default) } diff --git a/zio-http-testkit/src/main/scala/zio/http/TestClient.scala b/zio-http-testkit/src/main/scala/zio/http/TestClient.scala index 6205332459..46047fa173 100644 --- a/zio-http-testkit/src/main/scala/zio/http/TestClient.scala +++ b/zio-http-testkit/src/main/scala/zio/http/TestClient.scala @@ -10,7 +10,7 @@ import zio._ * Server */ final case class TestClient( - behavior: Ref[HttpApp[Any, Response]], + behavior: Ref[Routes[Any, Response]], serverSocketBehavior: Ref[WebSocketApp[Any]], ) extends ZClient.Driver[Any, Throwable] { @@ -21,11 +21,11 @@ final case class TestClient( * * @example * {{{ - * TestClient.addHttpApp(HttpApp.empty) + * TestClient.addRoutes(HttpApp.empty) * }}} */ - def addHttpApp( - app: HttpApp[Any, Response], + def addRoutes( + app: Routes[Any, Response], ): ZIO[Any, Nothing, Unit] = behavior.update(_ ++ app) @@ -104,7 +104,7 @@ final case class TestClient( ): ZIO[R, Nothing, Unit] = for { r <- ZIO.environment[R] - provided = HttpApp.fromIterable(route +: routes).provideEnvironment(r) + provided = Routes.fromIterable(route +: routes).provideEnvironment(r) _ <- behavior.update(_ ++ provided) } yield () @@ -174,8 +174,8 @@ final case class TestClient( object TestClient { - def addApp(app: HttpApp[Any, Response]): ZIO[TestClient, Nothing, Unit] = - ZIO.serviceWithZIO[TestClient](_.addHttpApp(app)) + def addRoutes(app: Routes[Any, Response]): ZIO[TestClient, Nothing, Unit] = + ZIO.serviceWithZIO[TestClient](_.addRoutes(app)) /** * Adds an exact 1-1 behavior @@ -243,7 +243,7 @@ object TestClient { val layer: ZLayer[Any, Nothing, TestClient & Client] = ZLayer.scopedEnvironment { for { - behavior <- Ref.make[HttpApp[Any, Response]](HttpApp.empty) + behavior <- Ref.make[Routes[Any, Response]](Routes.empty) socketBehavior <- Ref.make[WebSocketApp[Any]](WebSocketApp.unit) driver = TestClient(behavior, socketBehavior) } yield ZEnvironment[TestClient, Client](driver, ZClient.fromDriver(driver)) diff --git a/zio-http-testkit/src/main/scala/zio/http/TestServer.scala b/zio-http-testkit/src/main/scala/zio/http/TestServer.scala index c991464bbe..e144a05447 100644 --- a/zio-http-testkit/src/main/scala/zio/http/TestServer.scala +++ b/zio-http-testkit/src/main/scala/zio/http/TestServer.scala @@ -68,8 +68,8 @@ final case class TestServer(driver: Driver, bindPort: Int) extends Server { ): ZIO[R, Nothing, Unit] = for { r <- ZIO.environment[R] - provided = route.provideEnvironment(r) - app: HttpApp[Any, Response] = provided.toHttpApp + provided = route.provideEnvironment(r) + app: Routes[Any, Response] = provided.toRoutes _ <- driver.addApp(app, r) } yield () @@ -91,12 +91,12 @@ final case class TestServer(driver: Driver, bindPort: Int) extends Server { ): ZIO[R, Nothing, Unit] = for { r <- ZIO.environment[R] - provided = routes.provideEnvironment(r) - app: HttpApp[Any, Response] = provided.toHttpApp + provided = routes.provideEnvironment(r) + app: Routes[Any, Response] = provided _ <- driver.addApp(app, r) } yield () - override def install[R](httpApp: HttpApp[R, Response])(implicit + override def install[R](httpApp: Routes[R, Response])(implicit trace: zio.Trace, ): URIO[R, Unit] = ZIO diff --git a/zio-http-testkit/src/test/scala/zio/http/SocketContractSpec.scala b/zio-http-testkit/src/test/scala/zio/http/SocketContractSpec.scala index 76ca2141bc..b5d026360d 100644 --- a/zio-http-testkit/src/test/scala/zio/http/SocketContractSpec.scala +++ b/zio-http-testkit/src/test/scala/zio/http/SocketContractSpec.scala @@ -130,7 +130,7 @@ object SocketContractSpec extends ZIOHttpSpec { ZIO.serviceWithZIO[Server](server => for { p <- Promise.make[Throwable, Unit] - _ <- server.install(serverApp(p).toHttpAppWS) + _ <- server.install(serverApp(p).toRoutes) } yield (server.port, p), ) diff --git a/zio-http-testkit/src/test/scala/zio/http/TestClientSpec.scala b/zio-http-testkit/src/test/scala/zio/http/TestClientSpec.scala index 1d817d1d96..07e198e551 100644 --- a/zio-http-testkit/src/test/scala/zio/http/TestClientSpec.scala +++ b/zio-http-testkit/src/test/scala/zio/http/TestClientSpec.scala @@ -59,8 +59,8 @@ object TestClientSpec extends ZIOHttpSpec { test("addRoutes") { for { client <- ZIO.service[Client] - _ <- TestClient.addApp { - HttpApp( + _ <- TestClient.addRoutes { + Routes( Method.GET / trailing -> handler { Response.text("fallback") }, Method.GET / "hello" / "world" -> handler { Response.text("Hey there!") }, ) diff --git a/zio-http/jvm/src/main/scala/zio/http/netty/server/NettyDriver.scala b/zio-http/jvm/src/main/scala/zio/http/netty/server/NettyDriver.scala index 2e99fcd21e..3c47a98e98 100644 --- a/zio-http/jvm/src/main/scala/zio/http/netty/server/NettyDriver.scala +++ b/zio-http/jvm/src/main/scala/zio/http/netty/server/NettyDriver.scala @@ -24,7 +24,7 @@ import zio._ import zio.http.Driver.StartResult import zio.http.netty._ import zio.http.netty.client.NettyClientDriver -import zio.http.{ClientDriver, Driver, HttpApp, Response, Server} +import zio.http.{ClientDriver, Driver, Response, Routes, Server} import io.netty.bootstrap.ServerBootstrap import io.netty.channel._ @@ -53,12 +53,12 @@ private[zio] final case class NettyDriver( ) } yield StartResult(port, serverInboundHandler.inFlightRequests) - def addApp[R](newApp: HttpApp[R, Response], env: ZEnvironment[R])(implicit trace: Trace): UIO[Unit] = ZIO.succeed { + def addApp[R](newApp: Routes[R, Response], env: ZEnvironment[R])(implicit trace: Trace): UIO[Unit] = ZIO.succeed { var loop = true while (loop) { val oldAppAndEnv = appRef.get() val (oldApp, oldEnv) = oldAppAndEnv - val updatedApp = (oldApp ++ newApp).asInstanceOf[HttpApp[Any, Response]] + val updatedApp = (oldApp ++ newApp).asInstanceOf[Routes[Any, Response]] val updatedEnv = oldEnv.unionAll(env) val updatedAppAndEnv = (updatedApp, updatedEnv) @@ -114,7 +114,7 @@ object NettyDriver { implicit val trace: Trace = Trace.empty ZLayer.makeSome[EventLoopGroup & ChannelFactory[ServerChannel] & Server.Config & NettyConfig, Driver]( ZLayer.succeed( - new AtomicReference[(HttpApp[Any, Response], ZEnvironment[Any])]((HttpApp.empty, ZEnvironment.empty)), + new AtomicReference[(Routes[Any, Response], ZEnvironment[Any])]((Routes.empty, ZEnvironment.empty)), ), NettyRuntime.live, ServerChannelInitializer.layer, diff --git a/zio-http/jvm/src/main/scala/zio/http/netty/server/ServerInboundHandler.scala b/zio-http/jvm/src/main/scala/zio/http/netty/server/ServerInboundHandler.scala index 0e30f0aadf..88a6a93aec 100644 --- a/zio-http/jvm/src/main/scala/zio/http/netty/server/ServerInboundHandler.scala +++ b/zio-http/jvm/src/main/scala/zio/http/netty/server/ServerInboundHandler.scala @@ -49,8 +49,8 @@ private[zio] final case class ServerInboundHandler( implicit private val unsafe: Unsafe = Unsafe.unsafe - private var app: HttpApp[Any, Response] = _ - private var env: ZEnvironment[Any] = _ + private var app: Routes[Any, Response] = _ + private var env: ZEnvironment[Any] = _ val inFlightRequests: LongAdder = new LongAdder() val readClientCert = config.sslConfig.exists(_.includeClientCert) diff --git a/zio-http/jvm/src/main/scala/zio/http/netty/server/package.scala b/zio-http/jvm/src/main/scala/zio/http/netty/server/package.scala index 3e0fef2584..d62f15103e 100644 --- a/zio-http/jvm/src/main/scala/zio/http/netty/server/package.scala +++ b/zio-http/jvm/src/main/scala/zio/http/netty/server/package.scala @@ -23,7 +23,7 @@ import zio.http._ import java.util.concurrent.atomic.AtomicReference // scalafix:ok; import zio.stacktracer.TracingImplicits.disableAutoTrace package object server { - private[server] type AppRef = AtomicReference[(HttpApp[Any, Response], ZEnvironment[Any])] + private[server] type AppRef = AtomicReference[(Routes[Any, Response], ZEnvironment[Any])] private[server] type EnvRef = AtomicReference[ZEnvironment[Any]] val live: ZLayer[Server.Config, Throwable, Driver] = diff --git a/zio-http/jvm/src/test/scala/zio/http/ClientProxySpec.scala b/zio-http/jvm/src/test/scala/zio/http/ClientProxySpec.scala index 7a9e601650..7980724a94 100644 --- a/zio-http/jvm/src/test/scala/zio/http/ClientProxySpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/ClientProxySpec.scala @@ -20,11 +20,10 @@ import java.net.ConnectException import zio.Config.Secret import zio.test.Assertion._ -import zio.test.TestAspect.{sequential, timeout, withLiveClock} +import zio.test.TestAspect.{sequential, withLiveClock} import zio.test._ -import zio.{Scope, Trace, ZIO, ZLayer, durationInt} +import zio.{Scope, ZIO, ZLayer} -import zio.http.ZClient.{Config, DriverLive} import zio.http.internal.{DynamicServer, HttpRunnableSpec, serverTestLayer} import zio.http.netty.NettyConfig import zio.http.netty.client.NettyClientDriver @@ -56,7 +55,7 @@ object ClientProxySpec extends HttpRunnableSpec { for { port <- ZIO.environmentWithZIO[DynamicServer](_.get.port) url <- ZIO.fromEither(URL.decode(s"http://localhost:$port")) - id <- DynamicServer.deploy(Handler.ok.toHttpApp) + id <- DynamicServer.deploy(Handler.ok.toRoutes) proxy = Proxy.empty.url(url).headers(Headers(DynamicServer.APP_ID, id)) zclient <- ZIO.serviceWith[Client](_.proxy(proxy)) out <- zclient @@ -76,7 +75,7 @@ object ClientProxySpec extends HttpRunnableSpec { for { port <- ZIO.environmentWithZIO[DynamicServer](_.get.port) url <- ZIO.fromEither(URL.decode(s"http://localhost:$port")) - id <- DynamicServer.deploy(Handler.ok.toHttpApp) + id <- DynamicServer.deploy(Handler.ok.toRoutes) proxy = Proxy.empty.url(url).headers(Headers(DynamicServer.APP_ID, id)) out <- Client .request( @@ -101,7 +100,7 @@ object ClientProxySpec extends HttpRunnableSpec { else Response.status(Status.Forbidden) } - .toHttpApp + .toRoutes val res = for { 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 230baf8c46..2b4426f8e8 100644 --- a/zio-http/jvm/src/test/scala/zio/http/ClientSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/ClientSpec.scala @@ -31,26 +31,26 @@ object ClientSpec extends HttpRunnableSpec { def clientSpec = suite("ClientSpec")( test("respond Ok") { - val app = Handler.ok.toHttpApp.deploy(Request()).map(_.status) + val app = Handler.ok.toRoutes.deploy(Request()).map(_.status) assertZIO(app)(equalTo(Status.Ok)) }, test("non empty content") { - val app = Handler.text("abc").toHttpApp + val app = Handler.text("abc").toRoutes val responseContent = app.deploy(Request()).flatMap(_.body.asChunk) assertZIO(responseContent)(isNonEmpty) }, test("echo POST request content") { - val app = Handler.fromFunctionZIO[Request] { req => req.body.asString.map(Response.text(_)) }.sandbox.toHttpApp + val app = Handler.fromFunctionZIO[Request] { req => req.body.asString.map(Response.text(_)) }.sandbox.toRoutes val res = app.deploy(Request(method = Method.POST, body = Body.fromString("ZIO user"))).flatMap(_.body.asString) assertZIO(res)(equalTo("ZIO user")) }, test("empty content") { - val app = HttpApp.empty + val app = Routes.empty val responseContent = app.deploy(Request()).flatMap(_.body.asString.map(_.length)) assertZIO(responseContent)(equalTo(0)) }, test("text content") { - val app = Handler.text("zio user does not exist").toHttpApp + val app = Handler.text("zio user does not exist").toRoutes val responseContent = app.deploy(Request()).flatMap(_.body.asString) assertZIO(responseContent)(containsString("user")) }, @@ -61,7 +61,7 @@ object ClientSpec extends HttpRunnableSpec { assertZIO(res)(isLeft(isSubtype[ConnectException](anything))) }, test("streaming content to server") { - val app = Handler.fromFunctionZIO[Request] { req => req.body.asString.map(Response.text(_)) }.sandbox.toHttpApp + val app = Handler.fromFunctionZIO[Request] { req => req.body.asString.map(Response.text(_)) }.sandbox.toRoutes val stream = ZStream.fromIterable(List("a", "b", "c"), chunkSize = 1) val res = app .deploy(Request(method = Method.POST, body = Body.fromCharSequenceStreamChunked(stream))) @@ -71,7 +71,7 @@ object ClientSpec extends HttpRunnableSpec { test("no trailing slash for empty path") { for { baseURL <- DynamicServer.httpURL - _ <- Handler.ok.toHttpApp + _ <- Handler.ok.toRoutes .deployAndRequest(c => (c @@ ZClientAspect.requestLogging()).get("")) .runZIO(()) loggedUrl <- ZTestLogger.logOutput.map(_.collectFirst { case m => m.annotations("url") }.mkString) @@ -80,14 +80,14 @@ object ClientSpec extends HttpRunnableSpec { test("trailing slash for explicit slash") { for { baseURL <- DynamicServer.httpURL - _ <- Handler.ok.toHttpApp + _ <- Handler.ok.toRoutes .deployAndRequest(c => (c @@ ZClientAspect.requestLogging()).get("/")) .runZIO(()) loggedUrl <- ZTestLogger.logOutput.map(_.collectFirst { case m => m.annotations("url") }.mkString) } yield assertTrue(loggedUrl == s"$baseURL/") }, test("reading of unfinished body must fail") { - val app = Handler.fromStreamChunked(ZStream.never).sandbox.toHttpApp + val app = Handler.fromStreamChunked(ZStream.never).sandbox.toRoutes val requestCode = (client: Client) => (for { response <- ZIO.scoped(client(Request())) diff --git a/zio-http/jvm/src/test/scala/zio/http/ClientStreamingSpec.scala b/zio-http/jvm/src/test/scala/zio/http/ClientStreamingSpec.scala index 6d57883092..f18338092d 100644 --- a/zio-http/jvm/src/test/scala/zio/http/ClientStreamingSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/ClientStreamingSpec.scala @@ -32,7 +32,7 @@ import zio.http.netty.NettyConfig.LeakDetectionLevel object ClientStreamingSpec extends HttpRunnableSpec { def extractStatus(response: Response): Status = response.status - val app = HttpApp( + val app = Routes( Method.GET / "simple-get" -> handler(Response.text("simple response")), Method.GET / "streaming-get" -> diff --git a/zio-http/jvm/src/test/scala/zio/http/ContentTypeSpec.scala b/zio-http/jvm/src/test/scala/zio/http/ContentTypeSpec.scala index ead7cfbefc..c9fc132d6e 100644 --- a/zio-http/jvm/src/test/scala/zio/http/ContentTypeSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/ContentTypeSpec.scala @@ -18,7 +18,7 @@ package zio.http import zio._ import zio.test.Assertion.{equalTo, isNone, isSome} -import zio.test.TestAspect.{timeout, withLiveClock} +import zio.test.TestAspect.withLiveClock import zio.test._ import zio.http.internal.{DynamicServer, HttpRunnableSpec, serverTestLayer} @@ -28,31 +28,31 @@ object ContentTypeSpec extends HttpRunnableSpec { val contentSpec = suite("Content type header on file response")( test("mp4") { val res = - Handler.fromResource("TestFile2.mp4").sandbox.toHttpApp.deploy(Request()).map(_.header(Header.ContentType)) + Handler.fromResource("TestFile2.mp4").sandbox.toRoutes.deploy(Request()).map(_.header(Header.ContentType)) assertZIO(res)(isSome(equalTo(Header.ContentType(MediaType.video.`mp4`)))) }, test("js") { val res = - Handler.fromResource("TestFile3.js").sandbox.toHttpApp.deploy(Request()).map(_.header(Header.ContentType)) + Handler.fromResource("TestFile3.js").sandbox.toRoutes.deploy(Request()).map(_.header(Header.ContentType)) assertZIO(res)(isSome(equalTo(Header.ContentType(MediaType.application.`javascript`)))) }, test("no extension") { - val res = Handler.fromResource("TestFile4").sandbox.toHttpApp.deploy(Request()).map(_.header(Header.ContentType)) + val res = Handler.fromResource("TestFile4").sandbox.toRoutes.deploy(Request()).map(_.header(Header.ContentType)) assertZIO(res)(isNone) }, test("css") { val res = - Handler.fromResource("TestFile5.css").sandbox.toHttpApp.deploy(Request()).map(_.header(Header.ContentType)) + Handler.fromResource("TestFile5.css").sandbox.toRoutes.deploy(Request()).map(_.header(Header.ContentType)) assertZIO(res)(isSome(equalTo(Header.ContentType(MediaType.text.`css`)))) }, test("mp3") { val res = - Handler.fromResource("TestFile6.mp3").sandbox.toHttpApp.deploy(Request()).map(_.header(Header.ContentType)) + Handler.fromResource("TestFile6.mp3").sandbox.toRoutes.deploy(Request()).map(_.header(Header.ContentType)) assertZIO(res)(isSome(equalTo(Header.ContentType(MediaType.audio.`mpeg`)))) }, test("unidentified extension") { val res = - Handler.fromResource("truststore.jks").sandbox.toHttpApp.deploy(Request()).map(_.header(Header.ContentType)) + Handler.fromResource("truststore.jks").sandbox.toRoutes.deploy(Request()).map(_.header(Header.ContentType)) assertZIO(res)(isNone) }, test("already set content-type") { @@ -62,7 +62,7 @@ object ContentTypeSpec extends HttpRunnableSpec { .fromResource("TestFile6.mp3") .map(_.addHeader(Header.ContentType(expected))) .sandbox - .toHttpApp + .toRoutes .deploy(Request()) .map( _.header(Header.ContentType), diff --git a/zio-http/jvm/src/test/scala/zio/http/DualSSLSpec.scala b/zio-http/jvm/src/test/scala/zio/http/DualSSLSpec.scala index 52eeace751..18cf609579 100644 --- a/zio-http/jvm/src/test/scala/zio/http/DualSSLSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/DualSSLSpec.scala @@ -54,13 +54,13 @@ object DualSSLSpec extends ZIOHttpSpec { val payload = Gen.alphaNumericStringBounded(10000, 20000) - val app: HttpApp[Any] = Routes( + val app: Routes[Any, Response] = Routes( Method.GET / "success" -> handler((req: Request) => Response.text( req.remoteCertificate.map { _.asInstanceOf[X509Certificate].getSubjectX500Principal.getName() }.getOrElse(""), ), ), - ).sandbox.toHttpApp + ).sandbox val httpUrl = URL.decode("http://localhost:8073/success").toOption.get diff --git a/zio-http/jvm/src/test/scala/zio/http/DynamicAppTest.scala b/zio-http/jvm/src/test/scala/zio/http/DynamicAppTest.scala index 1b5f28e658..293820ca3f 100644 --- a/zio-http/jvm/src/test/scala/zio/http/DynamicAppTest.scala +++ b/zio-http/jvm/src/test/scala/zio/http/DynamicAppTest.scala @@ -26,13 +26,13 @@ import zio.http.netty.client.NettyClientDriver object DynamicAppTest extends ZIOHttpSpec { def extractStatus(response: Response): Status = response.status - val httpApp1: HttpApp[Any, Response] = - HttpApp( + val httpApp1: Routes[Any, Response] = + Routes( Method.GET / "good" -> Handler.ok, ).sandbox - val httpApp2: HttpApp[Any, Response] = - HttpApp( + val httpApp2: Routes[Any, Response] = + Routes( Method.GET / "better" -> handler(Response.status(Status.Created)), ).sandbox diff --git a/zio-http/jvm/src/test/scala/zio/http/FlashSpec.scala b/zio-http/jvm/src/test/scala/zio/http/FlashSpec.scala index f20f709ba2..6ece32043b 100644 --- a/zio-http/jvm/src/test/scala/zio/http/FlashSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/FlashSpec.scala @@ -118,7 +118,7 @@ object FlashSpec extends ZIOHttpSpec { } yield Response.html(html) } - val app = HttpApp(routeUserSave, routeConfirm) + val app = Routes(routeUserSave, routeConfirm) for { response1 <- app.runZIO(Request.post(URL(routeUserSavePath.format(()).toOption.get), Body.empty)) diff --git a/zio-http/jvm/src/test/scala/zio/http/KeepAliveSpec.scala b/zio-http/jvm/src/test/scala/zio/http/KeepAliveSpec.scala index 0147b4e27f..b8f50f4047 100644 --- a/zio-http/jvm/src/test/scala/zio/http/KeepAliveSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/KeepAliveSpec.scala @@ -16,16 +16,16 @@ package zio.http +import zio.Scope import zio.test.Assertion.{equalTo, isNone, isSome} -import zio.test.TestAspect.{sequential, timeout, withLiveClock} +import zio.test.TestAspect.{sequential, withLiveClock} import zio.test.{Spec, assert} -import zio.{Scope, ZIO, durationInt} import zio.http.internal.{DynamicServer, HttpRunnableSpec, serverTestLayer} object KeepAliveSpec extends HttpRunnableSpec { - private val app = Handler.ok.toHttpApp + private val routes = Handler.ok.toRoutes private val connectionCloseHeader = Headers(Header.Connection.Close) private val keepAliveHeader = Headers(Header.Connection.KeepAlive) private val appKeepAliveEnabled = serve @@ -35,13 +35,13 @@ object KeepAliveSpec extends HttpRunnableSpec { test("without connection close") { for { _ <- appKeepAliveEnabled - res <- app.deploy(Request()).map(_.header(Header.Connection)) + res <- routes.deploy(Request()).map(_.header(Header.Connection)) } yield assert(res)(isNone) }, test("with connection close") { for { _ <- appKeepAliveEnabled - res <- app.deploy(Request(headers = connectionCloseHeader)).map(_.header(Header.Connection)) + res <- routes.deploy(Request(headers = connectionCloseHeader)).map(_.header(Header.Connection)) } yield assert(res)(isSome(equalTo(Header.Connection.Close))) }, ), @@ -49,13 +49,13 @@ object KeepAliveSpec extends HttpRunnableSpec { test("without keep-alive") { for { _ <- appKeepAliveEnabled - res <- app.deploy(Request(version = Version.`HTTP/1.0`)).map(_.header(Header.Connection)) + res <- routes.deploy(Request(version = Version.`HTTP/1.0`)).map(_.header(Header.Connection)) } yield assert(res)(isSome(equalTo(Header.Connection.Close))) }, test("with keep-alive") { for { _ <- appKeepAliveEnabled - res <- app + res <- routes .deploy(Request(version = Version.Http_1_0, headers = keepAliveHeader)) .map(_.header(Header.Connection)) } yield assert(res)(isNone) diff --git a/zio-http/jvm/src/test/scala/zio/http/LogAnnotationMiddlewareSpec.scala b/zio-http/jvm/src/test/scala/zio/http/LogAnnotationMiddlewareSpec.scala index f9f58b5a1f..f11467f974 100644 --- a/zio-http/jvm/src/test/scala/zio/http/LogAnnotationMiddlewareSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/LogAnnotationMiddlewareSpec.scala @@ -12,7 +12,6 @@ object LogAnnotationMiddlewareSpec extends ZIOSpecDefault { handler(ZIO.logWarning("Oh!") *> ZIO.succeed(Response.text("Hey logging!"))), ) .@@(Middleware.logAnnotate("label", "value")) - .toHttpApp .runZIO(Request.get("/")) for { @@ -32,7 +31,6 @@ object LogAnnotationMiddlewareSpec extends ZIOSpecDefault { Set(LogAnnotation("method", req.method.name), LogAnnotation("path", req.path.encode)), ), ) - .toHttpApp .runZIO(Request.get("/")) for { @@ -51,7 +49,6 @@ object LogAnnotationMiddlewareSpec extends ZIOSpecDefault { ) .@@(Middleware.logAnnotateHeaders("header")) .@@(Middleware.logAnnotateHeaders(Header.UserAgent.name)) - .toHttpApp .runZIO { Request .get("/") diff --git a/zio-http/jvm/src/test/scala/zio/http/MultipartMixedSpec.scala b/zio-http/jvm/src/test/scala/zio/http/MultipartMixedSpec.scala index dc2e2c4a56..6192a7c028 100644 --- a/zio-http/jvm/src/test/scala/zio/http/MultipartMixedSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/MultipartMixedSpec.scala @@ -401,7 +401,7 @@ object MultipartMixedSpec extends ZIOHttpSpec { case (None, None) => res0 case (Some(mt), None) => res0.contentType(mt) case (Some(mt), Some(b)) => res0.contentType(mt, b) - case (None, Some(b)) => + case (None, Some(_)) => sys.error("r u joking me?!?!") } diff --git a/zio-http/jvm/src/test/scala/zio/http/NettyMaxHeaderLengthSpec.scala b/zio-http/jvm/src/test/scala/zio/http/NettyMaxHeaderLengthSpec.scala index 1d636ea173..86ea9d4a92 100644 --- a/zio-http/jvm/src/test/scala/zio/http/NettyMaxHeaderLengthSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/NettyMaxHeaderLengthSpec.scala @@ -29,7 +29,7 @@ object NettyMaxHeaderLengthSpec extends ZIOHttpSpec { override def spec: Spec[TestEnvironment with Scope, Any] = test("should get a failure instead of an empty body") { - val app = Handler + val routes = Handler .fromFunctionZIO[Request] { request => request.body.asString.map { body => val responseBody = if (body.isEmpty) "" else body @@ -37,9 +37,10 @@ object NettyMaxHeaderLengthSpec extends ZIOHttpSpec { } // this should not be run, as the request is invalid } .sandbox - .toHttpApp + .toRoutes + for { - port <- Server.install(app) + port <- Server.install(routes) url = URL.decode(s"http://localhost:$port").toOption.get headers = Headers( Header.UserAgent.Product("a looooooooooooooooooooooooooooong header", None), diff --git a/zio-http/jvm/src/test/scala/zio/http/NettyMaxInitialLineLengthSpec.scala b/zio-http/jvm/src/test/scala/zio/http/NettyMaxInitialLineLengthSpec.scala index 513d13d85e..441c6b962a 100644 --- a/zio-http/jvm/src/test/scala/zio/http/NettyMaxInitialLineLengthSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/NettyMaxInitialLineLengthSpec.scala @@ -32,7 +32,7 @@ object NettyMaxInitialLineLength extends ZIOHttpSpec { override def spec: Spec[TestEnvironment with Scope, Any] = test("should get a failure instead of an empty body") { - val app = Handler + val routes = Handler .fromFunctionZIO[Request] { request => request.body.asString.map { body => val responseBody = if (body.isEmpty) "" else body @@ -40,9 +40,10 @@ object NettyMaxInitialLineLength extends ZIOHttpSpec { } // this should not be run, as the request is invalid } .sandbox - .toHttpApp + .toRoutes + for { - port <- Server.install(app) + port <- Server.install(routes) url = URL .decode(s"http://localhost:$port/a%20looooooooooooooooooooooooooooong%20query%20parameter") .toOption diff --git a/zio-http/jvm/src/test/scala/zio/http/RequestStreamingServerSpec.scala b/zio-http/jvm/src/test/scala/zio/http/RequestStreamingServerSpec.scala index 2c23fd0262..ee69261f84 100644 --- a/zio-http/jvm/src/test/scala/zio/http/RequestStreamingServerSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/RequestStreamingServerSpec.scala @@ -49,14 +49,15 @@ object RequestStreamingServerSpec extends HttpRunnableSpec { test("test unsafe large content") { val size = 1024 * 1024 val content = genString(size, '?') - val app = Handler + val routes = Handler .fromFunctionZIO[Request] { _.body.asStream.runCount .map(bytesCount => Response.text(bytesCount.toString)) } .sandbox - .toHttpApp - val res = app.deploy(Request(body = Body.fromString(content))).flatMap(_.body.asString) + .toRoutes + + val res = routes.deploy(Request(body = Body.fromString(content))).flatMap(_.body.asString) assertZIO(res)(equalTo(size.toString)) }, test("multiple body read") { @@ -67,12 +68,12 @@ object RequestStreamingServerSpec extends HttpRunnableSpec { _ <- req.body.asChunk } yield Response.ok } - }.sandbox.toHttpApp + }.sandbox val res = app.deploy(Request()).map(_.status) assertZIO(res)(equalTo(Status.InternalServerError)) }, suite("streaming request passed to client")({ - val app = HttpApp( + val app = Routes( Method.POST / "1" -> handler { (req: Request) => val host = req.headers.get(Header.Host).get val newRequest = diff --git a/zio-http/jvm/src/test/scala/zio/http/ResponseCompressionSpec.scala b/zio-http/jvm/src/test/scala/zio/http/ResponseCompressionSpec.scala index 2e359e64be..64a4e8899d 100644 --- a/zio-http/jvm/src/test/scala/zio/http/ResponseCompressionSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/ResponseCompressionSpec.scala @@ -29,13 +29,13 @@ import zio.http.netty.NettyConfig object ResponseCompressionSpec extends ZIOHttpSpec { - private val text: HttpApp[Any, Response] = - HttpApp( + private val text: Routes[Any, Response] = + Routes( Method.GET / "text" -> handler(Response.text("Hello World!\n")), ) private val stream = - HttpApp( + Routes( Method.GET / "stream-chunked" -> handler( Response( diff --git a/zio-http/jvm/src/test/scala/zio/http/RouteSpec.scala b/zio-http/jvm/src/test/scala/zio/http/RouteSpec.scala index d1d4c02ea5..11e5be89d3 100644 --- a/zio-http/jvm/src/test/scala/zio/http/RouteSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/RouteSpec.scala @@ -82,7 +82,7 @@ object RouteSpec extends ZIOHttpSpec { .handleErrorCauseZIO(c => p.failCause(c).as(Response.internalServerError)) request = Request.get(URL.decode("/endpoint").toOption.get) - response <- errorHandled.toHttpApp.runZIO(request) + response <- errorHandled.toRoutes.runZIO(request) result <- p.await.catchAllCause(c => ZIO.succeed(c.prettyPrint)) } yield assertTrue(extractStatus(response) == Status.InternalServerError, result.contains("hmm...")) @@ -98,7 +98,7 @@ object RouteSpec extends ZIOHttpSpec { ) request = Request.get(URL.decode("/endpoint").toOption.get) - response <- errorHandled.toHttpApp.runZIO(request) + response <- errorHandled.toRoutes.runZIO(request) result <- p.await.catchAllCause(c => ZIO.succeed(c.prettyPrint)) resultWarning <- ZIO.fromOption(response.headers.get(Header.Warning).map(_.text)) @@ -116,7 +116,7 @@ object RouteSpec extends ZIOHttpSpec { ) val request = Request.get(URL.decode("/endpoint").toOption.get) for { - response <- errorHandled.toHttpApp.runZIO(request) + response <- errorHandled.toRoutes.runZIO(request) resultWarning <- ZIO.fromOption(response.headers.get(Header.Warning).map(_.text)) } yield assertTrue( @@ -129,7 +129,7 @@ object RouteSpec extends ZIOHttpSpec { val errorHandled = route.handleErrorCause(_ => Response.text("error").status(Status.InternalServerError)) val request = Request.get(URL.decode("/endpoint").toOption.get) for { - response <- errorHandled.toHttpApp.runZIO(request) + response <- errorHandled.toRoutes.runZIO(request) bodyString <- response.body.asString } yield assertTrue(extractStatus(response) == Status.InternalServerError, bodyString == "error") }, @@ -139,7 +139,7 @@ object RouteSpec extends ZIOHttpSpec { route.handleErrorCauseZIO(_ => ZIO.succeed(Response.text("error").status(Status.InternalServerError))) val request = Request.get(URL.decode("/endpoint").toOption.get) for { - response <- errorHandled.toHttpApp.runZIO(request) + response <- errorHandled.toRoutes.runZIO(request) bodyString <- response.body.asString } yield assertTrue(extractStatus(response) == Status.InternalServerError, bodyString == "error") }, @@ -149,7 +149,7 @@ object RouteSpec extends ZIOHttpSpec { route.handleErrorRequestCause((_, _) => Response.text("error").status(Status.InternalServerError)) val request = Request.get(URL.decode("/endpoint").toOption.get) for { - response <- errorHandled.toHttpApp.runZIO(request) + response <- errorHandled.toRoutes.runZIO(request) bodyString <- response.body.asString } yield assertTrue(extractStatus(response) == Status.InternalServerError, bodyString == "error") }, @@ -160,7 +160,7 @@ object RouteSpec extends ZIOHttpSpec { ) val request = Request.get(URL.decode("/endpoint").toOption.get) for { - response <- errorHandled.toHttpApp.runZIO(request) + response <- errorHandled.toRoutes.runZIO(request) bodyString <- response.body.asString } yield assertTrue(extractStatus(response) == Status.InternalServerError, bodyString == "error") }, diff --git a/zio-http/jvm/src/test/scala/zio/http/HttpAppMiddlewareSpec.scala b/zio-http/jvm/src/test/scala/zio/http/RoutesMiddlewareSpec.scala similarity index 97% rename from zio-http/jvm/src/test/scala/zio/http/HttpAppMiddlewareSpec.scala rename to zio-http/jvm/src/test/scala/zio/http/RoutesMiddlewareSpec.scala index 32a3903cfc..04aaac657b 100644 --- a/zio-http/jvm/src/test/scala/zio/http/HttpAppMiddlewareSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/RoutesMiddlewareSpec.scala @@ -22,7 +22,7 @@ import zio.test._ import zio.http.Method -object HttpAppMiddlewareSpec extends ZIOHttpSpec with ExitAssertion { +object RoutesMiddlewareSpec extends ZIOHttpSpec with ExitAssertion { def spec: Spec[Any, Any] = suite("HttpAppMiddleware")( diff --git a/zio-http/jvm/src/test/scala/zio/http/HttpAppSpec.scala b/zio-http/jvm/src/test/scala/zio/http/RoutesSpec.scala similarity index 88% rename from zio-http/jvm/src/test/scala/zio/http/HttpAppSpec.scala rename to zio-http/jvm/src/test/scala/zio/http/RoutesSpec.scala index 0079e900bf..c6ca58d121 100644 --- a/zio-http/jvm/src/test/scala/zio/http/HttpAppSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/RoutesSpec.scala @@ -16,23 +16,21 @@ package zio.http -import zio._ -import zio.test.Assertion._ import zio.test._ -object HttpAppSpec extends ZIOHttpSpec { +object RoutesSpec extends ZIOHttpSpec { def extractStatus(response: Response): Status = response.status def spec = suite("HttpAppSpec")( test("empty not found") { - val app = HttpApp.empty + val app = Routes.empty for { result <- app.run() } yield assertTrue(extractStatus(result) == Status.NotFound) }, test("compose empty not found") { - val app = HttpApp.empty ++ HttpApp.empty + val app = Routes.empty ++ Routes.empty for { result <- app.run() @@ -43,7 +41,7 @@ object HttpAppSpec extends ZIOHttpSpec { val app = handler { (req: Request) => Response(body = req.body) - }.toHttpApp + } for { result <- app.runZIO(Request(body = body)) diff --git a/zio-http/jvm/src/test/scala/zio/http/SSLSpec.scala b/zio-http/jvm/src/test/scala/zio/http/SSLSpec.scala index cb19d3e5c6..a75c0234f7 100644 --- a/zio-http/jvm/src/test/scala/zio/http/SSLSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/SSLSpec.scala @@ -33,7 +33,7 @@ object SSLSpec extends ZIOHttpSpec { val payload = Gen.alphaNumericStringBounded(10000, 20000) - val app: HttpApp[Any, Response] = HttpApp( + val app: Routes[Any, Response] = Routes( Method.GET / "success" -> handler(Response.ok), ).sandbox diff --git a/zio-http/jvm/src/test/scala/zio/http/ServerSpec.scala b/zio-http/jvm/src/test/scala/zio/http/ServerSpec.scala index d71d029d40..4ccfc7682b 100644 --- a/zio-http/jvm/src/test/scala/zio/http/ServerSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/ServerSpec.scala @@ -50,20 +50,20 @@ object ServerSpec extends HttpRunnableSpec { def dynamicAppSpec = suite("DynamicAppSpec")( suite("success")( test("status is 200") { - val status = Handler.ok.toHttpApp.deploy.status.run() + val status = Handler.ok.toRoutes.deploy.status.run() assertZIO(status)(equalTo(Status.Ok)) }, test("status is 200") { - val res = Handler.text("ABC").toHttpApp.deploy.status.run() + val res = Handler.text("ABC").toRoutes.deploy.status.run() assertZIO(res)(equalTo(Status.Ok)) }, test("content is set") { - val res = Handler.text("ABC").toHttpApp.deploy.body.mapZIO(_.asString).run() + val res = Handler.text("ABC").toRoutes.deploy.body.mapZIO(_.asString).run() assertZIO(res)(containsString("ABC")) }, ), suite("not found") { - val app = HttpApp.empty + val app = Routes.empty test("status is 404") { val res = app.deploy.status.run() assertZIO(res)(equalTo(Status.NotFound)) @@ -74,76 +74,76 @@ object ServerSpec extends HttpRunnableSpec { } } + suite("error") { - val app = Handler.fail(new Error("SERVER_ERROR")).sandbox.toHttpApp + val routes = Handler.fail(new Error("SERVER_ERROR")).sandbox.toRoutes test("status is 500") { - val res = app.deploy.status.run() + val res = routes.deploy.status.run() assertZIO(res)(equalTo(Status.InternalServerError)) } + test("content is empty") { - val res = app.deploy.body.mapZIO(_.asString).run() + val res = routes.deploy.body.mapZIO(_.asString).run() assertZIO(res)(isEmptyString) } + test("header is set") { - val res = app.deploy.header(Header.ContentLength).run() + val res = routes.deploy.header(Header.ContentLength).run() assertZIO(res)(isSome(anything)) } } + suite("die") { - val app = Handler.die(new Error("SERVER_ERROR")).toHttpApp + val routes = Handler.die(new Error("SERVER_ERROR")).toRoutes test("status is 500") { - val res = app.deploy.status.run() + val res = routes.deploy.status.run() assertZIO(res)(equalTo(Status.InternalServerError)) } + test("content is empty") { - val res = app.deploy.body.mapZIO(_.asString).run() + val res = routes.deploy.body.mapZIO(_.asString).run() assertZIO(res)(isEmptyString) } + test("header is set") { - val res = app.deploy.header(Header.ContentLength).run() + val res = routes.deploy.header(Header.ContentLength).run() assertZIO(res)(isSome(anything)) } } + suite("echo content") { - val app = (RoutePattern.any -> + val routes = (RoutePattern.any -> handler { (_: Path, req: Request) => req.body.asString.map(text => Response.text(text)) - }).sandbox.toHttpApp + }).sandbox.toRoutes test("status is 200") { - val res = app.deploy.status.run() + val res = routes.deploy.status.run() assertZIO(res)(equalTo(Status.Ok)) } + test("body is ok") { - val res = app.deploy.body.mapZIO(_.asString).run(body = Body.fromString("ABC")) + val res = routes.deploy.body.mapZIO(_.asString).run(body = Body.fromString("ABC")) assertZIO(res)(equalTo("ABC")) } + test("empty string") { - val res = app.deploy.body.mapZIO(_.asString).run(body = Body.fromString("")) + val res = routes.deploy.body.mapZIO(_.asString).run(body = Body.fromString("")) assertZIO(res)(equalTo("")) } + test("one char") { - val res = app.deploy.body.mapZIO(_.asString).run(body = Body.fromString("1")) + val res = routes.deploy.body.mapZIO(_.asString).run(body = Body.fromString("1")) assertZIO(res)(equalTo("1")) } + test("data") { val dataStream = ZStream.repeat("A").take(MaxSize.toLong) val app = - HttpApp(RoutePattern.any -> handler((_: Path, req: Request) => Response(body = req.body))) + Routes(RoutePattern.any -> handler((_: Path, req: Request) => Response(body = req.body))) val res = app.deploy.body.mapZIO(_.asChunk.map(_.length)).run(body = Body.fromCharSequenceStreamChunked(dataStream)) assertZIO(res)(equalTo(MaxSize)) } } + suite("headers") { - val app = Handler.ok.addHeader("Foo", "Bar").toHttpApp + val routes = Handler.ok.addHeader("Foo", "Bar").toRoutes test("headers are set") { - val res = app.deploy.rawHeader("Foo").run() + val res = routes.deploy.rawHeader("Foo").run() assertZIO(res)(isSome(equalTo("Bar"))) } } + suite("response") { - val app = Handler.fromResponse(Response(status = Status.Ok, body = Body.fromString("abc"))).toHttpApp + val routes = Handler.fromResponse(Response(status = Status.Ok, body = Body.fromString("abc"))).toRoutes test("body is set") { - val res = app.deploy.body.mapZIO(_.asString).run() + val res = routes.deploy.body.mapZIO(_.asString).run() assertZIO(res)(equalTo("abc")) } } + @@ -151,22 +151,22 @@ object ServerSpec extends HttpRunnableSpec { val body = "some-text" val bodyAsStream = ZStream.fromChunk(Chunk.fromArray(body.getBytes)) - val app = HttpApp( + val routes = Routes( RoutePattern.any -> handler { (_: Path, req: Request) => req.body.asString.map(body => Response.text(body)) }, - ).sandbox.deploy + ).sandbox.deploy.toRoutes.sandbox def roundTrip[R, E <: Throwable]( - app: HttpApp[R, Response], + routes: Routes[R, Response], headers: Headers, contentStream: ZStream[R, E, Byte], compressor: ZPipeline[R, E, Byte, Byte], decompressor: ZPipeline[R, E, Byte, Byte], ) = for { compressed <- contentStream.via(compressor).runCollect - response <- app.run(body = Body.fromChunk(compressed), headers = headers) + response <- routes.run(body = Body.fromChunk(compressed), headers = headers) body <- response.body.asChunk.flatMap(ch => ZStream.fromChunk(ch).via(decompressor).runCollect) } yield new String(body.toArray, StandardCharsets.UTF_8) @@ -188,7 +188,7 @@ object ServerSpec extends HttpRunnableSpec { ), ) { case (contentEncoding, compressor, acceptEncoding, decompressor) => val result = roundTrip( - app.sandbox.toHttpApp, + routes.sandbox, Headers(acceptEncoding, contentEncoding), bodyAsStream, compressor, @@ -198,7 +198,7 @@ object ServerSpec extends HttpRunnableSpec { } } + test("pass through for unsupported accept encoding request") { - val result = app.run( + val result = routes.run( body = Body.fromString(body), headers = Headers(Header.AcceptEncoding.Br()), ) @@ -214,7 +214,7 @@ object ServerSpec extends HttpRunnableSpec { ), ), ) { ae => - val result = app.run( + val result = routes.run( body = Body.fromString(body), headers = Headers(ae), ) @@ -224,16 +224,16 @@ object ServerSpec extends HttpRunnableSpec { } + suite("interruption")( test("interrupt closes the channel without response") { - val app = Handler.fromZIO { + val routes = Handler.fromZIO { ZIO.interrupt.as(Response.text("not interrupted")) - }.toHttpApp - assertZIO(app.deploy.run().exit)( + }.toRoutes + assertZIO(routes.deploy.run().exit)( fails(hasField("class.simpleName", _.getClass.getSimpleName, equalTo("PrematureChannelClosureException"))), ) }, ) + suite("proxy") { - val server = HttpApp( + val server = Routes( Method.ANY / "proxy" / trailing -> handler { (path: Path, req: Request) => val url = URL.decode(s"http://localhost:$port/$path").toOption.get @@ -267,13 +267,12 @@ object ServerSpec extends HttpRunnableSpec { ) def requestSpec = suite("RequestSpec") { - val app: HttpApp[Any, Response] = + val app: Routes[Any, Response] = Routes .singleton(handler { (_: Path, req: Request) => Response.text(req.header(Header.ContentLength).map(_.length).getOrElse(-1).toString) }) .sandbox - .toHttpApp test("has content-length") { check(Gen.alphaNumericString) { string => @@ -285,7 +284,7 @@ object ServerSpec extends HttpRunnableSpec { val app = Routes .singleton(handler { (_: Path, req: Request) => req.body.asChunk.as(Response.ok) }) .sandbox - .toHttpApp + val res = app.deploy.status.run(path = Root, method = Method.POST, body = Body.fromString("some text")) assertZIO(res)(equalTo(Status.Ok)) } + @@ -295,7 +294,7 @@ object ServerSpec extends HttpRunnableSpec { (req.body.asChunk *> req.body.asChunk).as(Response.ok) }) .sandbox - .toHttpApp + val res = app.deploy.status.run(method = Method.POST, body = Body.fromString("some text")) assertZIO(res)(equalTo(Status.Ok)) } @@ -304,12 +303,12 @@ object ServerSpec extends HttpRunnableSpec { def responseSpec = suite("ResponseSpec")( test("data") { check(nonEmptyContent) { case (string, data) => - val res = Handler.fromBody(data).toHttpApp.deploy.body.mapZIO(_.asString).run() + val res = Handler.fromBody(data).toRoutes.deploy.body.mapZIO(_.asString).run() assertZIO(res)(equalTo(string)) } }, test("data from file") { - val res = Handler.fromResource("TestFile.txt").sandbox.toHttpApp.deploy.body.mapZIO(_.asString).run() + val res = Handler.fromResource("TestFile.txt").sandbox.toRoutes.deploy.body.mapZIO(_.asString).run() assertZIO(res)(equalTo("foo\nbar")) }, test("content-type header on file response") { @@ -317,7 +316,7 @@ object ServerSpec extends HttpRunnableSpec { Handler .fromResource("TestFile2.mp4") .sandbox - .toHttpApp + .toRoutes .deploy .header(Header.ContentType) .run() @@ -326,19 +325,19 @@ object ServerSpec extends HttpRunnableSpec { }, test("status") { checkAll(HttpGen.status) { case status => - val res = Handler.status(status).toHttpApp.deploy.status.run() + val res = Handler.status(status).toRoutes.deploy.status.run() assertZIO(res)(equalTo(status)) } }, test("header") { check(HttpGen.header) { header => - val res = Handler.ok.addHeader(header).toHttpApp.deploy.rawHeader(header.headerName).run() + val res = Handler.ok.addHeader(header).toRoutes.deploy.rawHeader(header.headerName).run() assertZIO(res)(isSome(equalTo(header.renderedValue))) } }, test("text streaming") { - val res = Handler.fromStreamChunked(ZStream("a", "b", "c")).sandbox.toHttpApp.deploy.body.mapZIO(_.asString).run() + val res = Handler.fromStreamChunked(ZStream("a", "b", "c")).sandbox.toRoutes.deploy.body.mapZIO(_.asString).run() assertZIO(res)(equalTo("abc")) }, test("echo streaming") { @@ -352,7 +351,6 @@ object ServerSpec extends HttpRunnableSpec { ] }) .sandbox - .toHttpApp .deploy .body .mapZIO(_.asString) @@ -365,7 +363,7 @@ object ServerSpec extends HttpRunnableSpec { Handler .fromStreamChunked(ZStream.fromPath(Paths.get(path))) .sandbox - .toHttpApp + .toRoutes .deploy .body .mapZIO(_.asString) @@ -377,7 +375,7 @@ object ServerSpec extends HttpRunnableSpec { Handler .fromStream(ZStream.fromZIO(ZIO.attempt(throw new Exception("boom"))), 42) .sandbox - .toHttpApp + .toRoutes .deploy .body .mapZIO(_.asString) @@ -390,7 +388,7 @@ object ServerSpec extends HttpRunnableSpec { Handler .fromStreamChunked(ZStream.fromZIO(ZIO.attempt(throw new Exception("boom")))) .sandbox - .toHttpApp + .toRoutes .deploy .body .mapZIO(_.asString) @@ -404,7 +402,7 @@ object ServerSpec extends HttpRunnableSpec { Handler .html(zio.http.template.html(body(div(id := "foo", "bar")))) .sandbox - .toHttpApp + .toRoutes .deploy .body .mapZIO(_.asString) @@ -412,15 +410,15 @@ object ServerSpec extends HttpRunnableSpec { assertZIO(res)(equalTo("""
bar
""")) }, test("content-type") { - val app = Handler.html(zio.http.template.html(body(div(id := "foo", "bar")))).sandbox.toHttpApp - val res = app.deploy.header(Header.ContentType).run() + val app = Handler.html(zio.http.template.html(body(div(id := "foo", "bar")))).sandbox + val res = app.toRoutes.deploy.header(Header.ContentType).run() assertZIO(res)(isSome(equalTo(Header.ContentType(MediaType.text.html)))) }, ), suite("content-length")( suite("string") { test("unicode text") { - val res = Handler.text("äöü").sandbox.toHttpApp.deploy.contentLength.run() + val res = Handler.text("äöü").sandbox.toRoutes.deploy.contentLength.run() assertZIO(res)(isSome(equalTo(Header.ContentLength(6L)))) } + test("already set") { @@ -429,7 +427,7 @@ object ServerSpec extends HttpRunnableSpec { .text("1234567890") .addHeader(Header.ContentLength(4L)) .sandbox - .toHttpApp + .toRoutes .deploy .contentLength .run() @@ -443,7 +441,7 @@ object ServerSpec extends HttpRunnableSpec { val expected = (0 to size) map (_ => Status.Ok) val response = Response.text("abc") for { - actual <- ZIO.foreachPar(0 to size)(_ => Handler.fromResponse(response).toHttpApp.deploy.status.run()) + actual <- ZIO.foreachPar(0 to size)(_ => Handler.fromResponse(response).toRoutes.deploy.status.run()) } yield assertTrue(actual == expected) }, test("update after cache") { @@ -453,7 +451,7 @@ object ServerSpec extends HttpRunnableSpec { actual <- Handler .fromResponse(res) .addHeader(Header.Server(server)) - .toHttpApp + .toRoutes .deploy .header(Header.Server) .run() @@ -464,11 +462,11 @@ object ServerSpec extends HttpRunnableSpec { def requestBodySpec = suite("RequestBodySpec")( test("POST Request stream") { - val app: HttpApp[Any, Response] = Routes.singleton { + val app: Routes[Any, Response] = Routes.singleton { handler { (_: Path, req: Request) => Response(body = Body.fromStreamChunked(req.body.asStream)) } - }.toHttpApp + } check(Gen.alphaNumericString) { c => assertZIO(app.deploy.body.mapZIO(_.asString).run(path = Root, method = Method.POST, body = Body.fromString(c)))( @@ -479,17 +477,17 @@ object ServerSpec extends HttpRunnableSpec { ) def serverErrorSpec = suite("ServerErrorSpec") { - val app = Handler.fail(new Error("SERVER_ERROR")).sandbox.toHttpApp + val routes = Handler.fail(new Error("SERVER_ERROR")).sandbox.toRoutes test("status is 500") { - val res = app.deploy.status.run() + val res = routes.deploy.status.run() assertZIO(res)(equalTo(Status.InternalServerError)) } + test("content is empty") { - val res = app.deploy.body.mapZIO(_.asString).run() + val res = routes.deploy.body.mapZIO(_.asString).run() assertZIO(res)(isEmptyString) } + test("header is set") { - val res = app.deploy.headers.run().map(_.header(Header.ContentLength)) + val res = routes.deploy.headers.run().map(_.header(Header.ContentLength)) assertZIO(res)(isSome(anything)) } } diff --git a/zio-http/jvm/src/test/scala/zio/http/ServerStartSpec.scala b/zio-http/jvm/src/test/scala/zio/http/ServerStartSpec.scala index 225751a0b9..d2806b0d12 100644 --- a/zio-http/jvm/src/test/scala/zio/http/ServerStartSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/ServerStartSpec.scala @@ -30,7 +30,7 @@ object ServerStartSpec extends HttpRunnableSpec { test("desired port") { val port = 8088 val config = Server.Config.default.port(port) - serve(HttpApp.empty).flatMap { port => + serve(Routes.empty).flatMap { port => assertZIO(ZIO.attempt(port))(equalTo(port)) }.provide( ZLayer.succeed(config), @@ -42,7 +42,7 @@ object ServerStartSpec extends HttpRunnableSpec { test("available port") { val port = 0 val config = Server.Config.default.port(port) - serve(HttpApp.empty).flatMap { bindPort => + serve(Routes.empty).flatMap { bindPort => assertZIO(ZIO.attempt(bindPort))(not(equalTo(port))) }.provide( ZLayer.succeed(config), diff --git a/zio-http/jvm/src/test/scala/zio/http/StaticFileServerSpec.scala b/zio-http/jvm/src/test/scala/zio/http/StaticFileServerSpec.scala index 7499ab3c3f..3c5cecfcf2 100644 --- a/zio-http/jvm/src/test/scala/zio/http/StaticFileServerSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/StaticFileServerSpec.scala @@ -20,24 +20,24 @@ import java.io.File import zio._ import zio.test.Assertion._ -import zio.test.TestAspect.{timeout, unix, withLiveClock} +import zio.test.TestAspect.{unix, withLiveClock} import zio.test.assertZIO import zio.http.internal.{DynamicServer, HttpRunnableSpec, serverTestLayer} object StaticFileServerSpec extends HttpRunnableSpec { - private val fileOk = Handler.fromResource("TestFile.txt").sandbox.toHttpApp.deploy - private val fileNotFound = Handler.fromResource("Nothing").sandbox.toHttpApp.deploy + private val fileOk = Handler.fromResource("TestFile.txt").sandbox.toRoutes.deploy + private val fileNotFound = Handler.fromResource("Nothing").sandbox.toRoutes.deploy private val testArchivePath = getClass.getResource("/TestArchive.jar").getPath private val resourceOk = - Handler.fromResourceWithURL(new java.net.URL(s"jar:file:$testArchivePath!/TestFile.txt")).sandbox.toHttpApp.deploy + Handler.fromResourceWithURL(new java.net.URL(s"jar:file:$testArchivePath!/TestFile.txt")).sandbox.toRoutes.deploy private val resourceNotFound = Handler .fromResourceWithURL(new java.net.URL(s"jar:file:$testArchivePath!/NonExistent.txt")) .sandbox - .toHttpApp + .toRoutes .deploy override def spec = suite("StaticFileServerSpec") { @@ -72,7 +72,7 @@ object StaticFileServerSpec extends HttpRunnableSpec { suite("fromFile")( suite("failure on construction")( test("should respond with 500") { - val res = Handler.fromFile(throw new Error("Wut happened?")).sandbox.toHttpApp.deploy.run().map(_.status) + val res = Handler.fromFile(throw new Error("Wut happened?")).sandbox.toRoutes.deploy.run().map(_.status) assertZIO(res)(equalTo(Status.InternalServerError)) }, ), @@ -80,7 +80,7 @@ object StaticFileServerSpec extends HttpRunnableSpec { test("should respond with 500") { val tmpFile = File.createTempFile("test", "txt") tmpFile.setReadable(false) - val res = Handler.fromFile(tmpFile).sandbox.toHttpApp.deploy.run().map(_.status) + val res = Handler.fromFile(tmpFile).sandbox.toRoutes.deploy.run().map(_.status) assertZIO(res)(equalTo(Status.Forbidden)) } @@ unix, ), @@ -89,7 +89,7 @@ object StaticFileServerSpec extends HttpRunnableSpec { final class BadFile(name: String) extends File(name) { override def exists(): Boolean = throw new Error("Haha") } - val res = Handler.fromFile(new BadFile("Length Failure")).sandbox.toHttpApp.deploy.run().map(_.status) + val res = Handler.fromFile(new BadFile("Length Failure")).sandbox.toRoutes.deploy.run().map(_.status) assertZIO(res)(equalTo(Status.InternalServerError)) }, ), diff --git a/zio-http/jvm/src/test/scala/zio/http/StaticServerSpec.scala b/zio-http/jvm/src/test/scala/zio/http/StaticServerSpec.scala index 71203845f2..1d718404d9 100644 --- a/zio-http/jvm/src/test/scala/zio/http/StaticServerSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/StaticServerSpec.scala @@ -27,7 +27,7 @@ import zio.http.internal.{DynamicServer, HttpGen, HttpRunnableSpec, serverTestLa object StaticServerSpec extends HttpRunnableSpec { - private val staticApp = HttpApp( + private val staticApp = Routes( Method.GET / "success" -> handler(Response.ok), Method.GET / "failure" -> handler(ZIO.fail(new RuntimeException("FAILURE"))), Method.GET / "die" -> handler(ZIO.die(new RuntimeException("DIE"))), @@ -35,17 +35,17 @@ object StaticServerSpec extends HttpRunnableSpec { ).sandbox // Use this route to test anything that doesn't require ZIO related computations. - private val nonZIO = HttpApp( + private val nonZIO = Routes( Method.ANY / "ExitSuccess" -> handler(Exit.succeed(Response.ok)), Method.ANY / "ExitFailure" -> handler(Exit.fail(new RuntimeException("FAILURE"))), Method.ANY / "throwable" -> handlerTODO("Throw inside Handler"), ).sandbox - private val staticAppWithCors = HttpApp( + private val staticAppWithCors = Routes( Method.GET / "success-cors" -> handler(Response.ok.addHeader(Header.Vary("test1", "test2"))), ) @@ cors(CorsConfig(allowedMethods = AccessControlAllowMethods(Method.GET, Method.POST))) - private val combined: HttpApp[Any, Response] = nonZIO ++ staticApp ++ staticAppWithCors + private val combined: Routes[Any, Response] = nonZIO ++ staticApp ++ staticAppWithCors private val app = serve { combined } diff --git a/zio-http/jvm/src/test/scala/zio/http/StatusSpec.scala b/zio-http/jvm/src/test/scala/zio/http/StatusSpec.scala index a25fc3f24f..c4a63ab955 100644 --- a/zio-http/jvm/src/test/scala/zio/http/StatusSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/StatusSpec.scala @@ -42,7 +42,7 @@ object StatusSpec extends ZIOHttpSpec { suite("toHttpApp")( test("status") { checkAll(statusGen) { case status => - val res = status.toHttpApp.runZIO(Request.get(URL.empty)) + val res = status.toRoutes.runZIO(Request.get(URL.empty)) assertZIO(res.map(_.status))(equalTo(status)) } }, diff --git a/zio-http/jvm/src/test/scala/zio/http/WebSocketConfig.scala b/zio-http/jvm/src/test/scala/zio/http/WebSocketConfig.scala index de83ee1f88..7c7936b287 100644 --- a/zio-http/jvm/src/test/scala/zio/http/WebSocketConfig.scala +++ b/zio-http/jvm/src/test/scala/zio/http/WebSocketConfig.scala @@ -42,7 +42,7 @@ object WebSocketConfigSpec extends HttpRunnableSpec { channel.send(closeFrame) case _ => ZIO.unit } - }.toHttpAppWS + }.toRoutes } res <- ZIO.scoped { diff --git a/zio-http/jvm/src/test/scala/zio/http/WebSocketSpec.scala b/zio-http/jvm/src/test/scala/zio/http/WebSocketSpec.scala index b68d266c87..2df18f2628 100644 --- a/zio-http/jvm/src/test/scala/zio/http/WebSocketSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/WebSocketSpec.scala @@ -39,7 +39,7 @@ object WebSocketSpec extends HttpRunnableSpec { case event @ Unregistered => msg.add(event, true) case event => msg.add(event) } - }.toHttpAppWS + }.toRoutes } res <- ZIO.scoped { @@ -85,7 +85,7 @@ object WebSocketSpec extends HttpRunnableSpec { case _ => ZIO.unit } - }.toHttpAppWS.deployWS + }.toRoutes.deployWS // Setup Client // Client closes the connection after 1 second @@ -112,7 +112,7 @@ object WebSocketSpec extends HttpRunnableSpec { } @@ nonFlaky, test("Multiple websocket upgrades") { val app = - Handler.webSocket(channel => channel.send(ChannelEvent.Read(WebSocketFrame.text("BAR")))).toHttpAppWS.deployWS + Handler.webSocket(channel => channel.send(ChannelEvent.Read(WebSocketFrame.text("BAR")))).toRoutes.deployWS val codes = ZIO .foreach(1 to 1024)(_ => app.runZIO(WebSocketApp.unit).map(_.status)) .map(_.count(_ == Status.SwitchingProtocols)) @@ -130,7 +130,7 @@ object WebSocketSpec extends HttpRunnableSpec { case event @ Unregistered => msg.add(event, true) case event => msg.add(event) } - }.toHttpAppWS + }.toRoutes } res <- ZIO.scoped { @@ -179,7 +179,7 @@ object WebSocketSpec extends HttpRunnableSpec { case _ => ZIO.unit } } - }.toHttpAppWS + }.toRoutes } queue1 <- Queue.unbounded[String] diff --git a/zio-http/jvm/src/test/scala/zio/http/ZClientAspectSpec.scala b/zio-http/jvm/src/test/scala/zio/http/ZClientAspectSpec.scala index fc0501667b..88f7da5c8b 100644 --- a/zio-http/jvm/src/test/scala/zio/http/ZClientAspectSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/ZClientAspectSpec.scala @@ -26,19 +26,17 @@ import zio.http.netty.NettyConfig object ZClientAspectSpec extends ZIOHttpSpec { def extractStatus(response: Response): Status = response.status - val app: HttpApp[Any, Response] = { - Route.handled(Method.GET / "hello")(Handler.fromResponse(Response.text("hello"))) - }.toHttpApp + val routes: Routes[Any, Response] = + Route.handled(Method.GET / "hello")(Handler.fromResponse(Response.text("hello"))).toRoutes - val redir: HttpApp[Any, Response] = { - Route.handled(Method.GET / "redirect")(Handler.fromResponse(Response.redirect(URL.empty / "hello"))) - }.toHttpApp + val redir: Routes[Any, Response] = + Route.handled(Method.GET / "redirect")(Handler.fromResponse(Response.redirect(URL.empty / "hello"))).toRoutes override def spec: Spec[TestEnvironment with Scope, Any] = suite("ZClientAspect")( test("debug") { for { - port <- Server.install(app) + port <- Server.install(routes) baseClient <- ZIO.service[Client] client = baseClient.url( URL(Path.empty, Location.Absolute(Scheme.HTTP, "localhost", Some(port))), @@ -54,7 +52,7 @@ object ZClientAspectSpec extends ZIOHttpSpec { }, test("requestLogging")( for { - port <- Server.install(app) + port <- Server.install(routes) baseClient <- ZIO.service[Client] client = baseClient .url( @@ -87,7 +85,7 @@ object ZClientAspectSpec extends ZIOHttpSpec { ), test("followRedirects")( for { - port <- Server.install(redir ++ app) + port <- Server.install(redir ++ routes) baseClient <- ZIO.service[Client] client = baseClient .url( diff --git a/zio-http/jvm/src/test/scala/zio/http/endpoint/BadRequestSpec.scala b/zio-http/jvm/src/test/scala/zio/http/endpoint/BadRequestSpec.scala index 504a32630e..009c6a56e3 100644 --- a/zio-http/jvm/src/test/scala/zio/http/endpoint/BadRequestSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/endpoint/BadRequestSpec.scala @@ -18,7 +18,6 @@ object BadRequestSpec extends ZIOSpecDefault { .query(QueryCodec.queryInt("age")) .out[Unit] val route = endpoint.implement(handler((_: Int) => ())) - val app = route.toHttpApp val request = Request(method = Method.GET, url = url"/test?age=1&age=2").addHeader(Header.Accept(MediaType.text.`html`)) val expectedBody = @@ -31,7 +30,7 @@ object BadRequestSpec extends ZIOSpecDefault { ), ) for { - response <- app.runZIO(request) + response <- route.toRoutes.runZIO(request) body <- response.body.asString } yield assertTrue(body == expectedBody.encode.toString) }, @@ -40,14 +39,13 @@ object BadRequestSpec extends ZIOSpecDefault { .query(QueryCodec.queryInt("age")) .out[Unit] val route = endpoint.implement(handler((_: Int) => ())) - val app = route.toHttpApp val request = Request(method = Method.GET, url = url"/test?age=1&age=2") .addHeader(Header.Accept(MediaType.application.json)) val expectedBody = """{"name":"SchemaTransformationFailure","message":"Expected single value for query parameter age, but got 2 instead"}""" for { - response <- app.runZIO(request) + response <- route.toRoutes.runZIO(request) body <- response.body.asString } yield assertTrue(body == expectedBody) }, @@ -56,14 +54,13 @@ object BadRequestSpec extends ZIOSpecDefault { .query(QueryCodec.queryInt("age")) .out[Unit] val route = endpoint.implement(handler((_: Int) => ())) - val app = route.toHttpApp val request = Request(method = Method.GET, url = url"/test?age=1&age=2") .addHeader(Header.Accept(MediaType.application.`atf`)) val expectedBody = """{"name":"SchemaTransformationFailure","message":"Expected single value for query parameter age, but got 2 instead"}""" for { - response <- app.runZIO(request) + response <- route.toRoutes.runZIO(request) body <- response.body.asString } yield assertTrue(body == expectedBody) }, @@ -73,13 +70,12 @@ object BadRequestSpec extends ZIOSpecDefault { .out[Unit] .emptyErrorResponse val route = endpoint.implement(handler((_: Int) => ())) - val app = route.toHttpApp val request = Request(method = Method.GET, url = url"/test?age=1&age=2") .addHeader(Header.Accept(MediaType.application.`atf`)) val expectedBody = "" for { - response <- app.runZIO(request) + response <- route.toRoutes.runZIO(request) body <- response.body.asString } yield assertTrue(body == expectedBody) }, @@ -88,14 +84,13 @@ object BadRequestSpec extends ZIOSpecDefault { .query(QueryCodec.queryInt("age")) .out[Unit] val route = endpoint.implement(handler((_: Int) => ())) - val app = route.toHttpApp val request = Request(method = Method.GET, url = url"/test?age=1&age=2") .addHeader(Header.Accept(MediaType.application.json)) val expectedBody = """{"name":"SchemaTransformationFailure","message":"Expected single value for query parameter age, but got 2 instead"}""" for { - response <- app.runZIO(request) + response <- route.toRoutes.runZIO(request) body <- response.body.asString } yield assertTrue(body == expectedBody) }, @@ -105,14 +100,13 @@ object BadRequestSpec extends ZIOSpecDefault { .out[Unit] .outCodecError(default) val route = endpoint.implement(handler((_: Int) => ())) - val app = route.toHttpApp val request = Request(method = Method.GET, url = url"/test?age=1&age=2") .addHeader(Header.Accept(MediaType.application.json)) val expectedBody = """{"name2":"SchemaTransformationFailure","message2":"Expected single value for query parameter age, but got 2 instead"}""" for { - response <- app.runZIO(request) + response <- route.toRoutes.runZIO(request) body <- response.body.asString } yield assertTrue(body == expectedBody) }, diff --git a/zio-http/jvm/src/test/scala/zio/http/endpoint/CustomErrorSpec.scala b/zio-http/jvm/src/test/scala/zio/http/endpoint/CustomErrorSpec.scala index 50dc2afec6..3f096d8a96 100644 --- a/zio-http/jvm/src/test/scala/zio/http/endpoint/CustomErrorSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/endpoint/CustomErrorSpec.scala @@ -16,25 +16,17 @@ package zio.http.endpoint -import java.time.Instant - import zio._ import zio.test._ -import zio.stream.ZStream - import zio.schema.annotation.validate -import zio.schema.codec.{DecodeError, JsonCodec} import zio.schema.validation.Validation -import zio.schema.{DeriveSchema, Schema, StandardType} +import zio.schema.{DeriveSchema, Schema} -import zio.http.Header.ContentType import zio.http.Method._ import zio.http._ -import zio.http.codec.HttpCodec.{query, queryInt} import zio.http.codec._ import zio.http.endpoint.EndpointSpec.extractStatus -import zio.http.forms.Fixtures.formField object CustomErrorSpec extends ZIOHttpSpec { def spec = suite("CustomErrorSpec")( @@ -49,6 +41,7 @@ object CustomErrorSpec extends ZIOHttpSpec { ZIO.fail(s"path(users, $userId)") } } + .toRoutes val request = Request .get( @@ -56,7 +49,7 @@ object CustomErrorSpec extends ZIOHttpSpec { ) for { - response <- routes.toHttpApp.runZIO(request) + response <- routes.runZIO(request) body <- response.body.asString.orDie } yield assertTrue(extractStatus(response).code == customCode, body == s""""path(users, $userId)"""") } @@ -76,15 +69,16 @@ object CustomErrorSpec extends ZIOHttpSpec { else ZIO.fail(TestError.UnexpectedError("something went wrong")) } } + .toRoutes val request1 = Request.get(URL.decode(s"/users/$myUserId").toOption.get) val request2 = Request.get(URL.decode(s"/users/$invalidUserId").toOption.get) for { - response1 <- routes.toHttpApp.runZIO(request1) + response1 <- routes.runZIO(request1) body1 <- response1.body.asString.orDie - response2 <- routes.toHttpApp.runZIO(request2) + response2 <- routes.runZIO(request2) body2 <- response2.body.asString.orDie } yield assertTrue( extractStatus(response1) == Status.NotFound, @@ -111,14 +105,15 @@ object CustomErrorSpec extends ZIOHttpSpec { .handleErrorCause { cause => Response.text("Caught: " + cause.defects.headOption.fold("no known cause")(d => d.getMessage)) } + .toRoutes val request1 = Request.post(URL.decode("/users").toOption.get, Body.fromString("""{"id":0}""")) val request2 = Request.post(URL.decode("/users").toOption.get, Body.fromString(s"""{"id":$userId}""")) for { - response1 <- routes.toHttpApp.runZIO(request1) + response1 <- routes.runZIO(request1) body1 <- response1.body.asString.orDie - response2 <- routes.toHttpApp.runZIO(request2) + response2 <- routes.runZIO(request2) body2 <- response2.body.asString.orDie } yield assertTrue( extractStatus(response1) == Status.BadRequest, diff --git a/zio-http/jvm/src/test/scala/zio/http/endpoint/EndpointSpec.scala b/zio-http/jvm/src/test/scala/zio/http/endpoint/EndpointSpec.scala index 5f231e72b5..b94e6adea5 100644 --- a/zio-http/jvm/src/test/scala/zio/http/endpoint/EndpointSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/endpoint/EndpointSpec.scala @@ -28,13 +28,13 @@ import zio.http._ object EndpointSpec extends ZIOHttpSpec { def spec = suite("EndpointSpec")() - def testEndpoint[R](service: HttpApp[R, Nothing])( + def testEndpoint[R](service: Routes[R, Nothing])( url: String, expected: String, ): ZIO[R, Response, TestResult] = testEndpointWithHeaders(service)(url, headers = List.empty, expected) - def testEndpointWithHeaders[R](service: HttpApp[R, Nothing])( + def testEndpointWithHeaders[R](service: Routes[R, Nothing])( url: String, headers: List[(String, String)], expected: String, diff --git a/zio-http/jvm/src/test/scala/zio/http/endpoint/MultipartSpec.scala b/zio-http/jvm/src/test/scala/zio/http/endpoint/MultipartSpec.scala index 80d7c5988a..e8d390e9b9 100644 --- a/zio-http/jvm/src/test/scala/zio/http/endpoint/MultipartSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/endpoint/MultipartSpec.scala @@ -60,7 +60,7 @@ object MultipartSpec extends ZIOHttpSpec { ), ) } - result <- route.toHttpApp.runZIO(Request.get(URL.decode("/test-form").toOption.get)).exit + result <- route.toRoutes.runZIO(Request.get(URL.decode("/test-form").toOption.get)).exit response <- result match { case Exit.Success(value) => ZIO.succeed(value) case Exit.Failure(cause) => @@ -116,7 +116,7 @@ object MultipartSpec extends ZIOHttpSpec { ), ) } - result <- route.toHttpApp.runZIO(Request.get(URL.decode("/test-form").toOption.get)).exit + result <- route.toRoutes.runZIO(Request.get(URL.decode("/test-form").toOption.get)).exit response <- result match { case Exit.Success(value) => ZIO.succeed(value) case Exit.Failure(cause) => @@ -167,7 +167,7 @@ object MultipartSpec extends ZIOHttpSpec { FormField.binaryField("uploaded-image", bytes, MediaType.image.png), ) boundary <- Boundary.randomUUID - result <- route.toHttpApp + result <- route.toRoutes .runZIO( Request.post(URL.decode("/test-form").toOption.get, Body.fromMultipartForm(form, boundary)), ) @@ -243,7 +243,7 @@ object MultipartSpec extends ZIOHttpSpec { for { boundary <- Boundary.randomUUID - result <- route.toHttpApp + result <- route.toRoutes .runZIO( Request.post(URL.decode("/test-form").toOption.get, Body.fromMultipartForm(form, boundary)), ) diff --git a/zio-http/jvm/src/test/scala/zio/http/endpoint/NotFoundSpec.scala b/zio-http/jvm/src/test/scala/zio/http/endpoint/NotFoundSpec.scala index 3440b617a0..1594d5fb6b 100644 --- a/zio-http/jvm/src/test/scala/zio/http/endpoint/NotFoundSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/endpoint/NotFoundSpec.scala @@ -40,7 +40,7 @@ object NotFoundSpec extends ZIOHttpSpec { test("on wrong path") { check(Gen.int) { userId => val testRoutes = test404( - HttpApp( + Routes( Endpoint(GET / "users" / int("userId")) .out[String] .implement { @@ -65,7 +65,7 @@ object NotFoundSpec extends ZIOHttpSpec { test("on wrong method") { check(Gen.int, Gen.int, Gen.alphaNumericString) { (userId, postId, name) => val testRoutes = test404( - HttpApp( + Routes( Endpoint(GET / "users" / int("userId")) .out[String] .implement { @@ -89,7 +89,7 @@ object NotFoundSpec extends ZIOHttpSpec { }, ) - def test404[R](service: HttpApp[R, Nothing])( + def test404[R](service: Routes[R, Nothing])( url: String, method: Method, ): ZIO[R, Response, TestResult] = { diff --git a/zio-http/jvm/src/test/scala/zio/http/endpoint/QueryParameterSpec.scala b/zio-http/jvm/src/test/scala/zio/http/endpoint/QueryParameterSpec.scala index ea82190c04..884fbb99bf 100644 --- a/zio-http/jvm/src/test/scala/zio/http/endpoint/QueryParameterSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/endpoint/QueryParameterSpec.scala @@ -31,7 +31,7 @@ object QueryParameterSpec extends ZIOHttpSpec { test("simple request with query parameter") { check(Gen.int, Gen.int, Gen.alphaNumericString) { (userId, postId, username) => val testRoutes = testEndpoint( - HttpApp( + Routes( Endpoint(GET / "users" / int("userId")) .out[String] .implement { @@ -59,7 +59,7 @@ object QueryParameterSpec extends ZIOHttpSpec { test("optional query parameter") { check(Gen.int, Gen.alphaNumericString) { (userId, details) => val testRoutes = testEndpoint( - HttpApp( + Routes( Endpoint(GET / "users" / int("userId")) .query(query("details").optional) .out[String] @@ -78,7 +78,7 @@ object QueryParameterSpec extends ZIOHttpSpec { test("multiple optional query parameters") { check(Gen.int, Gen.alphaNumericString, Gen.alphaNumericString) { (userId, key, value) => val testRoutes = testEndpoint( - HttpApp( + Routes( Endpoint(GET / "users" / int("userId")) .query(query("key").optional) .query(query("value").optional) @@ -99,7 +99,7 @@ object QueryParameterSpec extends ZIOHttpSpec { test("query parameters with multiple values") { check(Gen.int, Gen.listOfN(3)(Gen.alphaNumericString)) { (userId, keys) => val testRoutes = testEndpoint( - HttpApp( + Routes( Endpoint(GET / "users" / int("userId")) .query(queryAll("key")) .out[String] @@ -128,7 +128,7 @@ object QueryParameterSpec extends ZIOHttpSpec { test("optional query parameters with multiple values") { check(Gen.int, Gen.listOfN(3)(Gen.alphaNumericString)) { (userId, keys) => val testRoutes = testEndpoint( - HttpApp( + Routes( Endpoint(GET / "users" / int("userId")) .query(queryAll("key").optional) .out[String] @@ -158,7 +158,7 @@ object QueryParameterSpec extends ZIOHttpSpec { check(Gen.int, Gen.listOfN(3)(Gen.alphaNumericString), Gen.listOfN(2)(Gen.alphaNumericString)) { (userId, keys, values) => val testRoutes = testEndpoint( - HttpApp( + Routes( Endpoint(GET / "users" / int("userId")) .query(queryAll("key") & queryAll("value")) .out[String] @@ -183,7 +183,7 @@ object QueryParameterSpec extends ZIOHttpSpec { test("mix of multi value and single value query parameters") { check(Gen.int, Gen.listOfN(2)(Gen.alphaNumericString), Gen.alphaNumericString) { (userId, multi, single) => val testRoutes = testEndpoint( - HttpApp( + Routes( Endpoint(GET / "users" / int("userId")) .query(queryAll("multi") & query("single")) .out[String] @@ -204,7 +204,7 @@ object QueryParameterSpec extends ZIOHttpSpec { test("either of two multi value query parameters") { check(Gen.int, Gen.listOfN(2)(Gen.alphaNumericString), Gen.listOfN(2)(Gen.boolean)) { (userId, left, right) => val testRoutes = testEndpoint( - HttpApp( + Routes( Endpoint(GET / "users" / int("userId")) .query(queryAll("left") | queryAllBool("right")) .out[String] @@ -234,7 +234,7 @@ object QueryParameterSpec extends ZIOHttpSpec { check(Gen.int, Gen.listOfN(2)(Gen.alphaNumericString), Gen.listOfN(2)(Gen.alphaNumericString)) { (userId, left, right) => val testRoutes = testEndpoint( - HttpApp( + Routes( Endpoint(GET / "users" / int("userId")) .query(queryAll("left") | queryAll("right")) .out[String] @@ -263,7 +263,7 @@ object QueryParameterSpec extends ZIOHttpSpec { test("either of multi value or single value query parameter") { check(Gen.int, Gen.listOfN(2)(Gen.alphaNumericString), Gen.alphaNumericString) { (userId, left, right) => val testRoutes = testEndpoint( - HttpApp( + Routes( Endpoint(GET / "users" / int("userId")) .query(queryAll("left") | query("right")) .out[String] @@ -291,7 +291,7 @@ object QueryParameterSpec extends ZIOHttpSpec { }, test("query parameters keys without values for multi value query") { val testRoutes = testEndpoint( - HttpApp( + Routes( Endpoint(GET / "users") .query(queryAllInt("ints")) .out[String] @@ -309,7 +309,7 @@ object QueryParameterSpec extends ZIOHttpSpec { ) }, test("no specified query parameters for multi value query") { - val testRoutes = HttpApp( + val testRoutes = Routes( Endpoint(GET / "users") .query(queryAllInt("ints")) .out[String] @@ -326,7 +326,7 @@ object QueryParameterSpec extends ZIOHttpSpec { }, test("multiple query parameter values to single value query parameter codec") { val testRoutes = - HttpApp( + Routes( Endpoint(GET / "users") .query(queryInt("ints")) .out[String] diff --git a/zio-http/jvm/src/test/scala/zio/http/endpoint/RequestSpec.scala b/zio-http/jvm/src/test/scala/zio/http/endpoint/RequestSpec.scala index 17e70e5d13..a5531366b2 100644 --- a/zio-http/jvm/src/test/scala/zio/http/endpoint/RequestSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/endpoint/RequestSpec.scala @@ -36,7 +36,7 @@ object RequestSpec extends ZIOHttpSpec { test("simple request with header") { check(Gen.int, Gen.int, Gen.uuid) { (userId, postId, correlationId) => val testRoutes = testEndpointWithHeaders( - HttpApp( + Routes( Endpoint(GET / "users" / int("userId")) .header(HeaderCodec.name[java.util.UUID]("X-Correlation-ID")) .out[String] @@ -79,7 +79,7 @@ object RequestSpec extends ZIOHttpSpec { } for { - response <- routes.toHttpApp.runZIO( + response <- routes.toRoutes.runZIO( Request .get(URL.decode(s"/posts?id=$id").toOption.get) .addHeader(Header.Accept(MediaType.text.`plain`)), @@ -101,7 +101,7 @@ object RequestSpec extends ZIOHttpSpec { } for { - response <- routes.toHttpApp.runZIO( + response <- routes.toRoutes.runZIO( Request .get(URL.decode(s"/posts?id=$id").toOption.get) .addHeader(Header.Accept(MediaType.application.`json`, MediaType.text.`plain`)), @@ -123,7 +123,7 @@ object RequestSpec extends ZIOHttpSpec { } for { - response <- routes.toHttpApp.runZIO( + response <- routes.toRoutes.runZIO( Request.get(URL.decode(s"/posts?id=$id").toOption.get), ) } yield assertTrue(extractStatus(response).code == 404) @@ -138,7 +138,7 @@ object RequestSpec extends ZIOHttpSpec { .out[Int] val routes = endpoint.implement { Handler.succeed(id) } for { - response <- routes.toHttpApp.runZIO( + response <- routes.toRoutes.runZIO( Request.get(url"/posts?id=$notAnId").addHeader(Header.Accept(MediaType.application.`json`)), ) contentType = response.header(Header.ContentType) @@ -160,7 +160,7 @@ object RequestSpec extends ZIOHttpSpec { } for { - response <- routes.toHttpApp.runZIO( + response <- routes.toRoutes.runZIO( Request.get(URL.decode(s"/posts").toOption.get).addHeader("X-Correlation-ID", notACorrelationId), ) } yield assertTrue(extractStatus(response).code == 400) @@ -179,7 +179,7 @@ object RequestSpec extends ZIOHttpSpec { } for { - response <- routes.toHttpApp.runZIO( + response <- routes.toRoutes.runZIO( Request.get(URL.decode(s"/posts").toOption.get), ) } yield assertTrue(extractStatus(response).code == 400) @@ -188,7 +188,7 @@ object RequestSpec extends ZIOHttpSpec { test("out of order api") { check(Gen.int, Gen.int, Gen.alphaNumericString, Gen.int(1, Int.MaxValue)) { (userId, postId, name, age) => val testRoutes = testEndpoint( - HttpApp( + Routes( Endpoint(GET / "users" / int("userId")) .out[String] .implement { @@ -217,7 +217,7 @@ object RequestSpec extends ZIOHttpSpec { test("fallback") { check(Gen.int, Gen.alphaNumericString) { (userId, username) => val testRoutes = testEndpoint( - HttpApp( + Routes( Endpoint(GET / "users") .query(queryInt("userId") | query("userId")) .out[String] @@ -356,7 +356,7 @@ object RequestSpec extends ZIOHttpSpec { } val testRoutes = testEndpoint( - HttpApp( + Routes( broadUsers, broadUsersId, boardUsersPosts, @@ -407,7 +407,7 @@ object RequestSpec extends ZIOHttpSpec { check(Gen.alphaNumericString, Gen.alphaNumericString) { (queryValue, headerValue) => val headerOrQuery = HeaderCodec.name[String]("X-Header") | QueryCodec.query("header") val endpoint = Endpoint(GET / "test").out[String].inCodec(headerOrQuery) - val routes = endpoint.implement(Handler.identity) + val routes = endpoint.implement(Handler.identity).toRoutes val request = Request.get( URL .decode(s"/test?header=$queryValue") @@ -425,11 +425,11 @@ object RequestSpec extends ZIOHttpSpec { .addHeader("X-Header", headerValue) for { - response <- routes.toHttpApp.runZIO(request) + response <- routes.runZIO(request) onlyQuery <- response.body.asString.orDie - response <- routes.toHttpApp.runZIO(requestWithHeader) + response <- routes.runZIO(requestWithHeader) onlyHeader <- response.body.asString.orDie - response <- routes.toHttpApp.runZIO(requestWithHeaderAndQuery) + response <- routes.runZIO(requestWithHeaderAndQuery) headerAndQuery <- response.body.asString.orDie } yield assertTrue( onlyQuery == s""""$queryValue"""", @@ -446,7 +446,7 @@ object RequestSpec extends ZIOHttpSpec { Handler.fromFunction { created => if (created) Right(()) else Left("not created") } - } + }.toRoutes val requestCreated = Request.get( URL .decode("/test?Created=true") @@ -461,9 +461,9 @@ object RequestSpec extends ZIOHttpSpec { ) for { - notCreated <- routes.toHttpApp.runZIO(requestNotCreated) + notCreated <- routes.runZIO(requestNotCreated) header = notCreated.rawHeader("X-Header").get - response <- routes.toHttpApp.runZIO(requestCreated) + response <- routes.runZIO(requestCreated) value = header == "not created" && extractStatus(notCreated) == Status.Ok && extractStatus(response) == Status.Created @@ -481,7 +481,7 @@ object RequestSpec extends ZIOHttpSpec { .in[NewPost](Doc.p("New post")) .out[PostCreated](Status.Created, MediaType.application.`json`) val routes = - endpoint.implement(Handler.succeed(PostCreated(postId))) + endpoint.implement(Handler.succeed(PostCreated(postId))).toRoutes val request = Request .post( @@ -490,7 +490,7 @@ object RequestSpec extends ZIOHttpSpec { ) for { - response <- routes.toHttpApp.runZIO(request) + response <- routes.runZIO(request) code = extractStatus(response) contentType = response.header(Header.ContentType) body <- response.body.asString.orDie @@ -509,10 +509,10 @@ object RequestSpec extends ZIOHttpSpec { .in[NewPost] .out[Int] val routes = - endpoint.implement(Handler.succeed(postId)) + endpoint.implement(Handler.succeed(postId)).toRoutes for { - response <- routes.toHttpApp.runZIO( + response <- routes.runZIO( Request .post( URL.decode("/posts").toOption.get, @@ -529,9 +529,10 @@ object RequestSpec extends ZIOHttpSpec { val route = Endpoint(GET / "test-byte-stream") .outStream[Byte](Doc.p("Test data")) .implement(Handler.succeed(ZStream.fromChunk(bytes).rechunk(16))) + .toRoutes for { - result <- route.toHttpApp.runZIO(Request.get(URL.decode("/test-byte-stream").toOption.get)).exit + result <- route.runZIO(Request.get(URL.decode("/test-byte-stream").toOption.get)).exit response <- result match { case Exit.Success(value) => ZIO.succeed(value) case Exit.Failure(cause) => @@ -552,9 +553,10 @@ object RequestSpec extends ZIOHttpSpec { val route = Endpoint(GET / "test-byte-stream") .outStream[Byte](Status.Ok, MediaType.image.png) .implement(Handler.succeed(ZStream.fromChunk(bytes).rechunk(16))) + .toRoutes for { - result <- route.toHttpApp.runZIO(Request.get(URL.decode("/test-byte-stream").toOption.get)).exit + result <- route.runZIO(Request.get(URL.decode("/test-byte-stream").toOption.get)).exit response <- result match { case Exit.Success(value) => ZIO.succeed(value) case Exit.Failure(cause) => @@ -580,9 +582,10 @@ object RequestSpec extends ZIOHttpSpec { byteStream.runCount } } + .toRoutes for { - result <- route.toHttpApp + result <- route .runZIO(Request.post(URL.decode("/test-byte-stream").toOption.get, Body.fromChunk(bytes))) .exit response <- result match { diff --git a/zio-http/jvm/src/test/scala/zio/http/endpoint/RoundtripSpec.scala b/zio-http/jvm/src/test/scala/zio/http/endpoint/RoundtripSpec.scala index cc3809e462..ba29734388 100644 --- a/zio-http/jvm/src/test/scala/zio/http/endpoint/RoundtripSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/endpoint/RoundtripSpec.scala @@ -79,7 +79,7 @@ object RoundtripSpec extends ZIOHttpSpec { def testEndpoint[P, In, Err, Out]( endpoint: Endpoint[P, In, Err, Out, EndpointMiddleware.None.type], - route: HttpApp[Any, Nothing], + route: Routes[Any, Nothing], in: In, out: Out, ): ZIO[Client with Server with Scope, Err, TestResult] = @@ -87,7 +87,7 @@ object RoundtripSpec extends ZIOHttpSpec { def testEndpointZIO[P, In, Err, Out]( endpoint: Endpoint[P, In, Err, Out, EndpointMiddleware.None.type], - route: HttpApp[Any, Nothing], + route: Routes[Any, Nothing], in: In, outF: Out => ZIO[Any, Err, TestResult], ): zio.ZIO[Server with Client with Scope, Err, TestResult] = @@ -100,7 +100,7 @@ object RoundtripSpec extends ZIOHttpSpec { } yield result def testEndpointCustomRequestZIO[P, In, Err, Out]( - route: HttpApp[Any, Nothing], + route: Routes[Any, Nothing], in: Request, outF: Response => ZIO[Any, Err, TestResult], ): zio.ZIO[Server with Client with Scope, Err, TestResult] = @@ -115,7 +115,7 @@ object RoundtripSpec extends ZIOHttpSpec { def testEndpointError[P, In, Err, Out]( endpoint: Endpoint[P, In, Err, Out, EndpointMiddleware.None.type], - route: HttpApp[Any, Nothing], + route: Routes[Any, Nothing], in: In, err: Err, ): ZIO[Client with Server with Scope, Out, TestResult] = @@ -123,7 +123,7 @@ object RoundtripSpec extends ZIOHttpSpec { def testEndpointErrorZIO[P, In, Err, Out]( endpoint: Endpoint[P, In, Err, Out, EndpointMiddleware.None.type], - route: HttpApp[Any, Nothing], + route: Routes[Any, Nothing], in: In, errorF: Err => ZIO[Any, Nothing, TestResult], ): ZIO[Client with Server with Scope, Out, TestResult] = @@ -155,7 +155,7 @@ object RoundtripSpec extends ZIOHttpSpec { testEndpoint( usersPostAPI, - HttpApp(usersPostHandler), + Routes(usersPostHandler), (10, 20), Post(20, "title", "body", 10), ) @@ -175,7 +175,7 @@ object RoundtripSpec extends ZIOHttpSpec { testEndpoint( usersPostAPI, - HttpApp(usersPostHandler), + Routes(usersPostHandler), (10, 20, Header.Accept(MediaType.parseCustomMediaType("application/protobuf").get)), Post(20, "title", "body", 10), ) && assertZIO(TestConsole.output)(contains("ContentType: application/protobuf\n")) @@ -196,7 +196,7 @@ object RoundtripSpec extends ZIOHttpSpec { testEndpoint( usersPostAPI, - HttpApp(usersPostHandler), + Routes(usersPostHandler), (10, 20, Header.Accept(MediaType.parseCustomMediaType("application/protobuf").get)), Post(20, "title", "body", 10), ) && assertZIO(TestConsole.output)(contains("ContentType: application/protobuf\n")) @@ -218,18 +218,18 @@ object RoundtripSpec extends ZIOHttpSpec { testEndpoint( api, - HttpApp(handler), + Routes(handler), (10, 20, None, Some("x")), Post(10, "-", "x", 20), ) && testEndpoint( api, - HttpApp(handler), + Routes(handler), (10, 20, None, None), Post(10, "-", "-", 20), ) && testEndpoint( api, - HttpApp(handler), + Routes(handler), (10, 20, Some("x"), Some("y")), Post(10, "x", "y", 20), ) @@ -244,14 +244,13 @@ object RoundtripSpec extends ZIOHttpSpec { val handler = api.implement { Handler.fromFunction { case (accountId, name, instanceName, args, env) => - println(s"$accountId, $name, $instanceName, $args, $env") throw new RuntimeException("I can't code") s"$accountId, $name, $instanceName, $args, $env" } } for { - port <- Server.install(handler.toHttpApp) + port <- Server.install(handler.toRoutes) client <- ZIO.service[Client] response <- client( Request.post( @@ -274,7 +273,7 @@ object RoundtripSpec extends ZIOHttpSpec { testEndpoint( api, - HttpApp(route), + Routes(route), (11, Post(1, "title", "body", 111)), "userId: 11, post: Post(1,title,body,111)", ) @@ -290,7 +289,7 @@ object RoundtripSpec extends ZIOHttpSpec { Random.nextBytes(1024 * 1024).flatMap { bytes => testEndpoint( api, - HttpApp(route), + Routes(route), ZStream.fromChunk(bytes).rechunk(1024), 1024 * 1024L, ) @@ -306,7 +305,7 @@ object RoundtripSpec extends ZIOHttpSpec { testEndpointZIO( api, - HttpApp(route), + Routes(route), 1024 * 1024, (stream: ZStream[Any, Nothing, Byte]) => stream.runCount.map(c => assert(c)(equalTo(1024L * 1024L))), ) @@ -326,7 +325,7 @@ object RoundtripSpec extends ZIOHttpSpec { testEndpoint( api, - HttpApp(route), + Routes(route), ("name", 10, Post(1, "title", "body", 111)), "name: name, value: 10, post: Post(1,title,body,111)", ) @@ -339,7 +338,7 @@ object RoundtripSpec extends ZIOHttpSpec { testEndpointError( api, - HttpApp(route), + Routes(route), (), "42", ) @@ -358,10 +357,10 @@ object RoundtripSpec extends ZIOHttpSpec { val endpointRoute = endpoint.implement(Handler.identity) - val routes = endpointRoute + val routes = endpointRoute.toRoutes - val app = routes.toHttpApp @@ alwaysFailingMiddleware - .implement(_ => ZIO.fail("FAIL"))(_ => ZIO.unit) + val app = routes @@ alwaysFailingMiddleware + .implement[Any, Unit](_ => ZIO.fail("FAIL"))(_ => ZIO.unit) for { port <- Server.install(app) @@ -397,10 +396,9 @@ object RoundtripSpec extends ZIOHttpSpec { val endpointRoute = endpoint.implement(Handler.identity) - val routes = endpointRoute + val routes = endpointRoute.toRoutes - val app = routes.toHttpApp @@ alwaysFailingMiddleware - .implement(_ => ZIO.fail("FAIL"))(_ => ZIO.unit) + val app = routes @@ alwaysFailingMiddleware.implement[Any, Unit](_ => ZIO.fail("FAIL"))(_ => ZIO.unit) for { port <- Server.install(app) @@ -444,12 +442,10 @@ object RoundtripSpec extends ZIOHttpSpec { } } - val routes = endpointRoute - - val app = routes.toHttpApp + val routes = endpointRoute.toRoutes for { - port <- Server.install(app) + port <- Server.install(routes) executorLayer = ZLayer(ZIO.serviceWith[Client](makeExecutor(_, port))) cause <- ZIO @@ -494,7 +490,7 @@ object RoundtripSpec extends ZIOHttpSpec { Random.nextBytes(1024 * 1024).flatMap { bytes => testEndpoint( api, - HttpApp(route), + Routes(route), ("xyz", 100, ZStream.fromChunk(bytes).rechunk(1024)), s"name: xyz, value: 100, count: ${1024 * 1024}", ) @@ -519,7 +515,7 @@ object RoundtripSpec extends ZIOHttpSpec { .nextBytes(1024 * 1024) .flatMap { bytes => testEndpointCustomRequestZIO( - HttpApp(route), + Routes(route), Request.post( "/test", Body.fromMultipartForm( diff --git a/zio-http/jvm/src/test/scala/zio/http/endpoint/openapi/SwaggerUISpec.scala b/zio-http/jvm/src/test/scala/zio/http/endpoint/openapi/SwaggerUISpec.scala index 12d9e3b11f..a7bfbb7f61 100644 --- a/zio-http/jvm/src/test/scala/zio/http/endpoint/openapi/SwaggerUISpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/endpoint/openapi/SwaggerUISpec.scala @@ -34,7 +34,7 @@ object SwaggerUISpec extends ZIOSpecDefault { OpenAPIGen.fromEndpoints(title = "Another Endpoint Example", version = "2.0", getUser, getUserPosts) val routes = - HttpApp(getUserRoute, getUserPostsRoute) ++ SwaggerUI.app("docs" / "openapi", openAPIv1, openAPIv2) + Routes(getUserRoute, getUserPostsRoute) ++ SwaggerUI.routes("docs" / "openapi", openAPIv1, openAPIv2) val response = routes.apply(Request(method = Method.GET, url = url"/docs/openapi")) diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/DynamicServer.scala b/zio-http/jvm/src/test/scala/zio/http/internal/DynamicServer.scala index 92a7de9a9a..0f1d070b9a 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/DynamicServer.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/DynamicServer.scala @@ -24,9 +24,9 @@ import zio.http._ import zio.http.internal.DynamicServer.Id sealed trait DynamicServer { - def add(app: HttpApp[Any, Response]): UIO[Id] + def add(app: Routes[Any, Response]): UIO[Id] - def get(id: Id): UIO[Option[HttpApp[Any, Response]]] + def get(id: Id): UIO[Option[Routes[Any, Response]]] def port: ZIO[Any, Nothing, Int] @@ -41,7 +41,7 @@ object DynamicServer { val APP_ID = "X-APP_ID" - def app(dynamicServer: DynamicServer): RequestHandler[Any, Response] = + def handler(dynamicServer: DynamicServer): RequestHandler[Any, Response] = Handler.fromFunctionHandler[Request] { (req: Request) => Handler .fromZIO(req.rawHeader(APP_ID) match { @@ -59,13 +59,13 @@ object DynamicServer { def baseURL(scheme: Scheme): ZIO[DynamicServer, Nothing, String] = port.map(port => s"${scheme.encode}://localhost:$port") - def deploy[R](app: HttpApp[R, Response]): ZIO[DynamicServer with R, Nothing, String] = + def deploy[R](app: Routes[R, Response]): ZIO[DynamicServer with R, Nothing, String] = for { env <- ZIO.environment[R] id <- ZIO.environmentWithZIO[DynamicServer](_.get.add(app.provideEnvironment(env))) } yield id - def get(id: Id): ZIO[DynamicServer, Nothing, Option[HttpApp[Any, Response]]] = + def get(id: Id): ZIO[DynamicServer, Nothing, Option[Routes[Any, Response]]] = ZIO.environmentWithZIO[DynamicServer](_.get.get(id)) def httpURL: ZIO[DynamicServer, Nothing, String] = baseURL(Scheme.HTTP) @@ -73,7 +73,7 @@ object DynamicServer { val live: ZLayer[Any, Nothing, DynamicServer] = ZLayer { for { - ref <- Ref.make(Map.empty[Id, HttpApp[Any, Response]]) + ref <- Ref.make(Map.empty[Id, Routes[Any, Response]]) pr <- Promise.make[Nothing, Server] } yield new Live(ref, pr) } @@ -87,13 +87,13 @@ object DynamicServer { def wsURL: ZIO[DynamicServer, Nothing, String] = baseURL(Scheme.WS) - final class Live(ref: Ref[Map[Id, HttpApp[Any, Response]]], pr: Promise[Nothing, Server]) extends DynamicServer { - def add(app: HttpApp[Any, Response]): UIO[Id] = for { + final class Live(ref: Ref[Map[Id, Routes[Any, Response]]], pr: Promise[Nothing, Server]) extends DynamicServer { + def add(app: Routes[Any, Response]): UIO[Id] = for { id <- ZIO.succeed(UUID.randomUUID().toString) _ <- ref.update(map => map + (id -> app)) } yield id - def get(id: Id): UIO[Option[HttpApp[Any, Response]]] = ref.get.map(_.get(id)) + def get(id: Id): UIO[Option[Routes[Any, Response]]] = ref.get.map(_.get(id)) def port: ZIO[Any, Nothing, Int] = start.map(_.port) diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/HttpAppTestExtensions.scala b/zio-http/jvm/src/test/scala/zio/http/internal/HttpAppTestExtensions.scala index 7c8c7b3cb9..5dc7a81d58 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/HttpAppTestExtensions.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/HttpAppTestExtensions.scala @@ -19,7 +19,7 @@ package zio.http.internal import zio.http._ trait HttpAppTestExtensions { - implicit class HttpAppSyntax[R](route: HttpApp[R, Response]) { + implicit class HttpAppSyntax[R](route: Routes[R, Response]) { def rawHeader(name: String): Handler[R, Response, Request, Option[String]] = route.toHandler.map(res => res.rawHeader(name)) diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/HttpRunnableSpec.scala b/zio-http/jvm/src/test/scala/zio/http/internal/HttpRunnableSpec.scala index 76c7f5ca21..eb6148e3cf 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/HttpRunnableSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/HttpRunnableSpec.scala @@ -29,9 +29,9 @@ import zio.http._ * requests. */ abstract class HttpRunnableSpec extends ZIOHttpSpec { self => - implicit class RunnableHttpClientAppSyntax[R](route: HttpApp[R, Response]) { + implicit class RunnableHttpClientAppSyntax[R](route: Routes[R, Response]) { - def app: HttpApp[R, Response] = route + def app: Routes[R, Response] = route /** * Deploys the http application on the test server and returns a Http of @@ -107,12 +107,12 @@ abstract class HttpRunnableSpec extends ZIOHttpSpec { self => for { server <- ZIO.service[Server] ds <- ZIO.service[DynamicServer] - app = DynamicServer.app(ds) - port <- Server.install(app.toHttpApp) + handler = DynamicServer.handler(ds) + port <- Server.install(handler.toRoutes) _ <- DynamicServer.setStart(server) } yield port - def serve[R](app: HttpApp[R, Response]): ZIO[R with DynamicServer with Server, Nothing, Int] = + def serve[R](app: Routes[R, Response]): ZIO[R with DynamicServer with Server, Nothing, Int] = for { server <- ZIO.service[Server] port <- Server.install(app) diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/AuthSpec.scala b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/AuthSpec.scala index 032d43a1cc..fc60b13403 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/AuthSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/AuthSpec.scala @@ -80,7 +80,7 @@ object AuthSpec extends ZIOHttpSpec with HttpAppTestExtensions { }, test("Extract username via context with Routes") { val app = { - HttpApp( + Routes( Method.GET / "context" -> basicAuthContextM -> Handler.fromFunction[(AuthContext, Request)] { case (c: AuthContext, _) => Response.text(c.value) }, ) @@ -106,7 +106,7 @@ object AuthSpec extends ZIOHttpSpec with HttpAppTestExtensions { assertZIO(app.runZIO(Request.get(URL.empty).copy(headers = failureBasicHeader)))(isSome) }, test("Provide for multiple routes") { - val secureRoutes = HttpApp( + val secureRoutes = Routes( Method.GET / "a" -> handler((_: Request) => ZIO.serviceWith[AuthContext](ctx => Response.text(ctx.value))), Method.GET / "b" / int("id") -> handler((id: Int, _: Request) => ZIO.serviceWith[AuthContext](ctx => Response.text(s"for id: $id: ${ctx.value}")), @@ -144,9 +144,9 @@ object AuthSpec extends ZIOHttpSpec with HttpAppTestExtensions { assertZIO(app.runZIO(Request.get(URL.empty).copy(headers = failureBearerHeader)))(isSome) }, test("Does not affect fallback apps") { - val app1 = HttpApp(Method.GET / "a" -> Handler.ok) - val app2 = HttpApp(Method.GET / "b" -> Handler.ok) - val app3 = HttpApp(Method.GET / "c" -> Handler.ok) + val app1 = Routes(Method.GET / "a" -> Handler.ok) + val app2 = Routes(Method.GET / "b" -> Handler.ok) + val app3 = Routes(Method.GET / "c" -> Handler.ok) val app = app1 ++ app2 @@ bearerAuthM ++ app3 for { s1 <- app.runZIO(Request.get(URL(Root / "a")).copy(headers = failureBearerHeader)) @@ -170,9 +170,9 @@ object AuthSpec extends ZIOHttpSpec with HttpAppTestExtensions { assertZIO(app.runZIO(Request.get(URL.empty).copy(headers = failureBearerHeader)))(isSome) }, test("Does not affect fallback apps") { - val app1 = HttpApp(Method.GET / "a" -> Handler.ok) - val app2 = HttpApp(Method.GET / "b" -> Handler.ok) - val app3 = HttpApp(Method.GET / "c" -> Handler.ok) + val app1 = Routes(Method.GET / "a" -> Handler.ok) + val app2 = Routes(Method.GET / "b" -> Handler.ok) + val app3 = Routes(Method.GET / "c" -> Handler.ok) val app = app1 ++ app2 @@ bearerAuthZIOM ++ app3 for { s1 <- app.runZIO(Request.get(URL(Root / "a")).copy(headers = failureBearerHeader)) diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/CorsSpec.scala b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/CorsSpec.scala index 312e4b76c0..02f2fa5aec 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/CorsSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/CorsSpec.scala @@ -27,7 +27,7 @@ import zio.http.internal.HttpAppTestExtensions object CorsSpec extends ZIOHttpSpec with HttpAppTestExtensions { def extractStatus(response: Response): Status = response.status - val app = HttpApp( + val app = Routes( Method.GET / "success" -> handler(Response.ok), Method.GET / "failure" -> handler(ZIO.fail("failure")), Method.GET / "die" -> handler(ZIO.dieMessage("die")), diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/MetricsSpec.scala b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/MetricsSpec.scala index 5c44953fd1..3e454e03e5 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/MetricsSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/MetricsSpec.scala @@ -29,7 +29,7 @@ object MetricsSpec extends ZIOHttpSpec with HttpAppTestExtensions { override def spec: Spec[TestEnvironment with Scope, Any] = suite("MetricsSpec")( test("http_requests_total & http_errors_total") { - val app = HttpApp( + val app = Routes( Method.GET / "ok" -> Handler.ok, Method.GET / "error" -> Handler.internalServerError, Method.GET / "fail" -> Handler.fail(Response.status(Status.Forbidden)), @@ -61,7 +61,7 @@ object MetricsSpec extends ZIOHttpSpec with HttpAppTestExtensions { ) }, test("http_requests_total with path label mapper") { - val app = (Method.GET / "user" / int("id") -> Handler.ok).toHttpApp @@ metrics( + val routes = (Method.GET / "user" / int("id") -> Handler.ok).toRoutes @@ metrics( extraLabels = Set(MetricLabel("test", "http_requests_total with path label mapper")), ) @@ -70,8 +70,8 @@ object MetricsSpec extends ZIOHttpSpec with HttpAppTestExtensions { val totalOk = total.tagged("path", "/user/{id}").tagged("method", "GET").tagged("status", "200") for { - _ <- app.runZIO(Request.get(url = URL(Root / "user" / "1"))) - _ <- app.runZIO(Request.get(url = URL(Root / "user" / "2"))) + _ <- routes.runZIO(Request.get(url = URL(Root / "user" / "1"))) + _ <- routes.runZIO(Request.get(url = URL(Root / "user" / "2"))) totalOkCount <- totalOk.value } yield assertTrue(totalOkCount == MetricState.Counter(2)) }, @@ -86,8 +86,8 @@ object MetricsSpec extends ZIOHttpSpec with HttpAppTestExtensions { .tagged("method", "GET") .tagged("status", "200") - val app: HttpApp[Any, Response] = - (Method.GET / "ok" -> Handler.ok).toHttpApp @@ metrics(extraLabels = + val app: Routes[Any, Response] = + (Method.GET / "ok" -> Handler.ok).toRoutes @@ metrics(extraLabels = Set(MetricLabel("test", "http_request_duration_seconds")), ) @@ -105,7 +105,7 @@ object MetricsSpec extends ZIOHttpSpec with HttpAppTestExtensions { for { promise <- Promise.make[Nothing, Unit] - app = HttpApp( + app = Routes( Method.ANY / PathCodec.trailing -> (Handler.fromZIO(promise.succeed(())) *> Handler.ok.delay(10.seconds)), ) @@ metrics(extraLabels = Set(MetricLabel("test", "http_concurrent_requests_total"))) before <- gauge.value diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/RequestLoggingSpec.scala b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/RequestLoggingSpec.scala index caf2b985a0..693ea0fee7 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/RequestLoggingSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/RequestLoggingSpec.scala @@ -25,7 +25,7 @@ import zio.http.internal.HttpAppTestExtensions object RequestLoggingSpec extends ZIOHttpSpec with HttpAppTestExtensions { - private val app = HttpApp( + private val app = Routes( Method.GET / "ok" -> Handler.ok, Method.GET / "error" -> Handler.internalServerError, Method.GET / "fail" -> Handler.fail(Response.status(Status.Forbidden)), diff --git a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/WebSpec.scala b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/WebSpec.scala index 347e345a99..e222151705 100644 --- a/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/WebSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/internal/middlewares/WebSpec.scala @@ -29,7 +29,7 @@ object WebSpec extends ZIOHttpSpec with HttpAppTestExtensions { self => def extractStatus(response: Response): Status = response.status private val app = - HttpApp( + Routes( Method.GET / "health" -> handler(ZIO.succeed(Response.ok).delay(1 second)), ) @@ -40,23 +40,23 @@ object WebSpec extends ZIOHttpSpec with HttpAppTestExtensions { self => suite("headers suite")( test("addHeaders") { val middleware = addHeaders(Headers("KeyA", "ValueA") ++ Headers("KeyB", "ValueB")) - val headers = (Handler.ok @@ middleware).toHttpApp.headerValues + val headers = (Handler.ok @@ middleware).headerValues assertZIO(headers.runZIO(Request.get(URL.empty)))(contains("ValueA") && contains("ValueB")) }, test("addHeader") { val middleware = addHeader("KeyA", "ValueA") - val headers = (Handler.ok @@ middleware).toHttpApp.headerValues + val headers = (Handler.ok @@ middleware).headerValues assertZIO(headers.runZIO(Request.get(URL.empty)))(contains("ValueA")) }, test("updateHeaders") { val middleware = updateHeaders(_ => Headers("KeyA", "ValueA")) - val headers = (Handler.ok @@ middleware).toHttpApp.headerValues + val headers = (Handler.ok @@ middleware).headerValues assertZIO(headers.runZIO(Request.get(URL.empty)))(contains("ValueA")) }, test("removeHeader") { val middleware = removeHeader("KeyA") val headers = - (Handler.succeed(Response.ok.setHeaders(Headers("KeyA", "ValueA"))) @@ middleware).toHttpApp rawHeader "KeyA" + (Handler.succeed(Response.ok.setHeaders(Headers("KeyA", "ValueA"))) @@ middleware) rawHeader "KeyA" assertZIO(headers.runZIO(Request.get(URL.empty)))(isNone) }, ), @@ -73,7 +73,7 @@ object WebSpec extends ZIOHttpSpec with HttpAppTestExtensions { self => }, test("log 404 status method url and time") { for { - _ <- runApp((Handler.notFound @@ debug).toHttpApp) + _ <- runApp(Handler.notFound.toRoutes @@ debug) log <- TestConsole.output } yield assertTrue( log.size == 1, @@ -216,7 +216,7 @@ object WebSpec extends ZIOHttpSpec with HttpAppTestExtensions { self => ), ) checkAll(urls) { case (url, expected) => - val app = HttpApp( + val app = Routes( Method.ANY / PathCodec.trailing -> handler { (_: Path, req: Request) => Response.text(req.url.encode) }, @@ -240,7 +240,7 @@ object WebSpec extends ZIOHttpSpec with HttpAppTestExtensions { self => ) checkAll(urls zip Gen.fromIterable(Seq(true, false))) { case (url, expected, perm) => - val app = (Handler.ok @@ redirectTrailingSlash(perm)).toHttpApp + val app = (Handler.ok @@ redirectTrailingSlash(perm)).toRoutes val location = if (url != expected) Some(expected) else None val status = if (url == expected) Status.Ok @@ -268,7 +268,7 @@ object WebSpec extends ZIOHttpSpec with HttpAppTestExtensions { self => ) checkAll(urls) { url => - val app = (Handler.ok @@ redirectTrailingSlash(true)).toHttpApp + val app = Handler.ok @@ redirectTrailingSlash(true) for { url <- ZIO.fromEither(URL.decode(url)) response <- app.runZIO(Request.get(url = url)) @@ -317,7 +317,7 @@ object WebSpec extends ZIOHttpSpec with HttpAppTestExtensions { self => private def condZIO(flg: Boolean) = (_: Any) => ZIO.succeed(flg) - private def runApp[R](app: HttpApp[R, Response]): ZIO[R, Response, Response] = { + private def runApp[R](app: Routes[R, Response]): ZIO[R, Response, Response] = { for { fib <- app.runZIO { Request.get(url = URL(Root / "health")) }.fork _ <- TestClock.adjust(10 seconds) diff --git a/zio-http/jvm/src/test/scala/zio/http/netty/NettyStreamBodySpec.scala b/zio-http/jvm/src/test/scala/zio/http/netty/NettyStreamBodySpec.scala index 89b34a8c2f..af011ee769 100644 --- a/zio-http/jvm/src/test/scala/zio/http/netty/NettyStreamBodySpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/netty/NettyStreamBodySpec.scala @@ -14,7 +14,7 @@ import zio.http.netty.NettyConfig.LeakDetectionLevel object NettyStreamBodySpec extends HttpRunnableSpec { def app(streams: Iterator[ZStream[Any, Throwable, Byte]], len: Long) = - HttpApp( + Routes( Method.GET / "with-content-length" -> handler( http.Response( diff --git a/zio-http/jvm/src/test/scala/zio/http/netty/client/NettyConnectionPoolSpec.scala b/zio-http/jvm/src/test/scala/zio/http/netty/client/NettyConnectionPoolSpec.scala index 2649765462..f670c4b722 100644 --- a/zio-http/jvm/src/test/scala/zio/http/netty/client/NettyConnectionPoolSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/netty/client/NettyConnectionPoolSpec.scala @@ -30,7 +30,7 @@ import zio.http.netty.NettyConfig object NettyConnectionPoolSpec extends HttpRunnableSpec { - private val app = HttpApp( + private val app = Routes( Method.POST / "streaming" -> handler((req: Request) => Response(body = Body.fromStreamChunked(req.body.asStream))), Method.GET / "slow" -> handler(ZIO.sleep(1.hour).as(Response.text("done"))), Method.ANY / trailing -> handler((_: Path, req: Request) => req.body.asString.map(Response.text(_))), diff --git a/zio-http/shared/src/main/scala-2/zio/http/HttpAppVersionSpecific.scala b/zio-http/shared/src/main/scala-2/zio/http/HttpAppVersionSpecific.scala deleted file mode 100644 index 02babc74ff..0000000000 --- a/zio-http/shared/src/main/scala-2/zio/http/HttpAppVersionSpecific.scala +++ /dev/null @@ -1,14 +0,0 @@ -package zio.http - -import zio.Tag - -trait HttpAppVersionSpecific { - private[http] class ApplyContextAspect[-Env, +Err, Env0](private val self: HttpApp[Env, Err]) { - def apply[Env1, Env2 <: Env, Ctx: Tag](aspect: HandlerAspect[Env1, Ctx])(implicit - ev: Env0 with Ctx <:< Env, - tag: Tag[Env0], - tag1: Tag[Env1], - ): HttpApp[Env0 with Env1, Err] = self.transform(_.@@[Env0](aspect)) - } - -} diff --git a/zio-http/shared/src/main/scala-2/zio/http/RoutesVersionSpecific.scala b/zio-http/shared/src/main/scala-2/zio/http/RoutesCompanionVersionSpecific.scala similarity index 90% rename from zio-http/shared/src/main/scala-2/zio/http/RoutesVersionSpecific.scala rename to zio-http/shared/src/main/scala-2/zio/http/RoutesCompanionVersionSpecific.scala index 8f85ce9cca..b225afdeb5 100644 --- a/zio-http/shared/src/main/scala-2/zio/http/RoutesVersionSpecific.scala +++ b/zio-http/shared/src/main/scala-2/zio/http/RoutesCompanionVersionSpecific.scala @@ -2,7 +2,7 @@ package zio.http import zio.Tag -trait RoutesVersionSpecific { +trait RoutesCompanionVersionSpecific { private[http] class ApplyContextAspect[-Env, +Err, Env0](private val self: Routes[Env, Err]) { def apply[Env1, Env2 <: Env, Ctx: Tag](aspect: HandlerAspect[Env1, Ctx])(implicit ev: Env0 with Ctx <:< Env, diff --git a/zio-http/shared/src/main/scala-3/zio/http/HttpAppVersionSpecific.scala b/zio-http/shared/src/main/scala-3/zio/http/HttpAppVersionSpecific.scala deleted file mode 100644 index 8d545ca3c8..0000000000 --- a/zio-http/shared/src/main/scala-3/zio/http/HttpAppVersionSpecific.scala +++ /dev/null @@ -1,10 +0,0 @@ -package zio.http - -trait HttpAppVersionSpecific { - private[http] class ApplyContextAspect[-Env, +Err, Env0](private val self: HttpApp[Env, Err]) { - transparent inline def apply[Env1, Env2 <: Env, Ctx](aspect: HandlerAspect[Env1, Ctx])(implicit - ev: Env0 with Ctx <:< Env, - ): HttpApp[Env0 with Env1, Err] = self.transform(_.@@[Env0](aspect)) - } - -} diff --git a/zio-http/shared/src/main/scala-3/zio/http/RoutesVersionSpecific.scala b/zio-http/shared/src/main/scala-3/zio/http/RoutesCompanionVersionSpecific.scala similarity index 89% rename from zio-http/shared/src/main/scala-3/zio/http/RoutesVersionSpecific.scala rename to zio-http/shared/src/main/scala-3/zio/http/RoutesCompanionVersionSpecific.scala index 0464786999..0a853e5d88 100644 --- a/zio-http/shared/src/main/scala-3/zio/http/RoutesVersionSpecific.scala +++ b/zio-http/shared/src/main/scala-3/zio/http/RoutesCompanionVersionSpecific.scala @@ -1,6 +1,6 @@ package zio.http -trait RoutesVersionSpecific { +trait RoutesCompanionVersionSpecific { private[http] class ApplyContextAspect[-Env, +Err, Env0](private val self: Routes[Env, Err]) { transparent inline def apply[Env1, Env2 <: Env, Ctx](aspect: HandlerAspect[Env1, Ctx])(implicit ev: Env0 with Ctx <:< Env, diff --git a/zio-http/shared/src/main/scala/zio/http/Driver.scala b/zio-http/shared/src/main/scala/zio/http/Driver.scala index 1eeb0abb0a..f1e7871230 100644 --- a/zio-http/shared/src/main/scala/zio/http/Driver.scala +++ b/zio-http/shared/src/main/scala/zio/http/Driver.scala @@ -26,7 +26,7 @@ import zio.http.Driver.StartResult trait Driver { def start(implicit trace: Trace): RIO[Scope, StartResult] - def addApp[R](newApp: HttpApp[R, Response], env: ZEnvironment[R])(implicit trace: Trace): UIO[Unit] + def addApp[R](newApp: Routes[R, Response], env: ZEnvironment[R])(implicit trace: Trace): UIO[Unit] def createClientDriver()(implicit trace: Trace): ZIO[Scope, Throwable, ClientDriver] } diff --git a/zio-http/shared/src/main/scala/zio/http/Handler.scala b/zio-http/shared/src/main/scala/zio/http/Handler.scala index 1b27ca083e..bf65d57b51 100644 --- a/zio-http/shared/src/main/scala/zio/http/Handler.scala +++ b/zio-http/shared/src/main/scala/zio/http/Handler.scala @@ -608,11 +608,25 @@ sealed trait Handler[-R, +Err, -In, +Out] { self => * the handler has been appropriately sandboxed, turning all possible failures * into well-formed HTTP responses. */ - def toHttpApp(implicit in: Request <:< In, out: Out <:< Response, trace: Trace): HttpApp[R, Err] = { + @deprecated("Use toRoutes instead. Will be removed in the next release.") + def toHttpApp(implicit err: Err <:< Response, in: Request <:< In, out: Out <:< Response, trace: Trace): HttpApp[R] = { + val handler: Handler[R, Response, Request, Response] = + self.asInstanceOf[Handler[R, Response, Request, Response]] + + HttpApp(Routes.singleton(handler.contramap[(Path, Request)](_._2))) + } + + /** + * Converts the request handler into an HTTP application. Note that the + * handler of the HTTP application is not identical to this handler, because + * the handler has been appropriately sandboxed, turning all possible failures + * into well-formed HTTP responses. + */ + def toRoutes(implicit in: Request <:< In, out: Out <:< Response, trace: Trace): Routes[R, Err] = { val handler: Handler[R, Err, Request, Response] = self.asInstanceOf[Handler[R, Err, Request, Response]] - HttpApp(Routes.singleton(handler.contramap[(Path, Request)](_._2))) + Routes.singleton(handler.contramap[(Path, Request)](_._2)) } /** diff --git a/zio-http/shared/src/main/scala/zio/http/HandlerAspect.scala b/zio-http/shared/src/main/scala/zio/http/HandlerAspect.scala index 2cdd998a9f..2628a0c515 100644 --- a/zio-http/shared/src/main/scala/zio/http/HandlerAspect.scala +++ b/zio-http/shared/src/main/scala/zio/http/HandlerAspect.scala @@ -90,8 +90,7 @@ final case class HandlerAspect[-Env, +CtxOut]( * Applies middleware to the specified handler, which may ignore the context * produced by this middleware. */ - @deprecated("Transform your Routes to HttpApp and use the overloaded apply for HttpApp.") - def apply[Env1 <: Env, Err]( + override def apply[Env1 <: Env, Err]( routes: Routes[Env1, Err], ): Routes[Env1, Err] = routes.transform[Env1] { handler => @@ -107,26 +106,6 @@ final case class HandlerAspect[-Env, +CtxOut]( } } - /** - * Applies middleware to the specified handler, which may ignore the context - * produced by this middleware. - */ - override def apply[Env1 <: Env, Err]( - routes: HttpApp[Env1, Err], - ): HttpApp[Env1, Err] = - routes.transform[Env1] { handler => - if (self == HandlerAspect.identity) handler - else { - for { - tuple <- protocol.incomingHandler - (state, (request, ctxOut)) = tuple - either <- Handler.fromZIO(handler(request)).either - response <- Handler.fromZIO(protocol.outgoingHandler((state, either.merge))) - response <- if (either.isLeft) Handler.fail(response) else Handler.succeed(response) - } yield response - } - } - /** * Applies middleware to the specified handler, which must process the context * produced by this middleware. diff --git a/zio-http/shared/src/main/scala/zio/http/HttpApp.scala b/zio-http/shared/src/main/scala/zio/http/HttpApp.scala index 03056a8d30..f98139e6d2 100644 --- a/zio-http/shared/src/main/scala/zio/http/HttpApp.scala +++ b/zio-http/shared/src/main/scala/zio/http/HttpApp.scala @@ -17,9 +17,7 @@ package zio.http import zio._ - -import zio.http.HttpApp.ApplyContextAspect -import zio.http.codec.PathCodec +import zio.stacktracer.TracingImplicits.disableAutoTrace /** * An HTTP application is a collection of routes, all of whose errors have been @@ -28,125 +26,30 @@ import zio.http.codec.PathCodec * HTTP applications can be installed into a [[zio.http.Server]], which is * capable of using them to serve requests. */ -final case class HttpApp[-Env, +Err](routes: Chunk[zio.http.Route[Env, Err]]) -/*extends PartialFunction[Request, ZIO[Env, Response, Response]]*/ { self => +@deprecated("Use Routes instead. Will be removed in the next release.") +final case class HttpApp[-Env](routes: Routes[Env, Response]) + extends PartialFunction[Request, ZIO[Env, Response, Response]] { self => private var _tree: HttpApp.Tree[_] = null.asInstanceOf[HttpApp.Tree[_]] - def @@[Env1 <: Env](aspect: Middleware[Env1]): HttpApp[Env1, Err] = - aspect(self) - - def @@[Env0](aspect: HandlerAspect[Env0, Unit]): HttpApp[Env with Env0, Err] = - aspect(self) - - def @@[Env0, Ctx <: Env]( - aspect: HandlerAspect[Env0, Ctx], - )(implicit tag: Tag[Ctx]): HttpApp[Env0, Err] = - self.transform(_ @@ aspect) - - def @@[Env0]: ApplyContextAspect[Env, Err, Env0] = - new ApplyContextAspect[Env, Err, Env0](self) + /** + * Applies the specified route aspect to every route in the HTTP application. + */ + def @@[Env1 <: Env](aspect: Middleware[Env1]): HttpApp[Env1] = + copy(routes = routes @@ aspect) /** * Combines this HTTP application with the specified HTTP application. In case * of route conflicts, the routes in this HTTP application take precedence * over the routes in the specified HTTP application. */ - def ++[Env1 <: Env, Err1 >: Err](that: HttpApp[Env1, Err1]): HttpApp[Env1, Err1] = + def ++[Env1 <: Env](that: HttpApp[Env1]): HttpApp[Env1] = copy(routes = routes ++ that.routes) - /** - * Prepend the specified route to this HttpApp - */ - def +:[Env1 <: Env, Err1 >: Err](route: zio.http.Route[Env1, Err1]): HttpApp[Env1, Err1] = - copy(routes = route +: routes) - - /** - * Appends the specified route to this HttpApp - */ - def :+[Env1 <: Env, Err1 >: Err](route: zio.http.Route[Env1, Err1]): HttpApp[Env1, Err1] = - copy(routes = routes :+ route) - /** * Executes the HTTP application with the specified request input, returning * an effect that will either succeed or fail with a Response. */ - def apply(request: Request)(implicit ev: Err <:< Response): ZIO[Env, Response, Response] = - runZIO(request) - - /** - * Handles all typed errors in the routes by converting them into responses. - * This method can be used to convert routes that do not handle their errors - * into ones that do handle their errors. - */ - def handleError(f: Err => Response)(implicit trace: Trace): HttpApp[Env, Nothing] = - new HttpApp(routes.map(_.handleError(f))) - - def handleErrorZIO(f: Err => ZIO[Any, Nothing, Response])(implicit trace: Trace): HttpApp[Env, Nothing] = - new HttpApp(routes.map(_.handleErrorZIO(f))) - - /** - * Handles all typed errors, as well as all non-recoverable errors, by - * converting them into responses. This method can be used to convert routes - * that do not handle their errors into ones that do handle their errors. - */ - def handleErrorCause(f: Cause[Err] => Response)(implicit trace: Trace): HttpApp[Env, Nothing] = - new HttpApp(routes.map(_.handleErrorCause(f))) - - /** - * Handles all typed errors, as well as all non-recoverable errors, by - * converting them into a ZIO effect that produces the response. This method - * can be used to convert routes that do not handle their errors into ones - * that do handle their errors. - */ - def handleErrorCauseZIO(f: Cause[Err] => ZIO[Any, Nothing, Response])(implicit trace: Trace): HttpApp[Env, Nothing] = - new HttpApp(routes.map(_.handleErrorCauseZIO(f))) - - /** - * Allows the transformation of the Err type through an Effectful program - * allowing one to build up a HttpApp in Stages delegates to the Route - */ - def mapErrorZIO[Err1](fxn: Err => ZIO[Any, Err1, Response])(implicit trace: Trace): HttpApp[Env, Err1] = - new HttpApp(routes.map(_.mapErrorZIO(fxn))) - - /** - * Allows the transformation of the Err type through a function allowing one - * to build up a HttpApp in Stages delegates to the Route - */ - def mapError[Err1](fxn: Err => Err1): HttpApp[Env, Err1] = - new HttpApp(routes.map(_.mapError(fxn))) - - def nest(prefix: PathCodec[Unit])(implicit trace: Trace, ev: Err <:< Response): HttpApp[Env, Err] = - new HttpApp(self.routes.map(_.nest(prefix))) - - /** - * Handles all typed errors in the routes by converting them into responses, - * taking into account the request that caused the error. This method can be - * used to convert routes that do not handle their errors into ones that do - * handle their errors. - */ - def handleErrorRequest(f: (Err, Request) => Response)(implicit trace: Trace): HttpApp[Env, Nothing] = - new HttpApp(routes.map(_.handleErrorRequest(f))) - - /** - * Handles all typed errors in the routes by converting them into responses, - * taking into account the request that caused the error. This method can be - * used to convert routes that do not handle their errors into ones that do - * handle their errors. - */ - def handleErrorRequestCause(f: (Request, Cause[Err]) => Response)(implicit trace: Trace): HttpApp[Env, Nothing] = - new HttpApp(routes.map(_.handleErrorRequestCause(f))) - - /** - * Handles all typed errors, as well as all non-recoverable errors, by - * converting them into a ZIO effect that produces the response, taking into - * account the request that caused the error. This method can be used to - * convert routes that do not handle their errors into ones that do handle - * their errors. - */ - def handleErrorRequestCauseZIO(f: (Request, Cause[Err]) => ZIO[Any, Nothing, Response])(implicit - trace: Trace, - ): HttpApp[Env, Nothing] = - new HttpApp(routes.map(_.handleErrorRequestCauseZIO(f))) + def apply(request: Request): ZIO[Env, Response, Response] = runZIO(request) /** * Checks to see if the HTTP application may be defined at the specified @@ -155,78 +58,41 @@ final case class HttpApp[-Env, +Err](routes: Chunk[zio.http.Route[Env, Err]]) * This method only checks for the presence of a handler that handles the * method and path of the specified request. */ - def isDefinedAt(request: Request)(implicit ev: Err <:< Response): Boolean = - tree(Trace.empty, ev).get(request.method, request.path).nonEmpty + def isDefinedAt(request: Request): Boolean = + tree(Trace.empty).get(request.method, request.path).nonEmpty /** * Provides the specified environment to the HTTP application, returning a new * HTTP application that has no environmental requirements. */ - def provideEnvironment(env: ZEnvironment[Env]): HttpApp[Any, Err] = - copy(routes = routes.map(_.provideEnvironment(env))) - - def run(request: Request)(implicit trace: Trace): ZIO[Env, Either[Err, Response], Response] = { - - class RouteFailure[+Err](val err: Cause[Err]) extends Throwable(null, null, true, false) { - override def getMessage: String = err.unified.headOption.fold("")(_.message) - - override def getStackTrace(): Array[StackTraceElement] = - err.unified.headOption.fold[Chunk[StackTraceElement]](Chunk.empty)(_.trace).toArray - - override def getCause(): Throwable = - err.find { case Cause.Die(throwable, _) => throwable } - .orElse(err.find { case Cause.Fail(value: Throwable, _) => value }) - .orNull - - override def toString = - err.prettyPrint - } - var routeFailure: RouteFailure[Err] = null - - handleErrorCauseZIO { cause => - routeFailure = new RouteFailure(cause) - ZIO.refailCause(Cause.die(routeFailure)) - } - .apply(request) - .mapErrorCause { - case Cause.Die(value: RouteFailure[_], _) if value == routeFailure => routeFailure.err.map(Left(_)) - case cause => cause.map(Right(_)) - } - } + def provideEnvironment(env: ZEnvironment[Env]): HttpApp[Any] = + copy(routes = routes.provideEnvironment(env)) def run( method: Method = Method.GET, path: Path = Path.root, headers: Headers = Headers.empty, body: Body = Body.empty, - )(implicit ev: Err <:< Response): ZIO[Env, Nothing, Response] = + ): ZIO[Env, Nothing, Response] = runZIO(Request(method = method, url = URL.root.path(path), headers = headers, body = body)) /** * An alias for `apply`. */ - def runZIO(request: Request)(implicit ev: Err <:< Response): ZIO[Env, Nothing, Response] = - toHandler(ev)(request) - - /** - * Returns new routes that automatically translate all failures into - * responses, using best-effort heuristics to determine the appropriate HTTP - * status code, and attaching error details using the HTTP header `Warning`. - */ - def sandbox(implicit trace: Trace): HttpApp[Env, Nothing] = - HttpApp(routes.map(_.sandbox)) + def runZIO(request: Request): ZIO[Env, Nothing, Response] = + toHandler(request) /** * Returns a new HTTP application whose requests will be timed out after the * specified duration elapses. */ - def timeout(duration: Duration)(implicit trace: Trace): HttpApp[Env, Err] = + def timeout(duration: Duration)(implicit trace: Trace): HttpApp[Env] = self @@ Middleware.timeout(duration) /** * Converts the HTTP application into a request handler. */ - def toHandler(implicit ev: Err <:< Response): Handler[Env, Nothing, Request, Response] = { + val toHandler: Handler[Env, Nothing, Request, Response] = { implicit val trace: Trace = Trace.empty Handler .fromFunctionHandler[Request] { req => @@ -247,44 +113,24 @@ final case class HttpApp[-Env, +Err](routes: Chunk[zio.http.Route[Env, Err]]) .merge } - /** - * Returns new new HttpApp whose handlers are transformed by the specified - * function. - */ - def transform[Env1]( - f: Handler[Env, Response, Request, Response] => Handler[Env1, Response, Request, Response], - ): HttpApp[Env1, Err] = - new HttpApp(routes.map(_.transform(f))) - /** * Accesses the underlying tree that provides fast dispatch to handlers. */ - def tree(implicit trace: Trace, ev: Err <:< Response): HttpApp.Tree[Env] = { + def tree(implicit trace: Trace): HttpApp.Tree[Env] = { if (_tree eq null) { - _tree = HttpApp.Tree.fromRoutes(routes.asInstanceOf[Chunk[Route[Env, Response]]]) + _tree = HttpApp.Tree.fromRoutes(routes) } + _tree.asInstanceOf[HttpApp.Tree[Env]] } } - -object HttpApp extends HttpAppVersionSpecific { +object HttpApp { /** * An HTTP application that does not handle any routes. */ - val empty: HttpApp[Any, Nothing] = HttpApp(Routes.empty) - - def apply[Env, Err](route: Route[Env, Err], routes: Route[Env, Err]*): HttpApp[Env, Err] = - HttpApp(Chunk.fromIterable(route +: routes)) - - def apply[Env, Err](routes: Routes[Env, Err]): HttpApp[Env, Err] = - HttpApp(routes.routes) - - def fromIterable[Env, Err](routes: Iterable[Route[Env, Err]]): HttpApp[Env, Err] = - HttpApp(Chunk.fromIterable(routes)) - - def singleton[Env, Err](h: Handler[Env, Err, (Path, Request), Response])(implicit trace: Trace): HttpApp[Env, Err] = - HttpApp(Route.route(RoutePattern.any)(h)) + @deprecated("Use Routes.empty instead. Will be removed in the next release.") + val empty: HttpApp[Any] = HttpApp(Routes.empty) private[http] final case class Tree[-Env](tree: RoutePattern.Tree[RequestHandler[Env, Response]]) { self => final def ++[Env1 <: Env](that: Tree[Env1]): Tree[Env1] = @@ -302,7 +148,7 @@ object HttpApp extends HttpAppVersionSpecific { private[http] object Tree { val empty: Tree[Any] = Tree(RoutePattern.Tree.empty) - def fromRoutes[Env](routes: Chunk[zio.http.Route[Env, Response]])(implicit trace: Trace): Tree[Env] = - empty.addAll(routes) + def fromRoutes[Env](routes: Routes[Env, Response])(implicit trace: Trace): Tree[Env] = + empty.addAll(routes.routes) } } diff --git a/zio-http/shared/src/main/scala/zio/http/Middleware.scala b/zio-http/shared/src/main/scala/zio/http/Middleware.scala index c0c0803cb6..a84f7b8644 100644 --- a/zio-http/shared/src/main/scala/zio/http/Middleware.scala +++ b/zio-http/shared/src/main/scala/zio/http/Middleware.scala @@ -24,12 +24,8 @@ import zio.http.codec.{PathCodec, SegmentCodec} import zio.http.endpoint.EndpointMiddleware.None.Err trait Middleware[-UpperEnv] { self => - def apply[Env1 <: UpperEnv, Err]( - routes: Routes[Env1, Err], - ): Routes[Env1, Err] - def apply[Env1 <: UpperEnv, Err](app: HttpApp[Env1, Err]): HttpApp[Env1, Err] = - HttpApp(self(Routes.fromIterable(app.routes))) + def apply[Env1 <: UpperEnv, Err](app: Routes[Env1, Err]): Routes[Env1, Err] def @@[UpperEnv1 <: UpperEnv]( that: Middleware[UpperEnv1], @@ -348,11 +344,11 @@ object Middleware extends HandlerAspects { acc || stop } - override def apply[Env1 <: Any, Err](routes: HttpApp[Env1, Err]): HttpApp[Env1, Err] = { + override def apply[Env1 <: Any, Err](routes: Routes[Env1, Err]): Routes[Env1, Err] = { val mountpoint = Method.GET / path.segments.map(PathCodec.literal).reduceLeftOption(_ / _).getOrElse(PathCodec.empty) val pattern = mountpoint / trailing - val other = HttpApp( + val other = Routes( pattern -> Handler .identity[Request] .flatMap { request => @@ -372,8 +368,6 @@ object Middleware extends HandlerAspects { routes ++ other } - override def apply[Env1 <: Any, Err](routes: Routes[Env1, Err]): Routes[Env1, Err] = - Routes.fromIterable(apply(HttpApp(routes)).routes) } /** diff --git a/zio-http/shared/src/main/scala/zio/http/Route.scala b/zio-http/shared/src/main/scala/zio/http/Route.scala index 5946fe05d6..fc25ba3c95 100644 --- a/zio-http/shared/src/main/scala/zio/http/Route.scala +++ b/zio-http/shared/src/main/scala/zio/http/Route.scala @@ -284,7 +284,7 @@ sealed trait Route[-Env, +Err] { self => * the request, or else this method will fail fatally. */ final def run(request: Request)(implicit trace: Trace): ZIO[Env, Either[Err, Response], Response] = - HttpApp(self).run(request) + Routes(self).run(request) /** * Returns a route that automatically translates all failures into responses, @@ -296,7 +296,10 @@ sealed trait Route[-Env, +Err] { self => def toHandler(implicit ev: Err <:< Response, trace: Trace): Handler[Env, Response, Request, Response] - final def toHttpApp: HttpApp[Env, Err] = HttpApp(self) + @deprecated("Use toRoutes instead") + final def toHttpApp(implicit ev: Err <:< Response): HttpApp[Env] = toHandler.toHttpApp + + final def toRoutes: Routes[Env, Err] = Routes(self) def transform[Env1]( f: Handler[Env, Response, Request, Response] => Handler[Env1, Response, Request, Response], diff --git a/zio-http/shared/src/main/scala/zio/http/Routes.scala b/zio-http/shared/src/main/scala/zio/http/Routes.scala index 7cef1197e6..b8dba3b80f 100644 --- a/zio-http/shared/src/main/scala/zio/http/Routes.scala +++ b/zio-http/shared/src/main/scala/zio/http/Routes.scala @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package zio.http import zio._ @@ -21,40 +22,14 @@ import zio.http.Routes.ApplyContextAspect import zio.http.codec.PathCodec /** - * Represents a collection of routes, each of which is defined by a pattern and - * a handler. This data type can be thought of as modeling a routing table, - * which decides where to direct every endpoint in an API based on both method - * and path of the request. - * - * When you are done building a collection of routes, you typically convert the - * routes into an [[zio.http.HttpApp]] value, which can be done with the - * `toHttpApp` method. + * An HTTP application is a collection of routes, all of whose errors have been + * handled through conversion into HTTP responses. * - * Routes may have handled or unhandled errors. A route of type `Route[Env, - * Throwable]`, for example, has not handled its errors by converting them into - * responses. Such unfinished routes cannot yet be converted into - * [[zio.http.HttpApp]] values. First, you must handle errors with the - * `handleError` or `handleErrorCause` methods. + * HTTP applications can be installed into a [[zio.http.Server]], which is + * capable of using them to serve requests. */ -final class Routes[-Env, +Err] private (val routes: Chunk[zio.http.Route[Env, Err]]) { self => - - /** - * Returns the concatenation of these routes with the specified routes. - */ - def ++[Env1 <: Env, Err1 >: Err](that: Routes[Env1, Err1]): Routes[Env1, Err1] = - new Routes(self.routes ++ that.routes) - - /** - * Appends the specified route to this collection of routes. - */ - def :+[Env1 <: Env, Err1 >: Err](route: zio.http.Route[Env1, Err1]): Routes[Env1, Err1] = - new Routes(routes :+ route) - - /** - * Prepends the specified route to this collection of routes. - */ - def +:[Env1 <: Env, Err1 >: Err](route: zio.http.Route[Env1, Err1]): Routes[Env1, Err1] = - new Routes(route +: routes) +final case class Routes[-Env, +Err](routes: Chunk[zio.http.Route[Env, Err]]) { self => + private var _tree: Routes.Tree[_] = null.asInstanceOf[Routes.Tree[_]] def @@[Env1 <: Env](aspect: Middleware[Env1]): Routes[Env1, Err] = aspect(self) @@ -70,14 +45,32 @@ final class Routes[-Env, +Err] private (val routes: Chunk[zio.http.Route[Env, Er def @@[Env0]: ApplyContextAspect[Env, Err, Env0] = new ApplyContextAspect[Env, Err, Env0](self) - def apply(request: Request)(implicit ev: Err <:< Response, trace: Trace): ZIO[Env, Response, Response] = - self.toHttpApp.apply(request) + /** + * Combines this HTTP application with the specified HTTP application. In case + * of route conflicts, the routes in this HTTP application take precedence + * over the routes in the specified HTTP application. + */ + def ++[Env1 <: Env, Err1 >: Err](that: Routes[Env1, Err1]): Routes[Env1, Err1] = + copy(routes = routes ++ that.routes) - def asEnvType[Env2](implicit ev: Env2 <:< Env): Routes[Env2, Err] = - self.asInstanceOf[Routes[Env2, Err]] + /** + * Prepend the specified route to this HttpApp + */ + def +:[Env1 <: Env, Err1 >: Err](route: zio.http.Route[Env1, Err1]): Routes[Env1, Err1] = + copy(routes = route +: routes) - def asErrorType[Err2](implicit ev: Err <:< Err2): Routes[Env, Err2] = - self.asInstanceOf[Routes[Env, Err2]] + /** + * Appends the specified route to this HttpApp + */ + def :+[Env1 <: Env, Err1 >: Err](route: zio.http.Route[Env1, Err1]): Routes[Env1, Err1] = + copy(routes = routes :+ route) + + /** + * Executes the HTTP application with the specified request input, returning + * an effect that will either succeed or fail with a Response. + */ + def apply(request: Request)(implicit ev: Err <:< Response): ZIO[Env, Response, Response] = + runZIO(request) /** * Handles all typed errors in the routes by converting them into responses. @@ -109,14 +102,14 @@ final class Routes[-Env, +Err] private (val routes: Chunk[zio.http.Route[Env, Er /** * Allows the transformation of the Err type through an Effectful program - * allowing one to build up a Routes in Stages delegates to the Route + * allowing one to build up a HttpApp in Stages delegates to the Route */ def mapErrorZIO[Err1](fxn: Err => ZIO[Any, Err1, Response])(implicit trace: Trace): Routes[Env, Err1] = new Routes(routes.map(_.mapErrorZIO(fxn))) /** * Allows the transformation of the Err type through a function allowing one - * to build up a Routes in Stages delegates to the Route + * to build up a HttpApp in Stages delegates to the Route */ def mapError[Err1](fxn: Err => Err1): Routes[Env, Err1] = new Routes(routes.map(_.mapError(fxn))) @@ -155,11 +148,21 @@ final class Routes[-Env, +Err] private (val routes: Chunk[zio.http.Route[Env, Er new Routes(routes.map(_.handleErrorRequestCauseZIO(f))) /** - * Returns new routes that have each been provided the specified environment, - * thus eliminating their requirement for any specific environment. + * Checks to see if the HTTP application may be defined at the specified + * request input. Note that it is still possible for an HTTP application to + * return a 404 Not Found response, which cannot be detected by this method. + * This method only checks for the presence of a handler that handles the + * method and path of the specified request. + */ + def isDefinedAt(request: Request)(implicit ev: Err <:< Response): Boolean = + tree(Trace.empty, ev).get(request.method, request.path).nonEmpty + + /** + * Provides the specified environment to the HTTP application, returning a new + * HTTP application that has no environmental requirements. */ def provideEnvironment(env: ZEnvironment[Env]): Routes[Any, Err] = - new Routes(routes.map(_.provideEnvironment(env))) + copy(routes = routes.map(_.provideEnvironment(env))) def run(request: Request)(implicit trace: Trace): ZIO[Env, Either[Err, Response], Response] = { @@ -190,63 +193,112 @@ final class Routes[-Env, +Err] private (val routes: Chunk[zio.http.Route[Env, Er } } + def run( + method: Method = Method.GET, + path: Path = Path.root, + headers: Headers = Headers.empty, + body: Body = Body.empty, + )(implicit ev: Err <:< Response): ZIO[Env, Nothing, Response] = + runZIO(Request(method = method, url = URL.root.path(path), headers = headers, body = body)) + + /** + * An alias for `apply`. + */ + def runZIO(request: Request)(implicit ev: Err <:< Response): ZIO[Env, Nothing, Response] = + toHandler(ev)(request) + /** * Returns new routes that automatically translate all failures into * responses, using best-effort heuristics to determine the appropriate HTTP * status code, and attaching error details using the HTTP header `Warning`. */ def sandbox(implicit trace: Trace): Routes[Env, Nothing] = - new Routes(routes.map(_.sandbox)) + Routes(routes.map(_.sandbox)) /** - * Returns new routes that are all timed out by the specified maximum - * duration. + * Returns a new HTTP application whose requests will be timed out after the + * specified duration elapses. */ def timeout(duration: Duration)(implicit trace: Trace): Routes[Env, Err] = self @@ Middleware.timeout(duration) /** - * Converts the routes into an app, which can be done only when errors are - * handled and converted into responses. + * Converts the HTTP application into a request handler. */ - def toHttpApp: HttpApp[Env, Err] = - HttpApp(self) + def toHandler(implicit ev: Err <:< Response): Handler[Env, Nothing, Request, Response] = { + implicit val trace: Trace = Trace.empty + Handler + .fromFunctionHandler[Request] { req => + val chunk = tree.get(req.method, req.path) + + if (chunk.length == 0) Handler.notFound + else if (chunk.length == 1) chunk(0) + else { + // TODO: Support precomputed fallback among all chunk elements: + chunk.tail.foldLeft(chunk.head) { (acc, h) => + acc.catchAll { response => + if (response.status == Status.NotFound) h + else Handler.fail(response) + } + } + } + } + .merge + } /** - * Returns new routes whose handlers are transformed by the specified + * Returns new new HttpApp whose handlers are transformed by the specified * function. */ def transform[Env1]( f: Handler[Env, Response, Request, Response] => Handler[Env1, Response, Request, Response], ): Routes[Env1, Err] = new Routes(routes.map(_.transform(f))) -} -object Routes extends RoutesVersionSpecific { /** - * Constructs new routes from a varargs of individual routes. + * Accesses the underlying tree that provides fast dispatch to handlers. */ - @deprecated("Use HttpApp.apply instead. Will be removed in the next release.") - def apply[Env, Err](route: zio.http.Route[Env, Err], routes: zio.http.Route[Env, Err]*): Routes[Env, Err] = - new Routes(Chunk(route) ++ Chunk.fromIterable(routes)) + def tree(implicit trace: Trace, ev: Err <:< Response): Routes.Tree[Env] = { + if (_tree eq null) { + _tree = Routes.Tree.fromRoutes(routes.asInstanceOf[Chunk[Route[Env, Response]]]) + } + _tree.asInstanceOf[Routes.Tree[Env]] + } +} - /** - * A empty routes value that contains no routes inside it. - */ - val empty: Routes[Any, Nothing] = new Routes(Chunk.empty) +object Routes extends RoutesCompanionVersionSpecific { /** - * Constructs new routes from an iterable of individual routes. + * An HTTP application that does not handle any routes. */ - @deprecated("Use HttpApp.fromIterable instead. Will be removed in the next release.") - def fromIterable[Env, Err](iterable: Iterable[Route[Env, Err]]): Routes[Env, Err] = - new Routes(Chunk.fromIterable(iterable)) + val empty: Routes[Any, Nothing] = Routes(Chunk.empty) + + def apply[Env, Err](route: Route[Env, Err], routes: Route[Env, Err]*): Routes[Env, Err] = + Routes(Chunk.fromIterable(route +: routes)) + + def fromIterable[Env, Err](routes: Iterable[Route[Env, Err]]): Routes[Env, Err] = + Routes(Chunk.fromIterable(routes)) - /** - * Constructs a singleton route from a handler that handles all possible - * methods and paths. You would only use this method for testing. - */ - @deprecated("Use HttpApp.singleton instead. Will be removed in the next release.") def singleton[Env, Err](h: Handler[Env, Err, (Path, Request), Response])(implicit trace: Trace): Routes[Env, Err] = Routes(Route.route(RoutePattern.any)(h)) + + private[http] final case class Tree[-Env](tree: RoutePattern.Tree[RequestHandler[Env, Response]]) { self => + final def ++[Env1 <: Env](that: Tree[Env1]): Tree[Env1] = + Tree(self.tree ++ that.tree) + + final def add[Env1 <: Env](route: Route[Env1, Response])(implicit trace: Trace): Tree[Env1] = + Tree(self.tree.add(route.routePattern, route.toHandler)) + + final def addAll[Env1 <: Env](routes: Iterable[Route[Env1, Response]])(implicit trace: Trace): Tree[Env1] = + Tree(self.tree.addAll(routes.map(r => (r.routePattern, r.toHandler)))) + + final def get(method: Method, path: Path): Chunk[RequestHandler[Env, Response]] = + tree.get(method, path) + } + private[http] object Tree { + val empty: Tree[Any] = Tree(RoutePattern.Tree.empty) + + def fromRoutes[Env](routes: Chunk[zio.http.Route[Env, Response]])(implicit trace: Trace): Tree[Env] = + empty.addAll(routes) + } } diff --git a/zio-http/shared/src/main/scala/zio/http/Server.scala b/zio-http/shared/src/main/scala/zio/http/Server.scala index 18f201faae..785e99387f 100644 --- a/zio-http/shared/src/main/scala/zio/http/Server.scala +++ b/zio-http/shared/src/main/scala/zio/http/Server.scala @@ -33,7 +33,14 @@ trait Server { /** * Installs the given HTTP application into the server. */ - def install[R](httpApp: HttpApp[R, Response])(implicit trace: Trace): URIO[R, Unit] + @deprecated("Install Routes instead. Will be removed in the next release.") + def install[R](httpApp: HttpApp[R])(implicit trace: Trace): URIO[R, Unit] = + install(httpApp.routes) + + /** + * Installs the given HTTP application into the server. + */ + def install[R](httpApp: Routes[R, Response])(implicit trace: Trace): URIO[R, Unit] /** * The port on which the server is listening. @@ -329,8 +336,23 @@ object Server extends ServerPlatformSpecific { } } + @deprecated("Serve Routes instead. Will be removed in the next release.") + def serve[R]( + httpApp: HttpApp[R], + )(implicit trace: Trace): URIO[R with Server, Nothing] = { + ZIO.logInfo("Starting the server...") *> + install(httpApp) *> + ZIO.logInfo("Server started") *> + ZIO.never + } + + @deprecated("Install Routes instead. Will be removed in the next release.") + def install[R](httpApp: HttpApp[R])(implicit trace: Trace): URIO[R with Server, Int] = { + ZIO.serviceWithZIO[Server](_.install(httpApp)) *> ZIO.service[Server].map(_.port) + } + def serve[R]( - httpApp: HttpApp[R, Response], + httpApp: Routes[R, Response], )(implicit trace: Trace): URIO[R with Server, Nothing] = { ZIO.logInfo("Starting the server...") *> install(httpApp) *> @@ -338,7 +360,7 @@ object Server extends ServerPlatformSpecific { ZIO.never } - def install[R](httpApp: HttpApp[R, Response])(implicit trace: Trace): URIO[R with Server, Int] = { + def install[R](httpApp: Routes[R, Response])(implicit trace: Trace): URIO[R with Server, Int] = { ZIO.serviceWithZIO[Server](_.install(httpApp)) *> ZIO.service[Server].map(_.port) } @@ -392,11 +414,12 @@ object Server extends ServerPlatformSpecific { driver: Driver, bindPort: Int, ) extends Server { - override def install[R](httpApp: HttpApp[R, Response])(implicit + override def install[R](httpApp: Routes[R, Response])(implicit trace: Trace, ): URIO[R, Unit] = ZIO.environment[R].flatMap(driver.addApp(httpApp, _)) override def port: Int = bindPort + } } diff --git a/zio-http/shared/src/main/scala/zio/http/Status.scala b/zio-http/shared/src/main/scala/zio/http/Status.scala index 089af773b9..1727675239 100644 --- a/zio-http/shared/src/main/scala/zio/http/Status.scala +++ b/zio-http/shared/src/main/scala/zio/http/Status.scala @@ -40,9 +40,10 @@ sealed trait Status extends Product with Serializable { self => lazy val text: String = code.toString /** - * Returns an HttpApp[Any, Nothing] that responses with this http status code. + * Returns an Routes[Any, Nothing] that responses with this http status code. */ - def toHttpApp(implicit trace: Trace): Handler[Any, Nothing, Any, Response] = Handler.status(self) + def toRoutes(implicit trace: Trace): Routes[Any, Nothing] = + Handler.status(self).toRoutes /** * Returns a Response with empty data and no headers. diff --git a/zio-http/shared/src/main/scala/zio/http/WebSocketApp.scala b/zio-http/shared/src/main/scala/zio/http/WebSocketApp.scala index abc784ff41..199cd592ee 100644 --- a/zio-http/shared/src/main/scala/zio/http/WebSocketApp.scala +++ b/zio-http/shared/src/main/scala/zio/http/WebSocketApp.scala @@ -72,9 +72,13 @@ final case class WebSocketApp[-R]( Response.fromSocketApp(self.provideEnvironment(env)) } - def toHttpAppWS(implicit trace: Trace): HttpApp[R, Response] = + @deprecated("Use toRoutes. Will be removed in the next release.") + def toHttpAppWS(implicit trace: Trace): HttpApp[R] = Handler.fromZIO(self.toResponse).toHttpApp + def toRoutes(implicit trace: Trace): Routes[R, Response] = + Handler.fromZIO(self.toResponse).toRoutes + def withConfig(config: WebSocketConfig): WebSocketApp[R] = copy(customConfig = Some(config)) } diff --git a/zio-http/shared/src/main/scala/zio/http/codec/PathCodec.scala b/zio-http/shared/src/main/scala/zio/http/codec/PathCodec.scala index ed7ced7ad1..a268b3eb2d 100644 --- a/zio-http/shared/src/main/scala/zio/http/codec/PathCodec.scala +++ b/zio-http/shared/src/main/scala/zio/http/codec/PathCodec.scala @@ -49,9 +49,9 @@ sealed trait PathCodec[A] { self => final def /[B](that: PathCodec[B])(implicit combiner: Combiner[A, B]): PathCodec[combiner.Out] = self ++ that - final def /[Env](routes: HttpApp[Env, Response])(implicit + final def /[Env](routes: Routes[Env, Response])(implicit ev: PathCodec[A] <:< PathCodec[Unit], - ): HttpApp[Env, Response] = + ): Routes[Env, Response] = routes.nest(ev(self)) final def annotate(metaData: MetaData[A]): PathCodec[A] = { diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/openapi/SwaggerUI.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/openapi/SwaggerUI.scala index bc10d1c36d..3ffe3c8a61 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/openapi/SwaggerUI.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/openapi/SwaggerUI.scala @@ -28,33 +28,10 @@ object SwaggerUI { * of the OpenAPI specification and is url encoded. */ //format: on - @deprecated("Use app instead. Will be removed with the next release.") def routes(path: PathCodec[Unit], api: OpenAPI, apis: OpenAPI*): Routes[Any, Response] = { routes(path, DefaultSwaggerUIVersion, api, apis: _*) } - /** - * Creates routes for serving the Swagger UI at the given path. - * - * Example: - * {{{ - * val routes: Routes[Any, Response] = ??? - * val openAPIv1: OpenAPI = ??? - * val openAPIv2: OpenAPI = ??? - * val swaggerUIRoutes = SwaggerUI.routes("docs" / "openapi", openAPIv1, openAPIv2) - * val routesWithSwagger = routes ++ swaggerUIRoutes - * }}} - * - * With this middleware in place, a request to `https://www.domain.com/[path]` - * would serve the Swagger UI. The different OpenAPI specifications are served - * at `https://www.domain.com/[path]/[title].json`. Where `title` is the title - * of the OpenAPI specification and is url encoded. - */ - // format: on - def app(path: PathCodec[Unit], api: OpenAPI, apis: OpenAPI*): HttpApp[Any, Response] = { - app(path, DefaultSwaggerUIVersion, api, apis: _*) - } - //format: off /** * Creates a middleware for serving the Swagger UI at the given path and with @@ -75,7 +52,6 @@ object SwaggerUI { * of the OpenAPI specification and is url encoded. */ //format: on - @deprecated("Use app instead. Will be removed with the next release.") def routes(path: PathCodec[Unit], version: String, api: OpenAPI, apis: OpenAPI*): Routes[Any, Response] = { import zio.http.template._ val basePath = Method.GET / path @@ -126,72 +102,4 @@ object SwaggerUI { Routes.fromIterable(jsonRoutes) :+ uiRoute } - /** - * Creates a middleware for serving the Swagger UI at the given path and with - * the given swagger ui version. - * - * Example: - * {{{ - * val routes: Routes[Any, Response] = ??? - * val openAPIv1: OpenAPI = ??? - * val openAPIv2: OpenAPI = ??? - * val swaggerUIRoutes = SwaggerUI.routes("docs" / "openapi", openAPIv1, openAPIv2) - * val routesWithSwagger = routes ++ swaggerUIRoutes - * }}} - * - * With this middleware in place, a request to `https://www.domain.com/[path]` - * would serve the Swagger UI. The different OpenAPI specifications are served - * at `https://www.domain.com/[path]/[title].json`. Where `title` is the title - * of the OpenAPI specification and is url encoded. - */ - // format: on - def app(path: PathCodec[Unit], version: String, api: OpenAPI, apis: OpenAPI*): HttpApp[Any, Response] = { - import zio.http.template._ - val basePath = Method.GET / path - val jsonRoutes = (api +: apis).map { api => - basePath / s"${URLEncoder.encode(api.info.title, Charsets.Utf8.name())}.json" -> handler { (_: Request) => - Response.json(api.toJson) - } - } - val jsonPaths = jsonRoutes.map(_.routePattern.pathCodec.render) - val jsonTitles = (api +: apis).map(_.info.title) - val jsonUrls = jsonTitles.zip(jsonPaths).map { case (title, path) => s"""{url: "$path", name: "$title"}""" } - val uiRoute = basePath -> handler { (_: Request) => - Response.html( - html( - head( - meta(charsetAttr := "utf-8"), - meta(nameAttr := "viewport", contentAttr := "width=device-width, initial-scale=1"), - meta(nameAttr := "description", contentAttr := "SwaggerUI"), - title("SwaggerUI"), - link(relAttr := "stylesheet", href := s"https://unpkg.com/swagger-ui-dist@$version/swagger-ui.css"), - link( - relAttr := "icon", - typeAttr := "image/png", - href := s"https://unpkg.com/swagger-ui-dist@$version/favicon-32x32.png", - ), - ), - body( - div(id := "swagger-ui"), - script(srcAttr := s"https://unpkg.com/swagger-ui-dist@$version/swagger-ui-bundle.js"), - script(srcAttr := s"https://unpkg.com/swagger-ui-dist@$version/swagger-ui-standalone-preset.js"), - Dom.raw(s"""""".stripMargin), - ), - ), - ) - } - HttpApp.fromIterable(jsonRoutes) :+ uiRoute - } } diff --git a/zio-http/shared/src/main/scala/zio/http/multipart/mixed/MultipartMixed.scala b/zio-http/shared/src/main/scala/zio/http/multipart/mixed/MultipartMixed.scala index 6752b70665..6c2ac2f465 100644 --- a/zio-http/shared/src/main/scala/zio/http/multipart/mixed/MultipartMixed.scala +++ b/zio-http/shared/src/main/scala/zio/http/multipart/mixed/MultipartMixed.scala @@ -75,8 +75,8 @@ object MultipartMixed { ZChannel .readWithCause( in => preamble(keep ++ in), - ZChannel.refailCause(_), - done => + ZChannel.refailCause, + _ => if (boundary.isClosing(buff)) ZChannel.succeed((Chunk.empty, true)) else if (boundary.isEncapsulating(buff)) @@ -161,7 +161,7 @@ object MultipartMixed { .readWithCause( in => parseBodyAux(buff, pendingCrlf, currLine ++ in, true), err => ZChannel.write(buff) *> ZChannel.refailCause(err), - done => { + _ => { // still possible that the current line is encapsulating or closing boundary if (boundary.isClosing(currLine)) ZChannel.write(buff) *> ZChannel.succeed((Chunk.empty, true)) @@ -178,12 +178,12 @@ object MultipartMixed { val (h, t) = currLine.splitAt(currLine.size - crlf.size + 1) if (t != currLine) { // also if we had a pending crlf we now know it's part of the content so we move it to the buffered part - parseBody(buff ++ pendingCrlf ++ h, Chunk.empty, t, false) + parseBody(buff ++ pendingCrlf ++ h, Chunk.empty, t, seekingBoundary = false) } else { ZChannel.readWithCause( - in => parseBodyAux(buff ++ pendingCrlf ++ h, Chunk.empty, t ++ in, false), + in => parseBodyAux(buff ++ pendingCrlf ++ h, Chunk.empty, t ++ in, seekingBoundary = false), err => ZChannel.write(buff ++ crlf ++ currLine) *> ZChannel.refailCause(err), - done => + _ => ZChannel.write(buff ++ crlf ++ currLine) *> ZChannel.fail( new IllegalStateException("multipart/chunked body ended with no boundary"), ), @@ -200,12 +200,12 @@ object MultipartMixed { else { // the crlf we just found can either be part of a following boundary or part of the content val nextLine = rest.drop(crlf.size) - parseBody(buff ++ pendingCrlf ++ h, crlf, nextLine, true) + parseBody(buff ++ pendingCrlf ++ h, crlf, nextLine, seekingBoundary = true) } case idx => // plain content // no need to check for boundary, just buffer and continue with parseBOL val (h, t) = currLine.splitAt(idx) - parseBody(buff ++ h, crlf, t.drop(crlf.size), true) + parseBody(buff ++ h, crlf, t.drop(crlf.size), seekingBoundary = true) } } } @@ -247,7 +247,7 @@ object MultipartMixed { ZChannel .fromZIO(pr.await) .flatMap { - case (rest, true) => + case (_, true) => epilogue case (rest, false) => cont(rest) @@ -267,7 +267,7 @@ object MultipartMixed { case (_, true) => epilogue } - .mapOut(Chunk.single(_)) + .mapOut(Chunk.single) val startPl: ZPipeline[Any, Throwable, Byte, Part] = startCh.toPipeline val result: ZStream[Any, Throwable, Part] = upstream.toStream >>> startPl @@ -301,9 +301,4 @@ object MultipartMixed { MultipartMixed(bytes, boundary, bufferSize) } - def fromPartsUUID(parts: ZStream[Any, Throwable, Part], bufferSize: Int = 8192): ZIO[Any, Nothing, MultipartMixed] = { - Boundary.randomUUID - .map(fromParts(parts, _, bufferSize)) - } - }