diff options
-rw-r--r-- | README.md | 21 | ||||
-rw-r--r-- | async-http-client-handler/future/src/main/scala/com/softwaremill/sttp/asynchttpclient/future/FutureAsyncHttpClientHandler.scala | 33 | ||||
-rw-r--r-- | async-http-client-handler/scalaz/src/main/scala/com/softwaremill/sttp/asynchttpclient/scalaz/ScalazAsyncHttpClientHandler.scala | 32 | ||||
-rw-r--r-- | async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/internal/AsyncHttpClientHandler.scala (renamed from async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/AsyncHttpClientHandler.scala) | 47 | ||||
-rw-r--r-- | build.sbt | 23 | ||||
-rw-r--r-- | tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala | 9 | ||||
-rw-r--r-- | tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala | 5 |
7 files changed, 135 insertions, 35 deletions
@@ -240,24 +240,33 @@ val response: Future[Response[Source[ByteString, Any]]] = To use, add the following dependency to your project: ```scala -"com.softwaremill.sttp" %% "async-http-client-handler" % version +"com.softwaremill.sttp" %% "async-http-client-handler-future" % version +// or +"com.softwaremill.sttp" %% "async-http-client-handler-scalaz" % version ``` This handler depends on [async-http-handler](https://github.com/AsyncHttpClient/async-http-client). A fully **asynchronous** handler, which uses [Netty](http://netty.io) behind the -scenes. Sending a request returns a response wrapped in a `Future`. Different -wrappers will be added in the future. +scenes. + +The responses are either wrapped in a `Future`, or a +[Scalaz](https://github.com/scalaz/scalaz) `Task`, depending on the +dependency chosen. In the latter case, there's an additional transitive +dependency on `scalaz-concurrent`. Next you'll need to add an implicit value: ```scala -implicit val sttpHandler = new AsyncHttpClientHandler() +implicit val sttpHandler = new FutureAsyncHttpClientHandler() + +// or, if you're using the scalaz version: +implicit val sttpHandler = new ScalazAsyncHttpClientHandler() // or, if you'd like to use custom configuration: -implicit val sttpHandler = new AsyncHttpClientHandler(asyncHttpClientConfig) +implicit val sttpHandler = new FutureAsyncHttpClientHandler(asyncHttpClientConfig) // or, if you'd like to instantiate the AsyncHttpClient yourself: -implicit val sttpHandler = new AsyncHttpClientHandler(asyncHttpClient) +implicit val sttpHandler = new FutureAsyncHttpClientHandler(asyncHttpClient) ``` Streaming is not (yet) supported. diff --git a/async-http-client-handler/future/src/main/scala/com/softwaremill/sttp/asynchttpclient/future/FutureAsyncHttpClientHandler.scala b/async-http-client-handler/future/src/main/scala/com/softwaremill/sttp/asynchttpclient/future/FutureAsyncHttpClientHandler.scala new file mode 100644 index 0000000..624c51b --- /dev/null +++ b/async-http-client-handler/future/src/main/scala/com/softwaremill/sttp/asynchttpclient/future/FutureAsyncHttpClientHandler.scala @@ -0,0 +1,33 @@ +package com.softwaremill.sttp.asynchttpclient.future + +import com.softwaremill.sttp.asynchttpclient.internal.{ + AsyncHttpClientHandler, + WrapperFromAsync +} +import org.asynchttpclient.{ + AsyncHttpClient, + AsyncHttpClientConfig, + DefaultAsyncHttpClient +} + +import scala.concurrent.{Future, Promise} + +class FutureAsyncHttpClientHandler(asyncHttpClient: AsyncHttpClient) + extends AsyncHttpClientHandler[Future](asyncHttpClient, FutureFromAsync) { + + def this() = this(new DefaultAsyncHttpClient()) + def this(cfg: AsyncHttpClientConfig) = this(new DefaultAsyncHttpClient(cfg)) +} + +private[asynchttpclient] object FutureFromAsync + extends WrapperFromAsync[Future] { + override def apply[T]( + register: ((Either[Throwable, T]) => Unit) => Unit): Future[T] = { + val p = Promise[T]() + register { + case Left(t) => p.failure(t) + case Right(t) => p.success(t) + } + p.future + } +} diff --git a/async-http-client-handler/scalaz/src/main/scala/com/softwaremill/sttp/asynchttpclient/scalaz/ScalazAsyncHttpClientHandler.scala b/async-http-client-handler/scalaz/src/main/scala/com/softwaremill/sttp/asynchttpclient/scalaz/ScalazAsyncHttpClientHandler.scala new file mode 100644 index 0000000..e2c664c --- /dev/null +++ b/async-http-client-handler/scalaz/src/main/scala/com/softwaremill/sttp/asynchttpclient/scalaz/ScalazAsyncHttpClientHandler.scala @@ -0,0 +1,32 @@ +package com.softwaremill.sttp.asynchttpclient.scalaz + +import com.softwaremill.sttp.asynchttpclient.internal.{ + AsyncHttpClientHandler, + WrapperFromAsync +} +import org.asynchttpclient.{ + AsyncHttpClient, + AsyncHttpClientConfig, + DefaultAsyncHttpClient +} + +import scalaz.{-\/, \/-} +import scalaz.concurrent.Task + +class ScalazAsyncHttpClientHandler(asyncHttpClient: AsyncHttpClient) + extends AsyncHttpClientHandler[Task](asyncHttpClient, TaskFromAsync) { + + def this() = this(new DefaultAsyncHttpClient()) + def this(cfg: AsyncHttpClientConfig) = this(new DefaultAsyncHttpClient(cfg)) +} + +private[asynchttpclient] object TaskFromAsync extends WrapperFromAsync[Task] { + override def apply[T]( + register: ((Either[Throwable, T]) => Unit) => Unit): Task[T] = + Task.async { cb => + register { + case Left(t) => cb(-\/(t)) + case Right(t) => cb(\/-(t)) + } + } +} diff --git a/async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/AsyncHttpClientHandler.scala b/async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/internal/AsyncHttpClientHandler.scala index ecd49cd..924e0bc 100644 --- a/async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/AsyncHttpClientHandler.scala +++ b/async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/internal/AsyncHttpClientHandler.scala @@ -1,4 +1,4 @@ -package com.softwaremill.sttp.asynchttpclient +package com.softwaremill.sttp.asynchttpclient.internal import java.nio.charset.Charset @@ -7,34 +7,31 @@ import com.softwaremill.sttp.{Request, Response, SttpHandler} import org.asynchttpclient.{ AsyncCompletionHandler, AsyncHttpClient, - AsyncHttpClientConfig, - DefaultAsyncHttpClient, RequestBuilder, Request => AsyncRequest, Response => AsyncResponse } -import scala.concurrent.{Future, Promise} import scala.collection.JavaConverters._ - -class AsyncHttpClientHandler(asyncHttpClient: AsyncHttpClient) - extends SttpHandler[Future, Nothing] { - def this() = this(new DefaultAsyncHttpClient()) - def this(cfg: AsyncHttpClientConfig) = this(new DefaultAsyncHttpClient(cfg)) - - override def send[T](r: Request[T, Nothing]): Future[Response[T]] = { - val p = Promise[Response[T]]() - asyncHttpClient - .prepareRequest(requestToAsync(r)) - .execute(new AsyncCompletionHandler[AsyncResponse] { - override def onCompleted(response: AsyncResponse): AsyncResponse = { - p.success(readResponse(response, r.responseAs)) - response - } - override def onThrowable(t: Throwable): Unit = p.failure(t) - }) - - p.future +import scala.language.higherKinds + +private[asynchttpclient] class AsyncHttpClientHandler[R[_]]( + asyncHttpClient: AsyncHttpClient, + wrapper: WrapperFromAsync[R]) + extends SttpHandler[R, Nothing] { + + override def send[T](r: Request[T, Nothing]): R[Response[T]] = { + wrapper { cb => + asyncHttpClient + .prepareRequest(requestToAsync(r)) + .execute(new AsyncCompletionHandler[AsyncResponse] { + override def onCompleted(response: AsyncResponse): AsyncResponse = { + cb(Right(readResponse(response, r.responseAs))) + response + } + override def onThrowable(t: Throwable): Unit = cb(Left(t)) + }) + } } private def requestToAsync(r: Request[_, Nothing]): AsyncRequest = { @@ -111,3 +108,7 @@ class AsyncHttpClientHandler(asyncHttpClient: AsyncHttpClient) } } } + +private[asynchttpclient] trait WrapperFromAsync[R[_]] { + def apply[T](register: (Either[Throwable, T] => Unit) => Unit): R[T] +} @@ -66,7 +66,7 @@ lazy val akkaHttpHandler: Project = (project in file("akka-http-handler")) libraryDependencies ++= Seq( akkaHttp ) - ) dependsOn (core) + ) dependsOn core lazy val asyncHttpClientHandler: Project = (project in file( "async-http-client-handler")) @@ -76,7 +76,24 @@ lazy val asyncHttpClientHandler: Project = (project in file( libraryDependencies ++= Seq( "org.asynchttpclient" % "async-http-client" % "2.0.33" ) - ) dependsOn (core) + ) dependsOn core + +lazy val futureAsyncHttpClientHandler: Project = (project in file( + "async-http-client-handler/future")) + .settings(commonSettings: _*) + .settings( + name := "async-http-client-handler-future" + ) dependsOn asyncHttpClientHandler + +lazy val scalazAsyncHttpClientHandler: Project = (project in file( + "async-http-client-handler/scalaz")) + .settings(commonSettings: _*) + .settings( + name := "async-http-client-handler-scalaz", + libraryDependencies ++= Seq( + "org.scalaz" %% "scalaz-concurrent" % "7.2.14" + ) + ) dependsOn asyncHttpClientHandler lazy val tests: Project = (project in file("tests")) .settings(commonSettings: _*) @@ -91,4 +108,4 @@ lazy val tests: Project = (project in file("tests")) "ch.qos.logback" % "logback-classic" % "1.2.3" ).map(_ % "test"), libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value % "test" - ) dependsOn (core, akkaHttpHandler, asyncHttpClientHandler) + ) dependsOn (core, akkaHttpHandler, futureAsyncHttpClientHandler, scalazAsyncHttpClientHandler) diff --git a/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala b/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala index 96fbbae..f456fb1 100644 --- a/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala +++ b/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala @@ -16,7 +16,8 @@ import com.typesafe.scalalogging.StrictLogging import org.scalatest.concurrent.{IntegrationPatience, ScalaFutures} import org.scalatest.{BeforeAndAfterAll, FlatSpec, Matchers} import better.files._ -import com.softwaremill.sttp.asynchttpclient.AsyncHttpClientHandler +import com.softwaremill.sttp.asynchttpclient.future.FutureAsyncHttpClientHandler +import com.softwaremill.sttp.asynchttpclient.scalaz.ScalazAsyncHttpClientHandler import scala.language.higherKinds @@ -117,8 +118,10 @@ class BasicTests ForceWrappedValue.id) runTests("Akka HTTP")(new AkkaHttpSttpHandler(actorSystem), ForceWrappedValue.future) - runTests("Async Http Client")(new AsyncHttpClientHandler(), - ForceWrappedValue.future) + runTests("Async Http Client - Future")(new FutureAsyncHttpClientHandler(), + ForceWrappedValue.future) + runTests("Async Http Client - Scalaz")(new ScalazAsyncHttpClientHandler(), + ForceWrappedValue.scalazTask) def runTests[R[_]](name: String)( implicit handler: SttpHandler[R, Nothing], diff --git a/tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala b/tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala index 6d83848..9114d8b 100644 --- a/tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala +++ b/tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala @@ -9,6 +9,7 @@ import org.scalatest.concurrent.ScalaFutures import scala.concurrent.Future import scala.language.higherKinds +import scalaz.concurrent.Task trait TestHttpServer extends BeforeAndAfterAll with ScalaFutures { this: Suite => @@ -43,6 +44,10 @@ trait ForceWrapped extends ScalaFutures { this: Suite => override def force[T](wrapped: Future[T]): T = wrapped.futureValue } + val scalazTask = new ForceWrappedValue[Task] { + override def force[T](wrapped: Task[T]): T = + wrapped.unsafePerformSync + } } implicit class ForceDecorator[R[_], T](wrapped: R[T]) { def force()(implicit fwv: ForceWrappedValue[R]): T = fwv.force(wrapped) |