From 088bbfa0d65de836f142694b182ecf127236aa2f Mon Sep 17 00:00:00 2001 From: Ivan Klass Date: Mon, 25 Nov 2024 10:53:24 +0100 Subject: [PATCH] http4s - migrate from Blaze to Ember --- build.sbt | 18 +++++------ .../client/http4s/Http4sClientTests.scala | 5 ++- .../sttp/tapir/client/tests/HttpServer.scala | 20 ++++++------ doc/server/http4s.md | 17 ++++------ doc/server/zio-http4s.md | 18 ++++------- doc/tutorials/07_cats_effect.md | 32 +++++++++---------- .../redoc/bundle/RedocInterpreterTest.scala | 10 +++--- .../bundle/SwaggerInterpreterTest.scala | 10 +++--- .../examples/HelloWorldHttp4sServer.scala | 11 +++---- .../examples/ZioEnvExampleHttp4sServer.scala | 22 +++++-------- .../examples/ZioExampleHttp4sServer.scala | 19 ++++------- .../ZioPartialServerLogicHttp4s.scala | 22 ++++++------- .../examples/client/Http4sClientExample.scala | 2 +- .../errors/ErrorUnionTypesHttp4sServer.scala | 11 +++---- ...leEndpointsDocumentationHttp4sServer.scala | 11 +++---- .../RedocContextPathHttp4sServer.scala | 11 +++---- .../security/OAuth2GithubHttp4sServer.scala | 11 +++---- .../streaming/ProxyHttp4sFs2Server.scala | 14 ++++---- .../streaming/StreamingHttp4sFs2Server.scala | 10 +++--- .../StreamingHttp4sFs2ServerOrError.scala | 14 ++++---- .../websocket/WebSocketHttp4sServer.scala | 11 +++---- generated-doc/out/server/http4s.md | 11 +++---- generated-doc/out/server/zio-http4s.md | 16 ++++------ generated-doc/out/tutorials/07_cats_effect.md | 32 +++++++++---------- .../scala/sttp/tapir/perf/http4s/Http4s.scala | 26 +++++++-------- project/Versions.scala | 2 -- .../http4s/Http4sServerInterpreter.scala | 2 +- .../server/http4s/Http4sServerTest.scala | 11 ++++--- .../http4s/Http4sTestServerInterpreter.scala | 13 ++++---- .../ztapir/ZHttp4sTestServerInterpreter.scala | 11 ++++--- .../server/tests/TestServerInterpreter.scala | 10 ++++-- 31 files changed, 200 insertions(+), 233 deletions(-) diff --git a/build.sbt b/build.sbt index 9a972b02e9..f046f00de2 100644 --- a/build.sbt +++ b/build.sbt @@ -390,7 +390,7 @@ lazy val clientTestServer = (projectMatrix in file("client/testserver")) publish / skip := true, libraryDependencies ++= Seq( "org.http4s" %% "http4s-dsl" % Versions.http4s, - "org.http4s" %% "http4s-blaze-server" % Versions.http4sBlazeServer, + "org.http4s" %% "http4s-ember-server" % Versions.http4s, "org.http4s" %% "http4s-circe" % Versions.http4s, logback ), @@ -533,7 +533,7 @@ lazy val perfTests: ProjectMatrix = (projectMatrix in file("perf-tests")) "io.github.classgraph" % "classgraph" % "4.8.179", "org.http4s" %% "http4s-core" % Versions.http4s, "org.http4s" %% "http4s-dsl" % Versions.http4s, - "org.http4s" %% "http4s-blaze-server" % Versions.http4sBlazeServer, + "org.http4s" %% "http4s-ember-server" % Versions.http4s, "org.typelevel" %%% "cats-effect" % Versions.catsEffect, logback ), @@ -1142,7 +1142,7 @@ lazy val swaggerUiBundle: ProjectMatrix = (projectMatrix in file("docs/swagger-u name := "tapir-swagger-ui-bundle", libraryDependencies ++= Seq( "com.softwaremill.sttp.apispec" %% "openapi-circe-yaml" % Versions.sttpApispec, - "org.http4s" %% "http4s-blaze-server" % Versions.http4sBlazeServer % Test, + "org.http4s" %% "http4s-ember-server" % Versions.http4s % Test, scalaTest.value % Test ) ) @@ -1168,7 +1168,7 @@ lazy val redocBundle: ProjectMatrix = (projectMatrix in file("docs/redoc-bundle" name := "tapir-redoc-bundle", libraryDependencies ++= Seq( "com.softwaremill.sttp.apispec" %% "openapi-circe-yaml" % Versions.sttpApispec, - "org.http4s" %% "http4s-blaze-server" % Versions.http4sBlazeServer % Test, + "org.http4s" %% "http4s-ember-server" % Versions.http4s % Test, scalaTest.value % Test ) ) @@ -1303,7 +1303,7 @@ lazy val http4sServer: ProjectMatrix = (projectMatrix in file("server/http4s-ser scalaVersions = scala2And3Versions, settings = commonJvmSettings ++ Seq { libraryDependencies ++= Seq( - "org.http4s" %%% "http4s-blaze-server" % Versions.http4sBlazeServer % Test + "org.http4s" %%% "http4s-ember-server" % Versions.http4s % Test ) } ) @@ -1319,7 +1319,7 @@ lazy val http4sServerZio: ProjectMatrix = (projectMatrix in file("server/http4s- name := "tapir-http4s-server-zio", libraryDependencies ++= Seq( "dev.zio" %% "zio-interop-cats" % Versions.zioInteropCats, - "org.http4s" %% "http4s-blaze-server" % Versions.http4sBlazeServer % Test + "org.http4s" %% "http4s-ember-server" % Versions.http4s % Test ) ) .jvmPlatform(scalaVersions = scala2And3Versions, settings = commonJvmSettings) @@ -1887,7 +1887,7 @@ lazy val http4sClient: ProjectMatrix = (projectMatrix in file("client/http4s-cli name := "tapir-http4s-client", libraryDependencies ++= Seq( "org.http4s" %% "http4s-core" % Versions.http4s, - "org.http4s" %% "http4s-blaze-client" % Versions.http4sBlazeClient % Test, + "org.http4s" %% "http4s-ember-client" % Versions.http4s % Test, "com.softwaremill.sttp.shared" %% "fs2" % Versions.sttpShared % Optional ) ) @@ -2049,7 +2049,7 @@ lazy val examples: ProjectMatrix = (projectMatrix in file("examples")) "com.github.jwt-scala" %% "jwt-circe" % Versions.jwtScala, "org.http4s" %% "http4s-dsl" % Versions.http4s, "org.http4s" %% "http4s-circe" % Versions.http4s, - "org.http4s" %% "http4s-blaze-server" % Versions.http4sBlazeServer, + "org.http4s" %% "http4s-ember-server" % Versions.http4s, "org.mock-server" % "mockserver-netty" % Versions.mockServer, "io.opentelemetry" % "opentelemetry-sdk" % Versions.openTelemetry, "io.opentelemetry" % "opentelemetry-sdk-metrics" % Versions.openTelemetry, @@ -2117,7 +2117,7 @@ lazy val documentation: ProjectMatrix = (projectMatrix in file("generated-doc")) name := "doc", libraryDependencies ++= Seq( "org.playframework" %% "play-netty-server" % Versions.playServer, - "org.http4s" %% "http4s-blaze-server" % Versions.http4sBlazeServer, + "org.http4s" %% "http4s-ember-server" % Versions.http4s, "com.softwaremill.sttp.apispec" %% "openapi-circe-yaml" % Versions.sttpApispec, "com.softwaremill.sttp.apispec" %% "asyncapi-circe-yaml" % Versions.sttpApispec ), diff --git a/client/http4s-client/src/test/scala/sttp/tapir/client/http4s/Http4sClientTests.scala b/client/http4s-client/src/test/scala/sttp/tapir/client/http4s/Http4sClientTests.scala index 4b81740e65..d460da66e2 100644 --- a/client/http4s-client/src/test/scala/sttp/tapir/client/http4s/Http4sClientTests.scala +++ b/client/http4s-client/src/test/scala/sttp/tapir/client/http4s/Http4sClientTests.scala @@ -1,11 +1,10 @@ package sttp.tapir.client.http4s import cats.effect.IO -import org.http4s.blaze.client.BlazeClientBuilder +import org.http4s.ember.client.EmberClientBuilder import org.http4s.{Request, Response, Uri} import sttp.tapir.client.tests.ClientTests import sttp.tapir.{DecodeResult, Endpoint} -import scala.concurrent.ExecutionContext.global abstract class Http4sClientTests[R] extends ClientTests[R] { override def send[A, I, E, O]( @@ -35,7 +34,7 @@ abstract class Http4sClientTests[R] extends ClientTests[R] { } private def sendAndParseResponse[Result](request: Request[IO], parseResponse: Response[IO] => IO[Result]) = - BlazeClientBuilder[IO](global).resource.use { client => + EmberClientBuilder.default[IO].build.use { client => client.run(request).use(parseResponse) } } diff --git a/client/testserver/src/main/scala/sttp/tapir/client/tests/HttpServer.scala b/client/testserver/src/main/scala/sttp/tapir/client/tests/HttpServer.scala index 13b7303794..9fe56619bd 100644 --- a/client/testserver/src/main/scala/sttp/tapir/client/tests/HttpServer.scala +++ b/client/testserver/src/main/scala/sttp/tapir/client/tests/HttpServer.scala @@ -3,12 +3,13 @@ package sttp.tapir.client.tests import cats.effect._ import cats.effect.std.Queue import cats.effect.unsafe.implicits.global +import com.comcast.ip4s import cats.implicits._ import fs2.{Pipe, Stream} import org.http4s.dsl.io._ import org.http4s.headers.{Accept, `Content-Type`} import org.http4s.server.Router -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.middleware._ import org.http4s.server.websocket.WebSocketBuilder2 import org.http4s.websocket.WebSocketFrame @@ -33,7 +34,7 @@ class HttpServer(port: Port) { private val logger = LoggerFactory.getLogger(getClass) - private var stopServer: IO[Unit] = _ + private val stopServer: Deferred[IO, Unit] = Deferred.unsafe[IO, Unit] // @@ -213,22 +214,19 @@ class HttpServer(port: Port) { // def start(): Unit = { - val (_, _stopServer) = BlazeServerBuilder[IO] - .withExecutionContext(ExecutionContext.global) - .bindHttp(port) + EmberServerBuilder + .default[IO] + .withPort(ip4s.Port.fromInt(port).get) .withHttpWebSocketApp(app) - .resource - .map(_.address.getPort) - .allocated + .build + .use(_ => stopServer.get) .unsafeRunSync() - stopServer = _stopServer - logger.info(s"Server on port $port started") } def close(): Unit = { - stopServer.unsafeRunSync() + stopServer.complete(()).unsafeRunSync() logger.info(s"Server on port $port stopped") } } diff --git a/doc/server/http4s.md b/doc/server/http4s.md index 184bd97df2..829a0020e3 100644 --- a/doc/server/http4s.md +++ b/doc/server/http4s.md @@ -52,11 +52,11 @@ The capability can be added to the classpath independently of the interpreter th ## Http4s backends Http4s integrates with a couple of [server backends](https://http4s.org/v1.0/integrations/), the most popular being -Blaze and Ember. In the [examples](../examples.md) and throughout the docs we use Blaze, but other backends can be used +Blaze and Ember. In the [examples](../examples.md) and throughout the docs we use Ember, but other backends can be used as well. This means adding another dependency, such as: ```scala -"org.http4s" %% "http4s-blaze-server" % Http4sVersion +"org.http4s" %% "http4s-ember-server" % Http4sVersion ``` ## Web sockets @@ -75,24 +75,21 @@ import sttp.tapir.* import sttp.tapir.server.http4s.Http4sServerInterpreter import cats.effect.IO import org.http4s.HttpRoutes -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import org.http4s.server.websocket.WebSocketBuilder2 import fs2.* import scala.concurrent.ExecutionContext -given ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global - val wsEndpoint: PublicEndpoint[Unit, Unit, Pipe[IO, String, String], Fs2Streams[IO] with WebSockets] = endpoint.get.in("count").out(webSocketBody[String, CodecFormat.TextPlain, String, CodecFormat.TextPlain](Fs2Streams[IO])) val wsRoutes: WebSocketBuilder2[IO] => HttpRoutes[IO] = Http4sServerInterpreter[IO]().toWebSocketRoutes(wsEndpoint.serverLogicSuccess[IO](_ => ???)) - -BlazeServerBuilder[IO] - .withExecutionContext(summon[ExecutionContext]) - .bindHttp(8080, "localhost") - .withHttpWebSocketApp(wsb => Router("/" -> wsRoutes(wsb)).orNotFound) + +EmberServerBuilder + .default[IO] + .withHttpWebSocketApp(wsb => Router("/" -> wsRoutes(wsb)).orNotFound) ``` ## Server Sent Events diff --git a/doc/server/zio-http4s.md b/doc/server/zio-http4s.md index 9a69d155a8..f4286b1410 100644 --- a/doc/server/zio-http4s.md +++ b/doc/server/zio-http4s.md @@ -99,11 +99,11 @@ The capability can be added to the classpath independently of the interpreter th ## Http4s backends Http4s integrates with a couple of [server backends](https://http4s.org/v1.0/integrations/), the most popular being -Blaze and Ember. In the [examples](../examples.md) and throughout the docs we use Blaze, but other backends can be used +Blaze and Ember. In the [examples](../examples.md) and throughout the docs we use Ember, but other backends can be used as well. This means adding another dependency, such as: ```scala -"org.http4s" %% "http4s-blaze-server" % Http4sVersion +"org.http4s" %% "http4s-ember-server" % Http4sVersion ``` ## Web sockets @@ -121,7 +121,7 @@ import sttp.tapir.{CodecFormat, PublicEndpoint} import sttp.tapir.ztapir.* import sttp.tapir.server.http4s.ztapir.ZHttp4sServerInterpreter import org.http4s.HttpRoutes -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import org.http4s.server.websocket.WebSocketBuilder2 import scala.concurrent.ExecutionContext @@ -131,8 +131,6 @@ import zio.stream.Stream def runtime: Runtime[Any] = ??? // provided by ZIOAppDefault -given ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global - val wsEndpoint: PublicEndpoint[Unit, Unit, Stream[Throwable, String] => Stream[Throwable, String], ZioStreams with WebSockets] = endpoint.get.in("count").out(webSocketBody[String, CodecFormat.TextPlain, String, CodecFormat.TextPlain](ZioStreams)) @@ -141,13 +139,11 @@ val wsRoutes: WebSocketBuilder2[Task] => HttpRoutes[Task] = val serve: Task[Unit] = ZIO.executor.flatMap(executor => - BlazeServerBuilder[Task] - .withExecutionContext(executor.asExecutionContext) - .bindHttp(8080, "localhost") + EmberServerBuilder + .default[Task] .withHttpWebSocketApp(wsb => Router("/" -> wsRoutes(wsb)).orNotFound) - .serve - .compile - .drain + .build + .useForever ) ``` diff --git a/doc/tutorials/07_cats_effect.md b/doc/tutorials/07_cats_effect.md index 1c774134c0..875db49890 100644 --- a/doc/tutorials/07_cats_effect.md +++ b/doc/tutorials/07_cats_effect.md @@ -132,11 +132,11 @@ standard code to start a server and handle requests until the application is int ```scala //> using dep com.softwaremill.sttp.tapir::tapir-core:@VERSION@ //> using dep com.softwaremill.sttp.tapir::tapir-http4s-server:@VERSION@ -//> using dep org.http4s::http4s-blaze-server:0.23.16 +//> using dep org.http4s::http4s-ember-server:0.23.29 import cats.effect.{ExitCode, IO, IOApp} import org.http4s.HttpRoutes -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import sttp.tapir.* import sttp.tapir.server.http4s.Http4sServerInterpreter @@ -154,12 +154,11 @@ object HelloWorldTapir extends IOApp: .toRoutes(helloWorldEndpoint) override def run(args: List[String]): IO[ExitCode] = - BlazeServerBuilder[IO] - .bindHttp(8080, "localhost") + EmberServerBuilder + .default[IO] .withHttpApp(Router("/" -> helloWorldRoutes).orNotFound) - .resource - .use(_ => IO.never) - .as(ExitCode.Success) + .build + .useForever ``` First of all, you might notice that instead of the `@main` method, we are extending the `IOApp` trait. This is needed, @@ -169,8 +168,8 @@ the `IOApp` will handle evaluating the `IO` description and actually running the Secondly, with http4s we need to use a specific server implementation (http4s itself is only an API to define endpoints - kind of a middle-man between Tapir and low-level networking code). We can choose from `blaze` and `ember` servers, here -we're using the `blaze` one, which is reflected in the additional dependency and the server configuration constructor: -`BlazeServerBuilder`. +we're using the `ember` one, which is reflected in the additional dependency and the server configuration constructor: +`EmberServerBuilder`. Finally, we've got the `run` method implementation, which attaches our interpreted route to the root context `/` and exposes the server on `localhost:8080`. @@ -195,12 +194,12 @@ the second step that we need to perform: //> using dep com.softwaremill.sttp.tapir::tapir-core:@VERSION@ //> using dep com.softwaremill.sttp.tapir::tapir-http4s-server:@VERSION@ //> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:@VERSION@ -//> using dep org.http4s::http4s-blaze-server:0.23.16 +//> using dep org.http4s::http4s-ember-server:0.23.29 import cats.effect.{ExitCode, IO, IOApp} import cats.syntax.all.* import org.http4s.HttpRoutes -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import sttp.tapir.* import sttp.tapir.server.http4s.Http4sServerInterpreter @@ -226,17 +225,16 @@ object HelloWorldTapir extends IOApp: val allRoutes: HttpRoutes[IO] = helloWorldRoutes <+> swaggerRoutes override def run(args: List[String]): IO[ExitCode] = - BlazeServerBuilder[IO] - .bindHttp(8080, "localhost") + EmberServerBuilder + .default[IO] .withHttpApp(Router("/" -> allRoutes).orNotFound) - .resource - .use(_ => IO.never) - .as(ExitCode.Success) + .build + .useForever ``` Hence, we first generate endpoint descriptions, which correspond to exposing the Swagger UI (containing the generated OpenAPI yaml for our `/hello/world` endpoint), which use `IO` to express their server logic. Then, we interpret those -endpoints as `HttpRoutes[IO]`, which we can expose using http4's blaze server. +endpoints as `HttpRoutes[IO]`, which we can expose using http4's ember server. ## Other concepts covered so far diff --git a/docs/redoc-bundle/src/test/scala/sttp/tapir/redoc/bundle/RedocInterpreterTest.scala b/docs/redoc-bundle/src/test/scala/sttp/tapir/redoc/bundle/RedocInterpreterTest.scala index 81e36d3412..97b75ff91a 100644 --- a/docs/redoc-bundle/src/test/scala/sttp/tapir/redoc/bundle/RedocInterpreterTest.scala +++ b/docs/redoc-bundle/src/test/scala/sttp/tapir/redoc/bundle/RedocInterpreterTest.scala @@ -2,8 +2,9 @@ package sttp.tapir.redoc.bundle import cats.effect.IO import cats.effect.unsafe.implicits.global +import com.comcast.ip4s.Port import org.http4s.HttpRoutes -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import org.scalatest.Assertion import org.scalatest.funsuite.AsyncFunSuite @@ -66,10 +67,11 @@ class RedocInterpreterTest extends AsyncFunSuite with Matchers { .fromEndpoints[IO](List(testEndpoint), "The tapir library", "1.0.0") ) - BlazeServerBuilder[IO] - .bindHttp(0, "localhost") + EmberServerBuilder + .default[IO] + .withPort(Port.fromInt(0).get) .withHttpApp(Router(s"/${context.mkString("/")}" -> redocUIRoutes).orNotFound) - .resource + .build .use { server => IO { val port = server.address.getPort diff --git a/docs/swagger-ui-bundle/src/test/scala/sttp/tapir/swagger/bundle/SwaggerInterpreterTest.scala b/docs/swagger-ui-bundle/src/test/scala/sttp/tapir/swagger/bundle/SwaggerInterpreterTest.scala index 0eaad61e02..a1b984ca8b 100644 --- a/docs/swagger-ui-bundle/src/test/scala/sttp/tapir/swagger/bundle/SwaggerInterpreterTest.scala +++ b/docs/swagger-ui-bundle/src/test/scala/sttp/tapir/swagger/bundle/SwaggerInterpreterTest.scala @@ -2,8 +2,9 @@ package sttp.tapir.swagger.bundle import cats.effect.IO import cats.effect.unsafe.implicits.global +import com.comcast.ip4s.Port import org.http4s.HttpRoutes -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import org.scalatest.Assertion import org.scalatest.funsuite.AsyncFunSuite @@ -33,10 +34,11 @@ class SwaggerInterpreterTest extends AsyncFunSuite with Matchers { .fromEndpoints[IO](List(testEndpoint), "The tapir library", "1.0.0") ) - BlazeServerBuilder[IO] - .bindHttp(0, "localhost") + EmberServerBuilder + .default[IO] + .withPort(Port.fromInt(0).get) .withHttpApp(Router(s"/${context.mkString("/")}" -> swaggerUIRoutes).orNotFound) - .resource + .build .use { server => IO { val port = server.address.getPort diff --git a/examples/src/main/scala/sttp/tapir/examples/HelloWorldHttp4sServer.scala b/examples/src/main/scala/sttp/tapir/examples/HelloWorldHttp4sServer.scala index a27248580e..1dc8e87256 100644 --- a/examples/src/main/scala/sttp/tapir/examples/HelloWorldHttp4sServer.scala +++ b/examples/src/main/scala/sttp/tapir/examples/HelloWorldHttp4sServer.scala @@ -3,14 +3,14 @@ //> using dep com.softwaremill.sttp.tapir::tapir-core:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-http4s-server:1.11.9 //> using dep com.softwaremill.sttp.client3::core:3.9.8 -//> using dep org.http4s::http4s-blaze-server:0.23.16 +//> using dep org.http4s::http4s-ember-server:0.23.16 package sttp.tapir.examples import cats.effect.* import cats.syntax.all.* import org.http4s.HttpRoutes -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import sttp.client3.* import sttp.shared.Identity @@ -33,11 +33,10 @@ object HelloWorldHttp4sServer extends IOApp: override def run(args: List[String]): IO[ExitCode] = // starting the server - BlazeServerBuilder[IO] - .withExecutionContext(ec) - .bindHttp(8080, "localhost") + EmberServerBuilder + .default[IO] .withHttpApp(Router("/" -> helloWorldRoutes).orNotFound) - .resource + .build .use { _ => IO { val backend: SttpBackend[Identity, Any] = HttpURLConnectionBackend() diff --git a/examples/src/main/scala/sttp/tapir/examples/ZioEnvExampleHttp4sServer.scala b/examples/src/main/scala/sttp/tapir/examples/ZioEnvExampleHttp4sServer.scala index 70606a24d2..b8e60b21ec 100644 --- a/examples/src/main/scala/sttp/tapir/examples/ZioEnvExampleHttp4sServer.scala +++ b/examples/src/main/scala/sttp/tapir/examples/ZioEnvExampleHttp4sServer.scala @@ -6,7 +6,7 @@ //> using dep com.softwaremill.sttp.tapir::tapir-http4s-server-zio:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-zio:1.11.9 -//> using dep org.http4s::http4s-blaze-server:0.23.16 +//> using dep org.http4s::http4s-ember-server:0.23.29 //> using dep dev.zio::zio-interop-cats:23.1.0.3 package sttp.tapir.examples @@ -14,7 +14,7 @@ package sttp.tapir.examples import cats.syntax.all.* import io.circe.generic.auto.* import org.http4s.* -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import sttp.tapir.PublicEndpoint import sttp.tapir.generic.auto.* @@ -71,17 +71,11 @@ object ZioEnvExampleHttp4sServer extends ZIOAppDefault: .toRoutes // Starting the server - val serve: ZIO[PetService, Throwable, Unit] = { - ZIO.executor.flatMap(executor => - BlazeServerBuilder[RIO[PetService, *]] - .withExecutionContext(executor.asExecutionContext) - .bindHttp(8080, "localhost") - .withHttpApp(Router("/" -> (petRoutes <+> swaggerRoutes)).orNotFound) - .serve - .compile - .drain - ) - - } + val serve: ZIO[PetService, Throwable, Unit] = + EmberServerBuilder + .default[RIO[PetService, *]] + .withHttpApp(Router("/" -> (petRoutes <+> swaggerRoutes)).orNotFound) + .build + .useForever override def run: URIO[Any, ExitCode] = serve.provide(PetService.live).exitCode diff --git a/examples/src/main/scala/sttp/tapir/examples/ZioExampleHttp4sServer.scala b/examples/src/main/scala/sttp/tapir/examples/ZioExampleHttp4sServer.scala index ce4c544917..334f22f3f1 100644 --- a/examples/src/main/scala/sttp/tapir/examples/ZioExampleHttp4sServer.scala +++ b/examples/src/main/scala/sttp/tapir/examples/ZioExampleHttp4sServer.scala @@ -5,7 +5,7 @@ //> using dep com.softwaremill.sttp.tapir::tapir-http4s-server-zio:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-zio:1.11.9 -//> using dep org.http4s::http4s-blaze-server:0.23.16 +//> using dep org.http4s::http4s-ember-server:0.23.29 //> using dep dev.zio::zio-interop-cats:23.1.0.3 package sttp.tapir.examples @@ -13,7 +13,7 @@ package sttp.tapir.examples import cats.syntax.all.* import io.circe.generic.auto.* import org.http4s.* -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import sttp.tapir.PublicEndpoint import sttp.tapir.generic.auto.* @@ -59,15 +59,10 @@ object ZioExampleHttp4sServer extends ZIOAppDefault: .toRoutes // Starting the server - val serve: Task[Unit] = - ZIO.executor.flatMap(executor => - BlazeServerBuilder[Task] - .withExecutionContext(executor.asExecutionContext) - .bindHttp(8080, "localhost") - .withHttpApp(Router("/" -> (petRoutes <+> swaggerRoutes)).orNotFound) - .serve - .compile - .drain - ) + val serve: Task[Unit] = EmberServerBuilder + .default[Task] + .withHttpApp(Router("/" -> (petRoutes <+> swaggerRoutes)).orNotFound) + .build + .useForever override def run: URIO[Any, ExitCode] = serve.exitCode diff --git a/examples/src/main/scala/sttp/tapir/examples/ZioPartialServerLogicHttp4s.scala b/examples/src/main/scala/sttp/tapir/examples/ZioPartialServerLogicHttp4s.scala index a44eb1217e..fd61322d4e 100644 --- a/examples/src/main/scala/sttp/tapir/examples/ZioPartialServerLogicHttp4s.scala +++ b/examples/src/main/scala/sttp/tapir/examples/ZioPartialServerLogicHttp4s.scala @@ -4,13 +4,13 @@ //> using dep com.softwaremill.sttp.tapir::tapir-core:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-http4s-server-zio:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-zio:1.11.9 -//> using dep org.http4s::http4s-blaze-server:0.23.16 +//> using dep org.http4s::http4s-ember-server:0.23.29 //> using dep com.softwaremill.sttp.client3::async-http-client-backend-zio:3.10.1 package sttp.tapir.examples import org.http4s.* -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import sttp.client3.* import sttp.client3.asynchttpclient.zio.AsyncHttpClientZioBackend @@ -78,16 +78,14 @@ object ZioPartialServerLogicHttp4s extends ZIOAppDefault: // override def run: URIO[Any, ExitCode] = - ZIO.executor.flatMap(executor => - BlazeServerBuilder[RIO[UserService, *]] - .withExecutionContext(executor.asExecutionContext) - .bindHttp(8080, "localhost") - .withHttpApp(Router("/" -> helloWorldRoutes).orNotFound) - .resource - .use(_ => test) - .provide(UserService.live) - .exitCode - ) + EmberServerBuilder + .default[RIO[UserService, *]] + .withHttpApp(Router("/" -> helloWorldRoutes).orNotFound) + .build + .use(_ => test) + .provide(UserService.live) + .exitCode + end ZioPartialServerLogicHttp4s object UserAuthenticationLayer: diff --git a/examples/src/main/scala/sttp/tapir/examples/client/Http4sClientExample.scala b/examples/src/main/scala/sttp/tapir/examples/client/Http4sClientExample.scala index 7a3d6e2fda..bbc0b39c79 100644 --- a/examples/src/main/scala/sttp/tapir/examples/client/Http4sClientExample.scala +++ b/examples/src/main/scala/sttp/tapir/examples/client/Http4sClientExample.scala @@ -4,7 +4,7 @@ //> using dep com.softwaremill.sttp.tapir::tapir-http4s-client:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-json-circe:1.11.9 //> using dep org.http4s::http4s-circe:0.23.27 -//> using dep org.http4s::http4s-blaze-server:0.23.16 +//> using dep org.http4s::http4s-ember-server:0.23.29 //> using dep org.http4s::http4s-dsl:0.23.27 package sttp.tapir.examples.client diff --git a/examples/src/main/scala/sttp/tapir/examples/errors/ErrorUnionTypesHttp4sServer.scala b/examples/src/main/scala/sttp/tapir/examples/errors/ErrorUnionTypesHttp4sServer.scala index 2b0f9db1f6..3c28c31283 100644 --- a/examples/src/main/scala/sttp/tapir/examples/errors/ErrorUnionTypesHttp4sServer.scala +++ b/examples/src/main/scala/sttp/tapir/examples/errors/ErrorUnionTypesHttp4sServer.scala @@ -3,7 +3,7 @@ //> using dep com.softwaremill.sttp.tapir::tapir-core:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-http4s-server:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-json-circe:1.11.9 -//> using dep org.http4s::http4s-blaze-server:0.23.16 +//> using dep org.http4s::http4s-ember-server:0.23.29 //> using dep com.softwaremill.sttp.client3::core:3.9.8 package sttp.tapir.examples.errors @@ -11,7 +11,7 @@ package sttp.tapir.examples.errors import cats.effect.* import io.circe.generic.auto.* import org.http4s.HttpRoutes -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import sttp.client3.* import sttp.model.StatusCode @@ -71,11 +71,10 @@ object ErrorUnionTypesHttp4sServer extends IOApp: override def run(args: List[String]): IO[ExitCode] = // starting the server - BlazeServerBuilder[IO] - .withExecutionContext(ec) - .bindHttp(8080, "localhost") + EmberServerBuilder + .default[IO] .withHttpApp(Router("/" -> helloWorldRoutes).orNotFound) - .resource + .build .use { _ => IO { val backend: SttpBackend[Identity, Any] = HttpURLConnectionBackend() diff --git a/examples/src/main/scala/sttp/tapir/examples/openapi/MultipleEndpointsDocumentationHttp4sServer.scala b/examples/src/main/scala/sttp/tapir/examples/openapi/MultipleEndpointsDocumentationHttp4sServer.scala index 67860ab416..e476484c92 100644 --- a/examples/src/main/scala/sttp/tapir/examples/openapi/MultipleEndpointsDocumentationHttp4sServer.scala +++ b/examples/src/main/scala/sttp/tapir/examples/openapi/MultipleEndpointsDocumentationHttp4sServer.scala @@ -4,7 +4,7 @@ //> using dep com.softwaremill.sttp.tapir::tapir-json-circe:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-http4s-server:1.11.9 -//> using dep org.http4s::http4s-blaze-server:0.23.16 +//> using dep org.http4s::http4s-ember-server:0.23.29 package sttp.tapir.examples.openapi @@ -12,7 +12,7 @@ import cats.effect.* import cats.syntax.all.* import io.circe.generic.auto.* import org.http4s.HttpRoutes -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import sttp.tapir.* import sttp.tapir.generic.auto.* @@ -73,11 +73,10 @@ object MultipleEndpointsDocumentationHttp4sServer extends IOApp: override def run(args: List[String]): IO[ExitCode] = // starting the server - BlazeServerBuilder[IO] - .withExecutionContext(ec) - .bindHttp(8080, "localhost") + EmberServerBuilder + .default[IO] .withHttpApp(Router("/" -> (routes)).orNotFound) - .resource + .build .use { _ => IO { println("Go to: http://localhost:8080/docs") diff --git a/examples/src/main/scala/sttp/tapir/examples/openapi/RedocContextPathHttp4sServer.scala b/examples/src/main/scala/sttp/tapir/examples/openapi/RedocContextPathHttp4sServer.scala index 9e187081c0..c699541bd8 100644 --- a/examples/src/main/scala/sttp/tapir/examples/openapi/RedocContextPathHttp4sServer.scala +++ b/examples/src/main/scala/sttp/tapir/examples/openapi/RedocContextPathHttp4sServer.scala @@ -3,15 +3,15 @@ //> using dep com.softwaremill.sttp.tapir::tapir-core:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-redoc-bundle:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-http4s-server:1.11.9 -//> using dep org.http4s::http4s-blaze-server:0.23.16 +//> using dep org.http4s::http4s-ember-server:0.23.29 package sttp.tapir.examples.openapi import cats.effect.* import cats.syntax.all.* import org.http4s.HttpRoutes +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router -import org.http4s.blaze.server.BlazeServerBuilder import sttp.tapir.* import sttp.tapir.redoc.RedocUIOptions import sttp.tapir.redoc.bundle.RedocInterpreter @@ -37,10 +37,9 @@ object RedocContextPathHttp4sServer extends IOApp: override def run(args: List[String]): IO[ExitCode] = // starting the server - BlazeServerBuilder[IO] - .withExecutionContext(ec) - .bindHttp(8080, "localhost") + EmberServerBuilder + .default[IO] .withHttpApp(Router(s"/${contextPath.mkString("/")}" -> routes).orNotFound) - .resource + .build .use { _ => IO.println(s"go to: http://127.0.0.1:8080/${(contextPath ++ docPathPrefix).mkString("/")}") *> IO.never } .as(ExitCode.Success) diff --git a/examples/src/main/scala/sttp/tapir/examples/security/OAuth2GithubHttp4sServer.scala b/examples/src/main/scala/sttp/tapir/examples/security/OAuth2GithubHttp4sServer.scala index efdae4774a..089c0af252 100644 --- a/examples/src/main/scala/sttp/tapir/examples/security/OAuth2GithubHttp4sServer.scala +++ b/examples/src/main/scala/sttp/tapir/examples/security/OAuth2GithubHttp4sServer.scala @@ -4,7 +4,7 @@ //> using dep com.softwaremill.sttp.tapir::tapir-http4s-server:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-json-circe:1.11.9 //> using dep com.softwaremill.sttp.client3::async-http-client-backend-cats:3.10.1 -//> using dep org.http4s::http4s-blaze-server:0.23.16 +//> using dep org.http4s::http4s-ember-server:0.23.29 //> using dep com.github.jwt-scala::jwt-circe:10.0.1 package sttp.tapir.examples.security @@ -13,8 +13,8 @@ import cats.effect.* import cats.syntax.all.* import io.circe.generic.auto.* import org.http4s.HttpRoutes +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router -import org.http4s.blaze.server.BlazeServerBuilder import pdi.jwt.{JwtAlgorithm, JwtCirce, JwtClaim} import sttp.client3.* import sttp.client3.asynchttpclient.cats.AsyncHttpClientCatsBackend @@ -118,11 +118,10 @@ object OAuth2GithubHttp4sServer extends IOApp: // starting the server httpClient .use(backend => - BlazeServerBuilder[IO] - .withExecutionContext(ec) - .bindHttp(8080, "localhost") + EmberServerBuilder + .default[IO] .withHttpApp(Router("/" -> (secretPlaceRoute <+> loginRoute <+> loginGithubRoute(backend))).orNotFound) - .resource + .build .use { _ => IO { println("Go to: http://localhost:8080") diff --git a/examples/src/main/scala/sttp/tapir/examples/streaming/ProxyHttp4sFs2Server.scala b/examples/src/main/scala/sttp/tapir/examples/streaming/ProxyHttp4sFs2Server.scala index 34cd49b4ec..4df871c7cd 100644 --- a/examples/src/main/scala/sttp/tapir/examples/streaming/ProxyHttp4sFs2Server.scala +++ b/examples/src/main/scala/sttp/tapir/examples/streaming/ProxyHttp4sFs2Server.scala @@ -3,14 +3,14 @@ //> using dep com.softwaremill.sttp.tapir::tapir-core:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-http4s-server:1.11.9 //> using dep com.softwaremill.sttp.client3::fs2:3.9.8 -//> using dep org.http4s::http4s-blaze-server:0.23.16 +//> using dep org.http4s::http4s-ember-server:0.23.29 package sttp.tapir.examples.streaming import cats.effect.{ExitCode, IO, IOApp} import fs2.Stream import org.http4s.HttpRoutes -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import sttp.capabilities.fs2.Fs2Streams import sttp.client3.* @@ -58,10 +58,8 @@ object ProxyHttp4sFs2Server extends IOApp: (for { backend <- HttpClientFs2Backend.resource[IO]() routes = proxyRoutes(backend) - _ <- BlazeServerBuilder[IO] - .bindHttp(8080, "localhost") + _ <- EmberServerBuilder + .default[IO] .withHttpApp(Router("/" -> routes).orNotFound) - .resource - } yield ()) - .use { _ => IO.never } - .as(ExitCode.Success) + .build + } yield ()).useForever diff --git a/examples/src/main/scala/sttp/tapir/examples/streaming/StreamingHttp4sFs2Server.scala b/examples/src/main/scala/sttp/tapir/examples/streaming/StreamingHttp4sFs2Server.scala index 66daa59f75..ce1a74829f 100644 --- a/examples/src/main/scala/sttp/tapir/examples/streaming/StreamingHttp4sFs2Server.scala +++ b/examples/src/main/scala/sttp/tapir/examples/streaming/StreamingHttp4sFs2Server.scala @@ -3,7 +3,7 @@ //> using dep com.softwaremill.sttp.tapir::tapir-core:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-http4s-server:1.11.9 //> using dep com.softwaremill.sttp.client3::core:3.9.8 -//> using dep org.http4s::http4s-blaze-server:0.23.16 +//> using dep org.http4s::http4s-ember-server:0.23.29 package sttp.tapir.examples.streaming @@ -11,7 +11,7 @@ import cats.effect.{ExitCode, IO, IOApp} import cats.implicits.* import fs2.{Chunk, Stream} import org.http4s.HttpRoutes -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import sttp.capabilities.fs2.Fs2Streams import sttp.client3.* @@ -52,10 +52,10 @@ object StreamingHttp4sFs2Server extends IOApp: override def run(args: List[String]): IO[ExitCode] = // starting the server - BlazeServerBuilder[IO] - .bindHttp(8080, "localhost") + EmberServerBuilder + .default[IO] .withHttpApp(Router("/" -> streamingRoutes).orNotFound) - .resource + .build .use { _ => IO { val backend: SttpBackend[Identity, Any] = HttpClientSyncBackend() diff --git a/examples/src/main/scala/sttp/tapir/examples/streaming/StreamingHttp4sFs2ServerOrError.scala b/examples/src/main/scala/sttp/tapir/examples/streaming/StreamingHttp4sFs2ServerOrError.scala index 3356488f78..4a80c0d356 100644 --- a/examples/src/main/scala/sttp/tapir/examples/streaming/StreamingHttp4sFs2ServerOrError.scala +++ b/examples/src/main/scala/sttp/tapir/examples/streaming/StreamingHttp4sFs2ServerOrError.scala @@ -2,13 +2,13 @@ //> using dep com.softwaremill.sttp.tapir::tapir-core:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-http4s-server:1.11.9 -//> using dep org.http4s::http4s-blaze-server:0.23.16 +//> using dep org.http4s::http4s-ember-server:0.23.16 package sttp.tapir.examples.streaming import cats.effect.* import org.http4s.HttpRoutes -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import sttp.capabilities.fs2.Fs2Streams import sttp.model.StatusCode @@ -51,10 +51,8 @@ object StreamingHttp4sFs2ServerOrError extends IOApp: // curl -v http://localhost:8080/user/another_user (responds with 404) override def run(args: List[String]): IO[ExitCode] = // starting the server - BlazeServerBuilder[IO] - .withExecutionContext(scala.concurrent.ExecutionContext.global) - .bindHttp(8080, "localhost") + EmberServerBuilder + .default[IO] .withHttpApp(Router("/" -> userDataRoutes).orNotFound) - .resource - .use { _ => IO.never } - .as(ExitCode.Success) + .build + .useForever diff --git a/examples/src/main/scala/sttp/tapir/examples/websocket/WebSocketHttp4sServer.scala b/examples/src/main/scala/sttp/tapir/examples/websocket/WebSocketHttp4sServer.scala index 5e931c52ff..e9fdcb44f3 100644 --- a/examples/src/main/scala/sttp/tapir/examples/websocket/WebSocketHttp4sServer.scala +++ b/examples/src/main/scala/sttp/tapir/examples/websocket/WebSocketHttp4sServer.scala @@ -6,7 +6,7 @@ //> using dep com.softwaremill.sttp.tapir::tapir-json-circe:1.11.9 //> using dep com.softwaremill.sttp.apispec::asyncapi-circe-yaml:0.10.0 //> using dep com.softwaremill.sttp.client3::async-http-client-backend-fs2:3.10.1 -//> using dep org.http4s::http4s-blaze-server:0.23.16 +//> using dep org.http4s::http4s-ember-server:0.23.16 package sttp.tapir.examples.websocket @@ -14,7 +14,7 @@ import cats.effect.{ExitCode, IO, IOApp} import io.circe.generic.auto.* import fs2.* import org.http4s.HttpRoutes -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import org.http4s.server.websocket.WebSocketBuilder2 import sttp.apispec.asyncapi.Server @@ -81,11 +81,10 @@ object WebSocketHttp4sServer extends IOApp: override def run(args: List[String]): IO[ExitCode] = // Starting the server - BlazeServerBuilder[IO] - .withExecutionContext(ec) - .bindHttp(8080, "localhost") + EmberServerBuilder + .default[IO] .withHttpWebSocketApp(wsb => Router("/" -> wsRoutes(wsb)).orNotFound) - .resource + .build .flatMap(_ => AsyncHttpClientFs2Backend.resource[IO]()) .use { backend => // Client which interacts with the web socket diff --git a/generated-doc/out/server/http4s.md b/generated-doc/out/server/http4s.md index b6693d1f4c..e85be6f387 100644 --- a/generated-doc/out/server/http4s.md +++ b/generated-doc/out/server/http4s.md @@ -52,11 +52,11 @@ The capability can be added to the classpath independently of the interpreter th ## Http4s backends Http4s integrates with a couple of [server backends](https://http4s.org/v1.0/integrations/), the most popular being -Blaze and Ember. In the [examples](../examples.md) and throughout the docs we use Blaze, but other backends can be used +Blaze and Ember. In the [examples](../examples.md) and throughout the docs we use Ember, but other backends can be used as well. This means adding another dependency, such as: ```scala -"org.http4s" %% "http4s-blaze-server" % Http4sVersion +"org.http4s" %% "http4s-ember-server" % Http4sVersion ``` ## Web sockets @@ -75,7 +75,7 @@ import sttp.tapir.* import sttp.tapir.server.http4s.Http4sServerInterpreter import cats.effect.IO import org.http4s.HttpRoutes -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import org.http4s.server.websocket.WebSocketBuilder2 import fs2.* @@ -89,9 +89,8 @@ val wsEndpoint: PublicEndpoint[Unit, Unit, Pipe[IO, String, String], Fs2Streams[ val wsRoutes: WebSocketBuilder2[IO] => HttpRoutes[IO] = Http4sServerInterpreter[IO]().toWebSocketRoutes(wsEndpoint.serverLogicSuccess[IO](_ => ???)) -BlazeServerBuilder[IO] - .withExecutionContext(summon[ExecutionContext]) - .bindHttp(8080, "localhost") +EmberServerBuilder + .default[IO] .withHttpWebSocketApp(wsb => Router("/" -> wsRoutes(wsb)).orNotFound) ``` diff --git a/generated-doc/out/server/zio-http4s.md b/generated-doc/out/server/zio-http4s.md index e7539cbfba..a238461f35 100644 --- a/generated-doc/out/server/zio-http4s.md +++ b/generated-doc/out/server/zio-http4s.md @@ -99,11 +99,11 @@ The capability can be added to the classpath independently of the interpreter th ## Http4s backends Http4s integrates with a couple of [server backends](https://http4s.org/v1.0/integrations/), the most popular being -Blaze and Ember. In the [examples](../examples.md) and throughout the docs we use Blaze, but other backends can be used +Blaze and Ember. In the [examples](../examples.md) and throughout the docs we use Ember, but other backends can be used as well. This means adding another dependency, such as: ```scala -"org.http4s" %% "http4s-blaze-server" % Http4sVersion +"org.http4s" %% "http4s-ember-server" % Http4sVersion ``` ## Web sockets @@ -121,7 +121,7 @@ import sttp.tapir.{CodecFormat, PublicEndpoint} import sttp.tapir.ztapir.* import sttp.tapir.server.http4s.ztapir.ZHttp4sServerInterpreter import org.http4s.HttpRoutes -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import org.http4s.server.websocket.WebSocketBuilder2 import scala.concurrent.ExecutionContext @@ -141,13 +141,11 @@ val wsRoutes: WebSocketBuilder2[Task] => HttpRoutes[Task] = val serve: Task[Unit] = ZIO.executor.flatMap(executor => - BlazeServerBuilder[Task] - .withExecutionContext(executor.asExecutionContext) - .bindHttp(8080, "localhost") + EmberServerBuilder + .default[Task] .withHttpWebSocketApp(wsb => Router("/" -> wsRoutes(wsb)).orNotFound) - .serve - .compile - .drain + .build + .useForever ) ``` diff --git a/generated-doc/out/tutorials/07_cats_effect.md b/generated-doc/out/tutorials/07_cats_effect.md index b6e8ea4b70..02609255ed 100644 --- a/generated-doc/out/tutorials/07_cats_effect.md +++ b/generated-doc/out/tutorials/07_cats_effect.md @@ -132,11 +132,11 @@ standard code to start a server and handle requests until the application is int ```scala //> using dep com.softwaremill.sttp.tapir::tapir-core:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-http4s-server:1.11.9 -//> using dep org.http4s::http4s-blaze-server:0.23.16 +//> using dep org.http4s::http4s-ember-server:0.23.29 import cats.effect.{ExitCode, IO, IOApp} import org.http4s.HttpRoutes -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import sttp.tapir.* import sttp.tapir.server.http4s.Http4sServerInterpreter @@ -154,12 +154,11 @@ object HelloWorldTapir extends IOApp: .toRoutes(helloWorldEndpoint) override def run(args: List[String]): IO[ExitCode] = - BlazeServerBuilder[IO] - .bindHttp(8080, "localhost") + EmberServerBuilder + .default[IO] .withHttpApp(Router("/" -> helloWorldRoutes).orNotFound) - .resource - .use(_ => IO.never) - .as(ExitCode.Success) + .build + .useForever ``` First of all, you might notice that instead of the `@main` method, we are extending the `IOApp` trait. This is needed, @@ -169,8 +168,8 @@ the `IOApp` will handle evaluating the `IO` description and actually running the Secondly, with http4s we need to use a specific server implementation (http4s itself is only an API to define endpoints - kind of a middle-man between Tapir and low-level networking code). We can choose from `blaze` and `ember` servers, here -we're using the `blaze` one, which is reflected in the additional dependency and the server configuration constructor: -`BlazeServerBuilder`. +we're using the `ember` one, which is reflected in the additional dependency and the server configuration constructor: +`EmberServerBuilder`. Finally, we've got the `run` method implementation, which attaches our interpreted route to the root context `/` and exposes the server on `localhost:8080`. @@ -195,12 +194,12 @@ the second step that we need to perform: //> using dep com.softwaremill.sttp.tapir::tapir-core:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-http4s-server:1.11.9 //> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.11.9 -//> using dep org.http4s::http4s-blaze-server:0.23.16 +//> using dep org.http4s::http4s-ember-server:0.23.29 import cats.effect.{ExitCode, IO, IOApp} import cats.syntax.all.* import org.http4s.HttpRoutes -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import sttp.tapir.* import sttp.tapir.server.http4s.Http4sServerInterpreter @@ -226,17 +225,16 @@ object HelloWorldTapir extends IOApp: val allRoutes: HttpRoutes[IO] = helloWorldRoutes <+> swaggerRoutes override def run(args: List[String]): IO[ExitCode] = - BlazeServerBuilder[IO] - .bindHttp(8080, "localhost") + EmberServerBuilder + .default[IO] .withHttpApp(Router("/" -> allRoutes).orNotFound) - .resource - .use(_ => IO.never) - .as(ExitCode.Success) + .build + .useForever ``` Hence, we first generate endpoint descriptions, which correspond to exposing the Swagger UI (containing the generated OpenAPI yaml for our `/hello/world` endpoint), which use `IO` to express their server logic. Then, we interpret those -endpoints as `HttpRoutes[IO]`, which we can expose using http4's blaze server. +endpoints as `HttpRoutes[IO]`, which we can expose using http4's ember server. ## Other concepts covered so far diff --git a/perf-tests/src/main/scala/sttp/tapir/perf/http4s/Http4s.scala b/perf-tests/src/main/scala/sttp/tapir/perf/http4s/Http4s.scala index 68eab44f21..733b7158e9 100644 --- a/perf-tests/src/main/scala/sttp/tapir/perf/http4s/Http4s.scala +++ b/perf-tests/src/main/scala/sttp/tapir/perf/http4s/Http4s.scala @@ -2,10 +2,11 @@ package sttp.tapir.perf.http4s import cats.effect._ import cats.syntax.all._ +import com.comcast.ip4s import fs2._ import fs2.io.file.{Files, Path => Fs2Path} import org.http4s._ -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.dsl._ import org.http4s.implicits._ import org.http4s.server.Router @@ -105,21 +106,18 @@ object Tapir extends Endpoints { object server { val maxConnections = 65536 - val connectorPoolSize: Int = Math.max(2, Runtime.getRuntime.availableProcessors() / 4) def runServer( router: WebSocketBuilder2[IO] => HttpRoutes[IO] - ): IO[ServerRunner.KillSwitch] = - BlazeServerBuilder[IO] - .bindHttp(Port, "localhost") - .withHttpWebSocketApp(wsb => router(wsb).orNotFound) - .withMaxConnections(maxConnections) - .withConnectorPoolSize(connectorPoolSize) - .resource - .allocated - .map(_._2) - .map(_.flatTap { _ => - IO.println("Http4s server closed.") - }) + ): IO[ServerRunner.KillSwitch] = EmberServerBuilder + .default[IO] + .withPort(ip4s.Port.fromInt(Port).get) + .withHttpWebSocketApp(wsb => router(wsb).orNotFound) + .withMaxConnections(maxConnections) + .build + .useForever + .start + .map(_.cancel *> IO.println("Http4s server closed.")) + } } object TapirServer extends ServerRunner { override def start = server.runServer(Tapir.router(1)) } diff --git a/project/Versions.scala b/project/Versions.scala index 3d12170341..3549b065c8 100644 --- a/project/Versions.scala +++ b/project/Versions.scala @@ -1,7 +1,5 @@ object Versions { val http4s = "0.23.29" - val http4sBlazeServer = "0.23.17" - val http4sBlazeClient = "0.23.17" val catsCore = "2.12.0" val catsEffect = "3.5.7" val circe = "0.14.9" diff --git a/server/http4s-server/src/main/scala/sttp/tapir/server/http4s/Http4sServerInterpreter.scala b/server/http4s-server/src/main/scala/sttp/tapir/server/http4s/Http4sServerInterpreter.scala index 64df3e9f66..902c0788e8 100644 --- a/server/http4s-server/src/main/scala/sttp/tapir/server/http4s/Http4sServerInterpreter.scala +++ b/server/http4s-server/src/main/scala/sttp/tapir/server/http4s/Http4sServerInterpreter.scala @@ -124,7 +124,7 @@ trait Http4sServerInterpreter[F[_]] { new Http4sInvalidWebSocketUse( "Invalid usage of web socket endpoint without WebSocketBuilder2. " + "Use the toWebSocketRoutes/toWebSocketHttp interpreter methods, " + - "and add the result using BlazeServerBuilder.withHttpWebSocketApp(..)." + "and add the result using (Blaze/Ember)ServerBuilder.withHttpWebSocketApp(..)." ) ) } diff --git a/server/http4s-server/src/test/scala/sttp/tapir/server/http4s/Http4sServerTest.scala b/server/http4s-server/src/test/scala/sttp/tapir/server/http4s/Http4sServerTest.scala index c89a8fe03b..dce747b163 100644 --- a/server/http4s-server/src/test/scala/sttp/tapir/server/http4s/Http4sServerTest.scala +++ b/server/http4s-server/src/test/scala/sttp/tapir/server/http4s/Http4sServerTest.scala @@ -4,9 +4,10 @@ import cats.data._ import cats.effect._ import cats.effect.unsafe.implicits.global import cats.syntax.all._ +import com.comcast.ip4s.Port import fs2.Pipe import fs2.Stream -import org.http4s.blaze.server.BlazeServerBuilder +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.Router import org.http4s.server.ContextMiddleware import org.http4s.ContextRoutes @@ -40,11 +41,11 @@ class Http4sServerTest[R >: Fs2Streams[IO] with WebSockets] extends TestSuite wi val sse2 = ServerSentEvent(randomUUID, randomUUID, randomUUID, Some(Random.nextInt(200))) def assert_get_apiTestRouter_respondsWithExpectedContent[T](routes: HttpRoutes[IO], expectedContext: T): IO[Assertion] = - BlazeServerBuilder[IO] - .withExecutionContext(ExecutionContext.global) - .bindHttp(0, "localhost") + EmberServerBuilder + .default[IO] + .withPort(Port.fromInt(0).get) .withHttpApp(Router("/api" -> routes).orNotFound) - .resource + .build .use { server => val port = server.address.getPort basicRequest.get(uri"http://localhost:$port/api/test/router").send(backend).map(_.body shouldBe Right(expectedContext)) diff --git a/server/http4s-server/src/test/scala/sttp/tapir/server/http4s/Http4sTestServerInterpreter.scala b/server/http4s-server/src/test/scala/sttp/tapir/server/http4s/Http4sTestServerInterpreter.scala index a5852ce240..c8021b20b2 100644 --- a/server/http4s-server/src/test/scala/sttp/tapir/server/http4s/Http4sTestServerInterpreter.scala +++ b/server/http4s-server/src/test/scala/sttp/tapir/server/http4s/Http4sTestServerInterpreter.scala @@ -3,7 +3,8 @@ package sttp.tapir.server.http4s import cats.data.NonEmptyList import cats.effect.{IO, Resource} import cats.syntax.all._ -import org.http4s.blaze.server.BlazeServerBuilder +import com.comcast.ip4s +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.websocket.WebSocketBuilder2 import org.http4s.{HttpApp, HttpRoutes} import sttp.capabilities.WebSockets @@ -35,11 +36,11 @@ class Http4sTestServerInterpreter extends TestServerInterpreter[IO, Fs2Streams[I val service: WebSocketBuilder2[IO] => HttpApp[IO] = wsb => routes.map(_.apply(wsb)).reduceK.orNotFound - BlazeServerBuilder[IO] - .withExecutionContext(ExecutionContext.global) - .bindHttp(0, "localhost") + EmberServerBuilder + .default[IO] + .withPort(ip4s.Port.fromInt(0).get) .withHttpWebSocketApp(service) - .resource - .map(_.address.getPort()) + .build + .map(_.address.getPort) } } diff --git a/server/http4s-server/zio/src/test/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sTestServerInterpreter.scala b/server/http4s-server/zio/src/test/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sTestServerInterpreter.scala index 9b46b6a198..1246c4310d 100644 --- a/server/http4s-server/zio/src/test/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sTestServerInterpreter.scala +++ b/server/http4s-server/zio/src/test/scala/sttp/tapir/server/http4s/ztapir/ZHttp4sTestServerInterpreter.scala @@ -4,7 +4,8 @@ import cats.data.NonEmptyList import cats.effect.{IO, Resource} import cats._ import cats.syntax.all._ -import org.http4s.blaze.server.BlazeServerBuilder +import com.comcast.ip4s +import org.http4s.ember.server.EmberServerBuilder import org.http4s.server.websocket.WebSocketBuilder2 import org.http4s.{HttpApp, HttpRoutes} import sttp.capabilities.WebSockets @@ -41,11 +42,11 @@ class ZHttp4sTestServerInterpreter extends TestServerInterpreter[Task, ZioStream val service: WebSocketBuilder2[Task] => HttpApp[Task] = wsb => routes.map(_.apply(wsb)).reduceK.orNotFound - BlazeServerBuilder[Task] - .withExecutionContext(ExecutionContext.global) - .bindHttp(0, "localhost") + EmberServerBuilder + .default[Task] + .withPort(ip4s.Port.fromInt(0).get) .withHttpWebSocketApp(service) - .resource + .build .map(_.address.getPort) .mapK(new ~>[Task, IO] { // Converting a ZIO effect to an Cats Effect IO effect diff --git a/server/tests/src/main/scala/sttp/tapir/server/tests/TestServerInterpreter.scala b/server/tests/src/main/scala/sttp/tapir/server/tests/TestServerInterpreter.scala index 8bb4548295..8a87c8c877 100644 --- a/server/tests/src/main/scala/sttp/tapir/server/tests/TestServerInterpreter.scala +++ b/server/tests/src/main/scala/sttp/tapir/server/tests/TestServerInterpreter.scala @@ -1,7 +1,7 @@ package sttp.tapir.server.tests import cats.data.NonEmptyList -import cats.effect.{IO, Resource} +import cats.effect.{Deferred, IO, Resource} import sttp.tapir.server.ServerEndpoint import sttp.tapir.server.interceptor.CustomiseInterceptors import sttp.tapir.tests._ @@ -21,8 +21,12 @@ trait TestServerInterpreter[F[_], +R, OPTIONS, ROUTE] { def serverWithStop( routes: NonEmptyList[ROUTE], gracefulShutdownTimeout: Option[FiniteDuration] = None - ): Resource[IO, (Port, KillSwitch)] = - Resource.eval(server(routes, gracefulShutdownTimeout).allocated) + ): Resource[IO, (Port, KillSwitch)] = for { + stopSignal <- Resource.eval(Deferred[IO, Unit]) + portValue <- Resource.eval(Deferred[IO, Port]) + _ <- server(routes, gracefulShutdownTimeout).use(port => portValue.complete(port) *> stopSignal.get).background + port <- Resource.eval(portValue.get) + } yield (port, stopSignal.complete(()).void) def server(routes: NonEmptyList[ROUTE], gracefulShutdownTimeout: Option[FiniteDuration] = None): Resource[IO, Port] }