Move to 0.7.20 or newer releases, now!
Sunsetting of Bintray is forcing our hand to give up on versions before 0.7.20
. So please migrate to 0.7.20
or later ASAP. Checkout the releases https://github.com/ppurang/asynch/releases.
Prologue
Sbt library dependency
libraryDependencies += "org.purang.net" %% "asynch" % "0.7.20" withSources()
From Maven Central, check the badge above.
For older releases use:
resolvers += "ppurang bintray" at "https://dl.bintray.com/ppurang/maven"
Note: Soon the bintray service will be sunset and not available hence use the maven central versions. Checkout - https://search.maven.org/search?q=org.purang.net
Quick
The code below executes a blocking POST
against http://httpize.herokuapp.com/post
with request headers Accept: application/json, text/html, text/plain
, Cache-Control: no-cache
and Content-Type: text/plain
, and request entity some very important message
. It expects a 200
with some response body. If it encounters an exception or another status code then they are returned too. The type returned is \/[String, String]
: left String
(-\/[String]
) indicates the error, and the right String
(\/-[String]
) contains the successful response body.
import org.purang.net.http._
import scalaz._, Scalaz._
import org.purang.net.http.ning.DefaultAsyncHttpClientNonBlockingExecutor
import org.asynchttpclient.DefaultAsyncHttpClientConfig
implicit val sse = java.util.concurrent.Executors.newScheduledThreadPool(2)
val config = new DefaultAsyncHttpClientConfig.Builder()
.setCompressionEnforced(true)
.setConnectTimeout(500)
.setRequestTimeout(3000)
.setCookieStore(null) //recommended as clients should be stateless
.build()
implicit val newExecutor = DefaultAsyncHttpClientNonBlockingExecutor(config)
val response = (POST >
"http://httpize.herokuapp.com/post" >>
("Accept" `:` "application/json" ++ "text/html" ++ "text/plain") ++
("Cache-Control" `:` "no-cache") ++
("Content-Type" `:` "text/plain") >>>
"some very important message").~>(
(x: ExecutedRequest) => x.fold(
t => t._1.getMessage.left,
{
case (200, _, Some(body), _) => body.right
case (status: Status, headers: Headers, body: Body, req: Request) => status.toString.left
}
))
// close the client
// newExecutor.close()
// sse.shutdownNow()
For examples of non blocking/ asynchronous calls look at src/test/scala/NonBlockingExecutorSpec.scala
For examples of blocking calls look at src/test/scala/ExecutorSpec.scala
For an example of a custom configured executor look at src/test/scala/CustomNingExecutorSpec.scala. Here is the meat of the code:
implicit val sse = java.util.concurrent.Executors.newScheduledThreadPool(2)
val config = new DefaultAsyncHttpClientConfig.Builder()
.setCompressionEnforced(true)
.setConnectTimeout(500)
.setRequestTimeout(3000)
.setCookieStore(null) //recommended as clients should be stateless
.build()
implicit val newExecutor = DefaultAsyncHttpClientNonBlockingExecutor(config)
Types
type FailedRequest = (Throwable, Request)
type AResponse = (Status, Headers, Body, Request)
type or[+E, +A] = \/[E, A]
type ExecutedRequest = FailedRequest or AResponse
type NonBlockingExecutedRequest = scalaz.concurrent.Task[AResponse]
trait NonBlockingExecutor extends (Timeout => Request => NonBlockingExecutedRequest)
type ExecutedRequestHandler[T] = (ExecutedRequest => T)
Testing support? Easy.
Here is an example of test executor src/test/scala/TestExecutor.scala that looks up things in a Map used internally to test things.
Philosophy
- Timeouts - Yes! we do timeouts.
- Immutable - API to assemble requests and response handling is immutable.
- Easy parts are easy (if you can look beyond weird operators and operator precedence). For example a request is easy to assemble
GET > "http://www.google.com"
actually even theGET
isn't really needed either("http://www.host.com" >> Accept(ApplicationJson))
. - Full control - you are forced to deal with the exceptions and responses. You even have the request that got executed if you wanted to modify it to re-execute.
- Parts are done with scalaz goodness.
Limitations
- Too many implicits!
- Entity bodies can only be strings or types that can implictly be converted to strings. No endless Streams.
- No explicit Authentication support.
- No web socket or such support.
- Underlying http call infrastructure is as asynchronous, fast, bug-free as async-http-client.
- No metrics and no circuit breakers.
Help/Join
Critique is sought actively. Help will be provided keenly. Contributions are welcome. Install simple build tool 0.13+, fork the repo and get hacking.
LICENSE
licenses += ("BSD", url("http://www.tldrlegal.com/license/bsd-3-clause-license-%28revised%29"))
Disclaimer
Use at your own risk. See LICENSE for more details.