Skip to content

Commit 8b5ea8a

Browse files
committed
http4s: Migrate from blaze to ember
1 parent 69bfafb commit 8b5ea8a

File tree

30 files changed

+196
-230
lines changed

30 files changed

+196
-230
lines changed

build.sbt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ lazy val clientTestServer = (projectMatrix in file("client/testserver"))
391391
publish / skip := true,
392392
libraryDependencies ++= Seq(
393393
"org.http4s" %% "http4s-dsl" % Versions.http4s,
394-
"org.http4s" %% "http4s-blaze-server" % Versions.http4sBlazeServer,
394+
"org.http4s" %% "http4s-ember-server" % Versions.http4s,
395395
"org.http4s" %% "http4s-circe" % Versions.http4s,
396396
logback
397397
),
@@ -534,7 +534,7 @@ lazy val perfTests: ProjectMatrix = (projectMatrix in file("perf-tests"))
534534
"io.github.classgraph" % "classgraph" % "4.8.179",
535535
"org.http4s" %% "http4s-core" % Versions.http4s,
536536
"org.http4s" %% "http4s-dsl" % Versions.http4s,
537-
"org.http4s" %% "http4s-blaze-server" % Versions.http4sBlazeServer,
537+
"org.http4s" %% "http4s-ember-server" % Versions.http4s,
538538
"org.typelevel" %%% "cats-effect" % Versions.catsEffect,
539539
logback
540540
),
@@ -1165,7 +1165,7 @@ lazy val swaggerUiBundle: ProjectMatrix = (projectMatrix in file("docs/swagger-u
11651165
name := "tapir-swagger-ui-bundle",
11661166
libraryDependencies ++= Seq(
11671167
"com.softwaremill.sttp.apispec" %% "openapi-circe-yaml" % Versions.sttpApispec,
1168-
"org.http4s" %% "http4s-blaze-server" % Versions.http4sBlazeServer % Test,
1168+
"org.http4s" %% "http4s-ember-server" % Versions.http4s % Test,
11691169
scalaTest.value % Test
11701170
)
11711171
)
@@ -1191,7 +1191,7 @@ lazy val redocBundle: ProjectMatrix = (projectMatrix in file("docs/redoc-bundle"
11911191
name := "tapir-redoc-bundle",
11921192
libraryDependencies ++= Seq(
11931193
"com.softwaremill.sttp.apispec" %% "openapi-circe-yaml" % Versions.sttpApispec,
1194-
"org.http4s" %% "http4s-blaze-server" % Versions.http4sBlazeServer % Test,
1194+
"org.http4s" %% "http4s-ember-server" % Versions.http4s % Test,
11951195
scalaTest.value % Test
11961196
)
11971197
)
@@ -1326,7 +1326,7 @@ lazy val http4sServer: ProjectMatrix = (projectMatrix in file("server/http4s-ser
13261326
scalaVersions = scala2And3Versions,
13271327
settings = commonJvmSettings ++ Seq {
13281328
libraryDependencies ++= Seq(
1329-
"org.http4s" %%% "http4s-blaze-server" % Versions.http4sBlazeServer % Test
1329+
"org.http4s" %%% "http4s-ember-server" % Versions.http4s % Test
13301330
)
13311331
}
13321332
)
@@ -1342,7 +1342,7 @@ lazy val http4sServerZio: ProjectMatrix = (projectMatrix in file("server/http4s-
13421342
name := "tapir-http4s-server-zio",
13431343
libraryDependencies ++= Seq(
13441344
"dev.zio" %% "zio-interop-cats" % Versions.zioInteropCats,
1345-
"org.http4s" %% "http4s-blaze-server" % Versions.http4sBlazeServer % Test
1345+
"org.http4s" %% "http4s-ember-server" % Versions.http4s % Test
13461346
)
13471347
)
13481348
.jvmPlatform(scalaVersions = scala2And3Versions, settings = commonJvmSettings)
@@ -1910,7 +1910,7 @@ lazy val http4sClient: ProjectMatrix = (projectMatrix in file("client/http4s-cli
19101910
name := "tapir-http4s-client",
19111911
libraryDependencies ++= Seq(
19121912
"org.http4s" %% "http4s-core" % Versions.http4s,
1913-
"org.http4s" %% "http4s-blaze-client" % Versions.http4sBlazeClient % Test,
1913+
"org.http4s" %% "http4s-ember-client" % Versions.http4s % Test,
19141914
"com.softwaremill.sttp.shared" %% "fs2" % Versions.sttpShared % Optional
19151915
)
19161916
)
@@ -2073,7 +2073,7 @@ lazy val examples: ProjectMatrix = (projectMatrix in file("examples"))
20732073
"com.github.jwt-scala" %% "jwt-circe" % Versions.jwtScala,
20742074
"org.http4s" %% "http4s-dsl" % Versions.http4s,
20752075
"org.http4s" %% "http4s-circe" % Versions.http4s,
2076-
"org.http4s" %% "http4s-blaze-server" % Versions.http4sBlazeServer,
2076+
"org.http4s" %% "http4s-ember-server" % Versions.http4s,
20772077
"org.mock-server" % "mockserver-netty" % Versions.mockServer,
20782078
"io.opentelemetry" % "opentelemetry-sdk" % Versions.openTelemetry,
20792079
"io.opentelemetry" % "opentelemetry-sdk-metrics" % Versions.openTelemetry,
@@ -2144,7 +2144,7 @@ lazy val documentation: ProjectMatrix = (projectMatrix in file("generated-doc"))
21442144
name := "doc",
21452145
libraryDependencies ++= Seq(
21462146
"org.playframework" %% "play-netty-server" % Versions.playServer,
2147-
"org.http4s" %% "http4s-blaze-server" % Versions.http4sBlazeServer,
2147+
"org.http4s" %% "http4s-ember-server" % Versions.http4s,
21482148
"com.softwaremill.sttp.apispec" %% "openapi-circe-yaml" % Versions.sttpApispec,
21492149
"com.softwaremill.sttp.apispec" %% "asyncapi-circe-yaml" % Versions.sttpApispec
21502150
),

client/http4s-client/src/test/scala/sttp/tapir/client/http4s/Http4sClientTests.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package sttp.tapir.client.http4s
22

33
import cats.effect.IO
4-
import org.http4s.blaze.client.BlazeClientBuilder
4+
import org.http4s.ember.client.EmberClientBuilder
55
import org.http4s.{Request, Response, Uri}
66
import sttp.tapir.client.tests.ClientTests
77
import sttp.tapir.{DecodeResult, Endpoint}
8-
import scala.concurrent.ExecutionContext.global
98

109
abstract class Http4sClientTests[R] extends ClientTests[R] {
1110
override def send[A, I, E, O](
@@ -35,7 +34,7 @@ abstract class Http4sClientTests[R] extends ClientTests[R] {
3534
}
3635

3736
private def sendAndParseResponse[Result](request: Request[IO], parseResponse: Response[IO] => IO[Result]) =
38-
BlazeClientBuilder[IO](global).resource.use { client =>
37+
EmberClientBuilder.default[IO].build.use { client =>
3938
client.run(request).use(parseResponse)
4039
}
4140
}

client/testserver/src/main/scala/sttp/tapir/client/tests/HttpServer.scala

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ package sttp.tapir.client.tests
22

33
import cats.effect._
44
import cats.effect.std.Queue
5-
import cats.effect.unsafe.implicits.global
65
import cats.implicits._
6+
import com.comcast.ip4s.Port
77
import fs2.{Pipe, Stream}
88
import org.http4s.dsl.io._
99
import org.http4s.headers.{Accept, `Content-Type`}
1010
import org.http4s.server.Router
11-
import org.http4s.blaze.server.BlazeServerBuilder
11+
import org.http4s.ember.server.EmberServerBuilder
1212
import org.http4s.server.middleware._
1313
import org.http4s.server.websocket.WebSocketBuilder2
1414
import org.http4s.websocket.WebSocketFrame
@@ -17,18 +17,17 @@ import org.slf4j.LoggerFactory
1717
import org.typelevel.ci.CIString
1818
import scodec.bits.ByteVector
1919

20-
import scala.concurrent.ExecutionContext
21-
2220
object HttpServer extends ResourceApp.Forever {
23-
type Port = Int
21+
22+
private val defaultPort = Port.fromInt(51823).get
2423

2524
def run(args: List[String]): Resource[IO, Unit] = {
26-
val port = args.headOption.map(_.toInt).getOrElse(51823)
25+
val port = args.headOption.flatMap(Port.fromString).getOrElse(defaultPort)
2726
new HttpServer(port).build.void
2827
}
2928
}
3029

31-
class HttpServer(port: HttpServer.Port) {
30+
class HttpServer(port: Port) {
3231

3332
private val logger = LoggerFactory.getLogger(getClass)
3433

@@ -207,13 +206,12 @@ class HttpServer(port: HttpServer.Port) {
207206
Router("/" -> corsService).orNotFound
208207
}
209208

210-
//
209+
def build: Resource[IO, server.Server] = EmberServerBuilder
210+
.default[IO]
211+
.withPort(port)
212+
.withHttpWebSocketApp(app)
213+
.build
214+
.evalTap(_ => IO(logger.info(s"Server on port $port started")))
215+
.onFinalize(IO(logger.info(s"Server on port $port stopped")))
211216

212-
def build: Resource[IO, server.Server] = BlazeServerBuilder[IO]
213-
.withExecutionContext(ExecutionContext.global)
214-
.bindHttp(port)
215-
.withHttpWebSocketApp(app)
216-
.resource
217-
.evalTap(_ => IO(logger.info(s"Server on port $port started")))
218-
.onFinalize(IO(logger.info(s"Server on port $port stopped")))
219217
}

doc/server/http4s.md

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ The capability can be added to the classpath independently of the interpreter th
5252
## Http4s backends
5353

5454
Http4s integrates with a couple of [server backends](https://http4s.org/v1.0/integrations/), the most popular being
55-
Blaze and Ember. In the [examples](../examples.md) and throughout the docs we use Blaze, but other backends can be used
55+
Blaze and Ember. In the [examples](../examples.md) and throughout the docs we use Ember, but other backends can be used
5656
as well. This means adding another dependency, such as:
5757

5858
```scala
59-
"org.http4s" %% "http4s-blaze-server" % Http4sVersion
59+
"org.http4s" %% "http4s-ember-server" % Http4sVersion
6060
```
6161

6262
## Web sockets
@@ -75,24 +75,21 @@ import sttp.tapir.*
7575
import sttp.tapir.server.http4s.Http4sServerInterpreter
7676
import cats.effect.IO
7777
import org.http4s.HttpRoutes
78-
import org.http4s.blaze.server.BlazeServerBuilder
78+
import org.http4s.ember.server.EmberServerBuilder
7979
import org.http4s.server.Router
8080
import org.http4s.server.websocket.WebSocketBuilder2
8181
import fs2.*
8282
import scala.concurrent.ExecutionContext
8383

84-
given ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global
85-
8684
val wsEndpoint: PublicEndpoint[Unit, Unit, Pipe[IO, String, String], Fs2Streams[IO] with WebSockets] =
8785
endpoint.get.in("count").out(webSocketBody[String, CodecFormat.TextPlain, String, CodecFormat.TextPlain](Fs2Streams[IO]))
8886

8987
val wsRoutes: WebSocketBuilder2[IO] => HttpRoutes[IO] =
9088
Http4sServerInterpreter[IO]().toWebSocketRoutes(wsEndpoint.serverLogicSuccess[IO](_ => ???))
91-
92-
BlazeServerBuilder[IO]
93-
.withExecutionContext(summon[ExecutionContext])
94-
.bindHttp(8080, "localhost")
95-
.withHttpWebSocketApp(wsb => Router("/" -> wsRoutes(wsb)).orNotFound)
89+
90+
EmberServerBuilder
91+
.default[IO]
92+
.withHttpWebSocketApp(wsb => Router("/" -> wsRoutes(wsb)).orNotFound)
9693
```
9794

9895
```{note}

doc/server/zio-http4s.md

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,11 @@ The capability can be added to the classpath independently of the interpreter th
9999
## Http4s backends
100100

101101
Http4s integrates with a couple of [server backends](https://http4s.org/v1.0/integrations/), the most popular being
102-
Blaze and Ember. In the [examples](../examples.md) and throughout the docs we use Blaze, but other backends can be used
102+
Blaze and Ember. In the [examples](../examples.md) and throughout the docs we use Ember, but other backends can be used
103103
as well. This means adding another dependency, such as:
104104

105105
```scala
106-
"org.http4s" %% "http4s-blaze-server" % Http4sVersion
106+
"org.http4s" %% "http4s-ember-server" % Http4sVersion
107107
```
108108

109109
## Web sockets
@@ -121,7 +121,7 @@ import sttp.tapir.{CodecFormat, PublicEndpoint}
121121
import sttp.tapir.ztapir.*
122122
import sttp.tapir.server.http4s.ztapir.ZHttp4sServerInterpreter
123123
import org.http4s.HttpRoutes
124-
import org.http4s.blaze.server.BlazeServerBuilder
124+
import org.http4s.ember.server.EmberServerBuilder
125125
import org.http4s.server.Router
126126
import org.http4s.server.websocket.WebSocketBuilder2
127127
import scala.concurrent.ExecutionContext
@@ -131,8 +131,6 @@ import zio.stream.Stream
131131

132132
def runtime: Runtime[Any] = ??? // provided by ZIOAppDefault
133133

134-
given ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global
135-
136134
val wsEndpoint: PublicEndpoint[Unit, Unit, Stream[Throwable, String] => Stream[Throwable, String], ZioStreams with WebSockets] =
137135
endpoint.get.in("count").out(webSocketBody[String, CodecFormat.TextPlain, String, CodecFormat.TextPlain](ZioStreams))
138136

@@ -141,14 +139,12 @@ val wsRoutes: WebSocketBuilder2[Task] => HttpRoutes[Task] =
141139

142140
val serve: Task[Unit] =
143141
ZIO.executor.flatMap(executor =>
144-
BlazeServerBuilder[Task]
145-
.withExecutionContext(executor.asExecutionContext)
146-
.bindHttp(8080, "localhost")
142+
EmberServerBuilder
143+
.default[Task]
147144
.withHttpWebSocketApp(wsb => Router("/" -> wsRoutes(wsb)).orNotFound)
148-
.serve
149-
.compile
150-
.drain
151-
)
145+
.build
146+
.useForever
147+
)
152148
```
153149

154150
## Server Sent Events

doc/tutorials/07_cats_effect.md

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,11 @@ standard code to start a server and handle requests until the application is int
132132
```scala
133133
//> using dep com.softwaremill.sttp.tapir::tapir-core:@VERSION@
134134
//> using dep com.softwaremill.sttp.tapir::tapir-http4s-server:@VERSION@
135-
//> using dep org.http4s::http4s-blaze-server:0.23.16
135+
//> using dep org.http4s::http4s-ember-server:0.23.30
136136

137137
import cats.effect.{ExitCode, IO, IOApp}
138138
import org.http4s.HttpRoutes
139-
import org.http4s.blaze.server.BlazeServerBuilder
139+
import org.http4s.ember.server.EmberServerBuilder
140140
import org.http4s.server.Router
141141
import sttp.tapir.*
142142
import sttp.tapir.server.http4s.Http4sServerInterpreter
@@ -154,12 +154,11 @@ object HelloWorldTapir extends IOApp:
154154
.toRoutes(helloWorldEndpoint)
155155

156156
override def run(args: List[String]): IO[ExitCode] =
157-
BlazeServerBuilder[IO]
158-
.bindHttp(8080, "localhost")
157+
EmberServerBuilder
158+
.default[IO]
159159
.withHttpApp(Router("/" -> helloWorldRoutes).orNotFound)
160-
.resource
161-
.use(_ => IO.never)
162-
.as(ExitCode.Success)
160+
.build
161+
.useForever
163162
```
164163

165164
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
169168

170169
Secondly, with http4s we need to use a specific server implementation (http4s itself is only an API to define endpoints -
171170
kind of a middle-man between Tapir and low-level networking code). We can choose from `blaze` and `ember` servers, here
172-
we're using the `blaze` one, which is reflected in the additional dependency and the server configuration constructor:
173-
`BlazeServerBuilder`.
171+
we're using the `ember` one, which is reflected in the additional dependency and the server configuration constructor:
172+
`EmberServerBuilder`.
174173

175174
Finally, we've got the `run` method implementation, which attaches our interpreted route to the root context `/` and
176175
exposes the server on `localhost:8080`.
@@ -195,12 +194,12 @@ the second step that we need to perform:
195194
//> using dep com.softwaremill.sttp.tapir::tapir-core:@VERSION@
196195
//> using dep com.softwaremill.sttp.tapir::tapir-http4s-server:@VERSION@
197196
//> using dep com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:@VERSION@
198-
//> using dep org.http4s::http4s-blaze-server:0.23.16
197+
//> using dep org.http4s::http4s-ember-server:0.23.30
199198

200199
import cats.effect.{ExitCode, IO, IOApp}
201200
import cats.syntax.all.*
202201
import org.http4s.HttpRoutes
203-
import org.http4s.blaze.server.BlazeServerBuilder
202+
import org.http4s.ember.server.EmberServerBuilder
204203
import org.http4s.server.Router
205204
import sttp.tapir.*
206205
import sttp.tapir.server.http4s.Http4sServerInterpreter
@@ -226,16 +225,16 @@ object HelloWorldTapir extends IOApp:
226225
val allRoutes: HttpRoutes[IO] = helloWorldRoutes <+> swaggerRoutes
227226

228227
override def run(args: List[String]): IO[ExitCode] =
229-
BlazeServerBuilder[IO]
230-
.bindHttp(8080, "localhost")
228+
EmberServerBuilder
229+
.default[IO]
231230
.withHttpApp(Router("/" -> allRoutes).orNotFound)
232-
.resource
231+
.build
233232
.useForever
234233
```
235234

236235
Hence, we first generate endpoint descriptions, which correspond to exposing the Swagger UI (containing the generated
237236
OpenAPI yaml for our `/hello/world` endpoint), which use `IO` to express their server logic. Then, we interpret those
238-
endpoints as `HttpRoutes[IO]`, which we can expose using http4's blaze server.
237+
endpoints as `HttpRoutes[IO]`, which we can expose using http4's ember server.
239238

240239
## Other concepts covered so far
241240

docs/redoc-bundle/src/test/scala/sttp/tapir/redoc/bundle/RedocInterpreterTest.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ package sttp.tapir.redoc.bundle
22

33
import cats.effect.IO
44
import cats.effect.unsafe.implicits.global
5+
import com.comcast.ip4s.Port
56
import org.http4s.HttpRoutes
6-
import org.http4s.blaze.server.BlazeServerBuilder
7+
import org.http4s.ember.server.EmberServerBuilder
78
import org.http4s.server.Router
89
import org.scalatest.Assertion
910
import org.scalatest.funsuite.AsyncFunSuite
@@ -66,10 +67,11 @@ class RedocInterpreterTest extends AsyncFunSuite with Matchers {
6667
.fromEndpoints[IO](List(testEndpoint), "The tapir library", "1.0.0")
6768
)
6869

69-
BlazeServerBuilder[IO]
70-
.bindHttp(0, "localhost")
70+
EmberServerBuilder
71+
.default[IO]
72+
.withPort(Port.fromInt(0).get)
7173
.withHttpApp(Router(s"/${context.mkString("/")}" -> redocUIRoutes).orNotFound)
72-
.resource
74+
.build
7375
.use { server =>
7476
IO {
7577
val port = server.address.getPort

docs/swagger-ui-bundle/src/test/scala/sttp/tapir/swagger/bundle/SwaggerInterpreterTest.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ package sttp.tapir.swagger.bundle
22

33
import cats.effect.IO
44
import cats.effect.unsafe.implicits.global
5+
import com.comcast.ip4s.Port
56
import org.http4s.HttpRoutes
6-
import org.http4s.blaze.server.BlazeServerBuilder
7+
import org.http4s.ember.server.EmberServerBuilder
78
import org.http4s.server.Router
89
import org.scalatest.Assertion
910
import org.scalatest.funsuite.AsyncFunSuite
@@ -33,10 +34,11 @@ class SwaggerInterpreterTest extends AsyncFunSuite with Matchers {
3334
.fromEndpoints[IO](List(testEndpoint), "The tapir library", "1.0.0")
3435
)
3536

36-
BlazeServerBuilder[IO]
37-
.bindHttp(0, "localhost")
37+
EmberServerBuilder
38+
.default[IO]
39+
.withPort(Port.fromInt(0).get)
3840
.withHttpApp(Router(s"/${context.mkString("/")}" -> swaggerUIRoutes).orNotFound)
39-
.resource
41+
.build
4042
.use { server =>
4143
IO {
4244
val port = server.address.getPort

0 commit comments

Comments
 (0)