diff --git a/build.sbt b/build.sbt index a1510d3c0..0d0302eca 100644 --- a/build.sbt +++ b/build.sbt @@ -140,6 +140,14 @@ lazy val `idlgen-sbt` = project .settings(buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion)) .settings(buildInfoPackage := "freestyle.rpc.idlgen") +lazy val `http-server` = project + .in(file("modules/http/server")) + .dependsOn(common % "compile->compile;test->test") + .dependsOn(internal) + .settings(moduleName := "frees-rpc-http-server") + .settings(rpcHttpServerSettings) + .disablePlugins(ScriptedPlugin) + ////////////////////////// //// MODULES REGISTRY //// ////////////////////////// @@ -160,7 +168,8 @@ lazy val allModules: Seq[ProjectReference] = Seq( `dropwizard-client`, testing, ssl, - `idlgen-core` + `idlgen-core`, + `http-server` ) lazy val allModulesDeps: Seq[ClasspathDependency] = diff --git a/modules/http/server/src/main/scala/HttpConfig.scala b/modules/http/server/src/main/scala/HttpConfig.scala new file mode 100644 index 000000000..93fabaa36 --- /dev/null +++ b/modules/http/server/src/main/scala/HttpConfig.scala @@ -0,0 +1,20 @@ +/* + * Copyright 2017-2018 47 Degrees, LLC. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package freestyle.rpc.http +package server + +final case class HttpConfig(host: String, port: Int) diff --git a/modules/http/server/src/main/scala/HttpServerBuilder.scala b/modules/http/server/src/main/scala/HttpServerBuilder.scala new file mode 100644 index 000000000..e399437a5 --- /dev/null +++ b/modules/http/server/src/main/scala/HttpServerBuilder.scala @@ -0,0 +1,31 @@ +/* + * Copyright 2017-2018 47 Degrees, LLC. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package freestyle.rpc.http +package server + +import cats.effect.Effect +import monix.execution.Scheduler +import org.http4s.HttpService +import org.http4s.server.blaze.BlazeBuilder + +class HttpServerBuilder[F[_]: Effect](implicit C: HttpConfig, S: Scheduler) { + + def build(service: HttpService[F], prefix: String = "/"): BlazeBuilder[F] = + BlazeBuilder[F] + .bindHttp(C.port, C.host) + .mountService(service, prefix) +} diff --git a/modules/http/server/src/main/scala/HttpServerStream.scala b/modules/http/server/src/main/scala/HttpServerStream.scala new file mode 100644 index 000000000..f71f7d9ba --- /dev/null +++ b/modules/http/server/src/main/scala/HttpServerStream.scala @@ -0,0 +1,35 @@ +/* + * Copyright 2017-2018 47 Degrees, LLC. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package freestyle.rpc.http +package server + +import cats.effect.Effect +import fs2.{Stream, StreamApp} +import monix.execution.Scheduler +import org.http4s.HttpService + +object HttpServerStream { + + def apply[F[_]: Effect](service: HttpService[F], prefix: String = "/")( + implicit C: HttpConfig, + S: Scheduler): Stream[F, StreamApp.ExitCode] = { + val httpServerBuilder: HttpServerBuilder[F] = new HttpServerBuilder[F] + + httpServerBuilder.build(service, prefix).serve + } + +} diff --git a/modules/http/server/src/test/scala/ExampleService.scala b/modules/http/server/src/test/scala/ExampleService.scala new file mode 100644 index 000000000..0433340a2 --- /dev/null +++ b/modules/http/server/src/test/scala/ExampleService.scala @@ -0,0 +1,33 @@ +/* + * Copyright 2017-2018 47 Degrees, LLC. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package freestyle.rpc.http +package server + +import cats.effect.Effect +import monix.execution.Scheduler +import org.http4s.HttpService +import org.http4s.dsl.Http4sDsl + +class ExampleService[F[_]: Effect] extends Http4sDsl[F] { + + def service(implicit scheduler: Scheduler): HttpService[F] = + HttpService[F] { + case GET -> Root / "ping" => + Ok("pong") + } + +} diff --git a/modules/http/server/src/test/scala/HttpServerTests.scala b/modules/http/server/src/test/scala/HttpServerTests.scala new file mode 100644 index 000000000..45779908a --- /dev/null +++ b/modules/http/server/src/test/scala/HttpServerTests.scala @@ -0,0 +1,45 @@ +/* + * Copyright 2017-2018 47 Degrees, LLC. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package freestyle.rpc.http +package server + +import cats.effect.IO +import monix.execution.Scheduler +import org.http4s.HttpService +import org.scalatest.{Assertion, Matchers, WordSpec} + +class HttpServerTests extends WordSpec with Matchers { + + implicit val S: Scheduler = monix.execution.Scheduler.Implicits.global + implicit val C: HttpConfig = HttpConfig("0.0.0.0", 8090) + + val service: HttpService[IO] = new ExampleService[IO].service + val prefix: String = "/" + + def ok: Assertion = 1 shouldBe 1 + + "HttpServerBuilder.build" should { + + "work as expected" in { + + new HttpServerBuilder[IO].build(service, prefix) + + ok + } + + } +} diff --git a/project/ProjectPlugin.scala b/project/ProjectPlugin.scala index 2d092af71..4b0475c3a 100644 --- a/project/ProjectPlugin.scala +++ b/project/ProjectPlugin.scala @@ -23,6 +23,7 @@ object ProjectPlugin extends AutoPlugin { val frees: String = "0.8.0" val fs2ReactiveStreams: String = "0.5.1" val grpc: String = "1.10.0" + val http4s = "0.18.3" val nettySSL: String = "2.0.7.Final" val pbdirect: String = "0.1.0" val prometheus: String = "0.3.0" @@ -150,6 +151,15 @@ object ProjectPlugin extends AutoPlugin { ) ) + lazy val rpcHttpServerSettings: Seq[Def.Setting[_]] = Seq( + libraryDependencies ++= Seq( + %%("http4s-dsl", V.http4s), + %%("http4s-blaze-server", V.http4s), + %%("http4s-circe", V.http4s), + %%("http4s-blaze-client", V.http4s) % Test + ) + ) + lazy val docsSettings = Seq( // Pointing to https://github.com/frees-io/freestyle/tree/master/docs/src/main/tut/docs/rpc tutTargetDirectory := baseDirectory.value.getParentFile.getParentFile / "docs" / "src" / "main" / "tut" / "docs" / "rpc"