Skip to content

Commit 3ffa8c4

Browse files
authored
Merge pull request #18 from avast/feat/http4s
feat: Add http4s client and server modules with simple tests
2 parents 5f51d3a + ade1f62 commit 3ffa8c4

File tree

21 files changed

+597
-18
lines changed

21 files changed

+597
-18
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Scala Server Toolkit
22

33
[![Build Status](https://travis-ci.org/avast/scala-server-toolkit.svg?branch=master)](https://travis-ci.org/avast/scala-server-toolkit)
4-
[![Maven Central](https://img.shields.io/maven-central/v/com.avast/scala-server-toolkit-pureconfig_2.12)](https://repo1.maven.org/maven2/com/avast/scala-server-toolkit-pureconfig_2.12/)
4+
[![Maven Central](https://img.shields.io/maven-central/v/com.avast/scala-server-toolkit-http4s-blaze-server_2.12)](https://repo1.maven.org/maven2/com/avast/scala-server-toolkit-http4s-blaze-server_2.12/)
55
[![Scala Steward badge](https://img.shields.io/badge/Scala_Steward-helping-brightgreen.svg?style=flat&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAVFBMVEUAAACHjojlOy5NWlrKzcYRKjGFjIbp293YycuLa3pYY2LSqql4f3pCUFTgSjNodYRmcXUsPD/NTTbjRS+2jomhgnzNc223cGvZS0HaSD0XLjbaSjElhIr+AAAAAXRSTlMAQObYZgAAAHlJREFUCNdNyosOwyAIhWHAQS1Vt7a77/3fcxxdmv0xwmckutAR1nkm4ggbyEcg/wWmlGLDAA3oL50xi6fk5ffZ3E2E3QfZDCcCN2YtbEWZt+Drc6u6rlqv7Uk0LdKqqr5rk2UCRXOk0vmQKGfc94nOJyQjouF9H/wCc9gECEYfONoAAAAASUVORK5CYII=)](https://scala-steward.org)
66

77
This project is a culmination of years of Scala development at Avast and tries to represent the best practices of Scala server development

build.sbt

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,22 @@ lazy val commonSettings = Seq(
1616
libraryDependencies ++= Seq(
1717
compilerPlugin(Dependencies.kindProjector),
1818
Dependencies.catsEffect,
19-
Dependencies.Test.scalaTest
19+
Dependencies.logbackClassic % Test,
20+
Dependencies.scalaTest % Test
2021
),
2122
Test / publishArtifact := false
2223
)
2324

2425
lazy val root = project
2526
.in(file("."))
26-
.aggregate(example, jvmExecution, jvmSsl, jvmSystem, pureconfig)
27+
.aggregate(example, http4sBlazeClient, http4sBlazeServer, jvmExecution, jvmSsl, jvmSystem, pureconfig)
2728
.settings(
2829
name := "scala-server-toolkit",
2930
publish / skip := true
3031
)
3132

3233
lazy val example = project
33-
.dependsOn(jvmExecution, jvmSsl, jvmSystem, pureconfig)
34+
.dependsOn(jvmExecution, http4sBlazeClient, http4sBlazeServer, jvmSsl, jvmSystem, pureconfig)
3435
.enablePlugins(MdocPlugin)
3536
.settings(
3637
commonSettings,
@@ -46,6 +47,28 @@ lazy val example = project
4647
)
4748
)
4849

50+
lazy val http4sBlazeClient = project
51+
.in(file("http4s-blaze-client"))
52+
.dependsOn(jvmSsl)
53+
.settings(commonSettings)
54+
.settings(
55+
name := "scala-server-toolkit-http4s-blaze-client",
56+
libraryDependencies += Dependencies.http4sBlazeClient
57+
)
58+
59+
lazy val http4sBlazeServer = project
60+
.in(file("http4s-blaze-server"))
61+
.dependsOn(http4sBlazeClient % Test)
62+
.settings(commonSettings)
63+
.settings(
64+
name := "scala-server-toolkit-http4s-blaze-server",
65+
libraryDependencies ++= Seq(
66+
Dependencies.http4sBlazeServer,
67+
Dependencies.http4sDsl,
68+
Dependencies.slf4jApi
69+
)
70+
)
71+
4972
lazy val jvmExecution = project
5073
.in(file("jvm-execution"))
5174
.settings(

docs/http4s.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Module http4s
2+
3+
[![Maven Central](https://img.shields.io/maven-central/v/com.avast/scala-server-toolkit-http4s-blaze-server_2.12)](https://repo1.maven.org/maven2/com/avast/scala-server-toolkit-http4s-blaze-server_2.12/)
4+
5+
`libraryDependencies += "com.avast" %% "scala-server-toolkit-http4s-blaze-server" % "<VERSION>"`
6+
7+
There are `http4s-*` modules that provide easy initialization of a server and a client. Http4s is an interface with multiple possible
8+
implementations - for now we provide only implementations based on [Blaze](https://github.com/http4s/blaze).
9+
10+
Both server and client are configured via configuration `case class` which contains default values taken from the underlying implementations.
11+
12+
```scala
13+
import cats.effect._
14+
import com.avast.server.toolkit.execution.ExecutorModule
15+
import com.avast.server.toolkit.http4s._
16+
import com.avast.server.toolkit.system.console.ConsoleModule
17+
import org.http4s.dsl.Http4sDsl
18+
import org.http4s.HttpRoutes
19+
import zio.DefaultRuntime
20+
import zio.interop.catz._
21+
import zio.interop.catz.implicits._
22+
import zio.Task
23+
24+
implicit val runtime = new DefaultRuntime {} // this is just needed in example
25+
26+
val dsl = Http4sDsl[Task] // this is just needed in example
27+
import dsl._
28+
29+
val routes = Http4sRouting.make {
30+
HttpRoutes.of[Task] {
31+
case GET -> Root / "hello" => Ok("Hello World!")
32+
}
33+
}
34+
35+
val resource = for {
36+
executorModule <- ExecutorModule.makeDefault[Task]
37+
console = ConsoleModule.make[Task]
38+
server <- Http4sBlazeServerModule.make[Task](Http4sBlazeServerConfig("127.0.0.1", 0), routes, executorModule.executionContext)
39+
client <- Http4sBlazeClient.make[Task](Http4sBlazeClientConfig(), executorModule.executionContext)
40+
} yield (server, client, console)
41+
42+
val program = resource
43+
.use {
44+
case (server, client, console) =>
45+
client
46+
.expect[String](s"http://127.0.0.1:${server.address.getPort}/hello")
47+
.flatMap(console.printLine)
48+
}
49+
```
50+
51+
```scala
52+
runtime.unsafeRun(program)
53+
// Hello World!
54+
```
55+
56+
## Middleware
57+
58+
### Correlation ID Middleware
59+
60+
```scala
61+
import cats.effect._
62+
import com.avast.server.toolkit.execution.ExecutorModule
63+
import com.avast.server.toolkit.http4s._
64+
import com.avast.server.toolkit.http4s.middleware.CorrelationIdMiddleware
65+
import org.http4s.dsl.Http4sDsl
66+
import org.http4s.HttpRoutes
67+
import zio.DefaultRuntime
68+
import zio.interop.catz._
69+
import zio.interop.catz.implicits._
70+
import zio.Task
71+
72+
val dsl = Http4sDsl[Task] // this is just needed in example
73+
import dsl._
74+
75+
implicit val runtime = new DefaultRuntime {} // this is just needed in example
76+
77+
for {
78+
middleware <- Resource.liftF(CorrelationIdMiddleware.default[Task])
79+
executorModule <- ExecutorModule.makeDefault[Task]
80+
routes = Http4sRouting.make {
81+
middleware.wrap {
82+
HttpRoutes.of[Task] {
83+
case req @ GET -> Root =>
84+
// val correlationId = middleware.retrieveCorrelationId(req)
85+
???
86+
}
87+
}
88+
}
89+
server <- Http4sBlazeServerModule.make[Task](Http4sBlazeServerConfig.localhost8080, routes, executorModule.executionContext)
90+
} yield server
91+
```
92+

docs/index.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
* [Getting Started](#getting-started)
44
* [Rationale](rationale.md)
5+
* [Modules http4s](http4s.md)
56
* [Modules JVM](jvm.md)
67
* [Module PureConfig](pureconfig.md)
78

@@ -11,6 +12,6 @@ Creating a simple HTTP server is as easy as this:
1112

1213
#### build.sbt
1314

14-
[![Maven Central](https://img.shields.io/maven-central/v/com.avast/scala-server-toolkit-pureconfig_2.12)](https://repo1.maven.org/maven2/com/avast/scala-server-toolkit-pureconfig_2.12/)
15+
[![Maven Central](https://img.shields.io/maven-central/v/com.avast/scala-server-toolkit-http4s-blaze-server_2.12)](https://repo1.maven.org/maven2/com/avast/scala-server-toolkit-http4s-blaze-server_2.12/)
1516

16-
`libraryDependencies += "com.avast" %% "scala-server-toolkit-pureconfig" % "<VERSION>"`
17+
`libraryDependencies += "com.avast" %% "scala-server-toolkit-http4s-blaze-server" % "<VERSION>"`

docs/jvm.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ val program = for {
2323
console = ConsoleModule.make[Task]
2424
_ <- console.printLine(s"Random number: $randomNumber")
2525
} yield ()
26-
// program: zio.ZIO[Any, Throwable, Unit] = zio.ZIO$FlatMap@2f1f9515
26+
// program: zio.ZIO[Any, Throwable, Unit] = zio.ZIO$FlatMap@4cc26df
2727

28-
val runtime = new DefaultRuntime {} // this is just in example
29-
// runtime: AnyRef with DefaultRuntime = repl.Session$App$$anon$1@33ebe4f0 // this is just in example
28+
val runtime = new DefaultRuntime {} // this is just needed in example
29+
// runtime: AnyRef with DefaultRuntime = repl.Session$App$$anon$1@3bab95ca // this is just needed in example
3030
runtime.unsafeRun(program)
31-
// Random number: 776310297
31+
// Random number: 1797916077
3232
```
3333

docs/pureconfig.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ import zio.Task
2020
final case class ServerConfiguration(listenAddress: String, listenPort: Int)
2121

2222
implicit val serverConfigurationReader: ConfigReader[ServerConfiguration] = deriveReader
23-
// serverConfigurationReader: ConfigReader[ServerConfiguration] = pureconfig.generic.DerivedConfigReader1$$anon$3@2a8eed58
2423

2524
val maybeConfiguration = PureConfigModule.make[Task, ServerConfiguration]
26-
// maybeConfiguration: Task[Either[cats.data.NonEmptyList[String], ServerConfiguration]] = zio.ZIO$EffectPartial@352bea0e
2725
```
2826

example/src/main/mdoc/http4s.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Module http4s
2+
3+
[![Maven Central](https://img.shields.io/maven-central/v/com.avast/scala-server-toolkit-http4s-blaze-server_2.12)](https://repo1.maven.org/maven2/com/avast/scala-server-toolkit-http4s-blaze-server_2.12/)
4+
5+
`libraryDependencies += "com.avast" %% "scala-server-toolkit-http4s-blaze-server" % "<VERSION>"`
6+
7+
There are `http4s-*` modules that provide easy initialization of a server and a client. Http4s is an interface with multiple possible
8+
implementations - for now we provide only implementations based on [Blaze](https://github.com/http4s/blaze).
9+
10+
Both server and client are configured via configuration `case class` which contains default values taken from the underlying implementations.
11+
12+
```scala mdoc:silent:reset-class
13+
import cats.effect._
14+
import com.avast.server.toolkit.execution.ExecutorModule
15+
import com.avast.server.toolkit.http4s._
16+
import com.avast.server.toolkit.system.console.ConsoleModule
17+
import org.http4s.dsl.Http4sDsl
18+
import org.http4s.HttpRoutes
19+
import zio.DefaultRuntime
20+
import zio.interop.catz._
21+
import zio.interop.catz.implicits._
22+
import zio.Task
23+
24+
implicit val runtime = new DefaultRuntime {} // this is just needed in example
25+
26+
val dsl = Http4sDsl[Task] // this is just needed in example
27+
import dsl._
28+
29+
val routes = Http4sRouting.make {
30+
HttpRoutes.of[Task] {
31+
case GET -> Root / "hello" => Ok("Hello World!")
32+
}
33+
}
34+
35+
val resource = for {
36+
executorModule <- ExecutorModule.makeDefault[Task]
37+
console = ConsoleModule.make[Task]
38+
server <- Http4sBlazeServerModule.make[Task](Http4sBlazeServerConfig("127.0.0.1", 0), routes, executorModule.executionContext)
39+
client <- Http4sBlazeClient.make[Task](Http4sBlazeClientConfig(), executorModule.executionContext)
40+
} yield (server, client, console)
41+
42+
val program = resource
43+
.use {
44+
case (server, client, console) =>
45+
client
46+
.expect[String](s"http://127.0.0.1:${server.address.getPort}/hello")
47+
.flatMap(console.printLine)
48+
}
49+
```
50+
51+
```scala mdoc
52+
runtime.unsafeRun(program)
53+
```
54+
55+
## Middleware
56+
57+
### Correlation ID Middleware
58+
59+
```scala mdoc:silent:reset
60+
import cats.effect._
61+
import com.avast.server.toolkit.execution.ExecutorModule
62+
import com.avast.server.toolkit.http4s._
63+
import com.avast.server.toolkit.http4s.middleware.CorrelationIdMiddleware
64+
import org.http4s.dsl.Http4sDsl
65+
import org.http4s.HttpRoutes
66+
import zio.DefaultRuntime
67+
import zio.interop.catz._
68+
import zio.interop.catz.implicits._
69+
import zio.Task
70+
71+
val dsl = Http4sDsl[Task] // this is just needed in example
72+
import dsl._
73+
74+
implicit val runtime = new DefaultRuntime {} // this is just needed in example
75+
76+
for {
77+
middleware <- Resource.liftF(CorrelationIdMiddleware.default[Task])
78+
executorModule <- ExecutorModule.makeDefault[Task]
79+
routes = Http4sRouting.make {
80+
middleware.wrap {
81+
HttpRoutes.of[Task] {
82+
case req @ GET -> Root =>
83+
// val correlationId = middleware.retrieveCorrelationId(req)
84+
???
85+
}
86+
}
87+
}
88+
server <- Http4sBlazeServerModule.make[Task](Http4sBlazeServerConfig.localhost8080, routes, executorModule.executionContext)
89+
} yield server
90+
```

example/src/main/mdoc/index.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
* [Getting Started](#getting-started)
44
* [Rationale](rationale.md)
5+
* [Modules http4s](http4s.md)
56
* [Modules JVM](jvm.md)
67
* [Module PureConfig](pureconfig.md)
78

@@ -11,6 +12,6 @@ Creating a simple HTTP server is as easy as this:
1112

1213
#### build.sbt
1314

14-
[![Maven Central](https://img.shields.io/maven-central/v/com.avast/scala-server-toolkit-pureconfig_2.12)](https://repo1.maven.org/maven2/com/avast/scala-server-toolkit-pureconfig_2.12/)
15+
[![Maven Central](https://img.shields.io/maven-central/v/com.avast/scala-server-toolkit-http4s-blaze-server_2.12)](https://repo1.maven.org/maven2/com/avast/scala-server-toolkit-http4s-blaze-server_2.12/)
1516

16-
`libraryDependencies += "com.avast" %% "scala-server-toolkit-pureconfig" % "<VERSION>"`
17+
`libraryDependencies += "com.avast" %% "scala-server-toolkit-http4s-blaze-server" % "<VERSION>"`

example/src/main/mdoc/jvm.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ val program = for {
2424
_ <- console.printLine(s"Random number: $randomNumber")
2525
} yield ()
2626

27-
val runtime = new DefaultRuntime {} // this is just in example
27+
val runtime = new DefaultRuntime {} // this is just needed in example
2828
runtime.unsafeRun(program)
2929
```

example/src/main/mdoc/pureconfig.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ that your application's configuration will be in [HOCON](https://github.com/ligh
1010

1111
Loading of configuration is side-effectful so it is wrapped in `F` which is `Sync`. This module also tweaks the error messages a little.
1212

13-
```scala mdoc
13+
```scala mdoc:silent
1414
import com.avast.server.toolkit.pureconfig._
1515
import pureconfig.ConfigReader
1616
import pureconfig.generic.semiauto.deriveReader

0 commit comments

Comments
 (0)