PageRenderTime 31ms CodeModel.GetById 23ms app.highlight 6ms RepoModel.GetById 1ms app.codeStats 0ms

/content/posts/gigahorse-010.md

https://github.com/eed3si9n/eed3si9n.com
Markdown | 118 lines | 92 code | 26 blank | 0 comment | 0 complexity | 665087125879de24510d2d3c62151f7e MD5 | raw file
  1---
  2title:       "Gigahorse 0.1.0"
  3type:        story
  4date:        2016-07-29
  5changed:     2016-08-02
  6draft:       false
  7promote:     true
  8sticky:      false
  9url:         /gigahorse-010
 10aliases:     [ /node/204 ]
 11---
 12
 13  [1]: http://eed3si9n.com/gigahorse/
 14  [AHC]: https://github.com/AsyncHttpClient/async-http-client/tree/1.9.x
 15  [netty]: http://netty.io
 16  [sslconfig]: https://github.com/typesafehub/ssl-config
 17  [config]: https://github.com/typesafehub/config
 18  [ws]: https://www.playframework.com/documentation/2.5.x/ScalaWS
 19  [dispatch]: http://dispatch.databinder.net/Dispatch.html
 20  [datatype]: http://www.scala-sbt.org/0.13/docs/Datatype.html
 21  [@wsargent]: https://github.com/wsargent
 22  [@n8han]: https://github.com/n8han
 23  [@Duhemm]: https://github.com/Duhemm
 24  [@wheaties]: https://github.com/wheaties
 25  [AutoLift]: https://github.com/wheaties/AutoLifts
 26  [stacking]: http://eed3si9n.com/herding-cats/stacking-future-and-either.html
 27  [thegigahorse]: http://madmax.wikia.com/wiki/The_Gigahorse
 28
 29> Update: please use Gigahorse 0.1.1
 30
 31Gigahorse 0.1.0 is now released. It is an HTTP client for Scala with Async Http Client underneath. Please see [Gigahorse docs][1] for the details. Here's an example snippet to get the feel of the library.
 32
 33```scala
 34scala> import gigahorse._
 35scala> import scala.concurrent._, duration._
 36scala> Gigahorse.withHttp(Gigahorse.config) { http =>
 37         val r = Gigahorse.url("http://api.duckduckgo.com").get.
 38           addQueryString(
 39             "q" -> "1 + 1",
 40             "format" -> "json"
 41           )
 42         val f = http.run(r, Gigahorse.asString andThen {_.take(60)})
 43         Await.result(f, 120.seconds)
 44       }
 45```
 46
 47<!--more-->
 48
 49### background
 50
 51There's a constant need for HTTP client library. Looking around for Dispatch Reboot replacement, I couldn't find something that doesn't pull in extra dependencies beyond AHC, so I decided to port Play's [WS API][ws]. Because I didn't want to require JDK 8, I've intentionally picked AHC 1.9, but hopefully AHC 2.0 version will follow up eventually.
 52WS API is one of the hardened libraries by Lightbend engineers, so it made sense as a good starting point. This is true especially around sensible default values and secure defaults in [@wsargent][@wsargent]'s [SSL Config][sslconfig]. The fluent style API also seems to be popular too.
 53
 54### sbt-datatype 0.2.3
 55
 56The immutable datatypes used in Gigahorse are generated using [sbt-datatype][datatype], which is a pseudo case class generator that I designed and implemented together with [@Duhemm][@Duhemm]. It uses Avro-like schema like this:
 57
 58    {
 59      "name": "Config",
 60      "namespace": "gigahorse",
 61      "type": "record",
 62      "target": "Scala",
 63      "fields": [
 64        {
 65          "name": "connectTimeout",
 66          "type": "scala.concurrent.duration.Duration",
 67          "doc": [
 68            "The maximum time an `HttpClient` can wait when connecting to a remote host. (Default: 120s)"
 69          ],
 70          "default": "ConfigDefaults.defaultConnectTimeout",
 71          "since": "0.1.0"
 72        },
 73        ....
 74      ]
 75    }
 76
 77and it generates pseudo case classes that's growable over time. Using the `since` field, it can generate multiple `apply` constructor, and it does not generate `unapply` or expose `copy` because they can not grow in binary compatible way. Instead it generates fluent style method:
 78
 79```scala
 80  def withConnectTimeout(connectTimeout: scala.concurrent.duration.Duration): Config = {
 81    copy(connectTimeout = connectTimeout)
 82  }
 83```
 84
 85This also motivated us to add a few features such as `extra` field to hand-code convenience functions, for example to do `Some(...)` wrapping:
 86
 87```scala
 88  def withAuth(auth: Realm): Config = copy(authOpt = Some(auth))
 89```
 90
 91### using functions
 92
 93The API design of Gigahorse is also influenced by that of [Dispatch Reboot][dispatch] by [@n8han][@n8han]. In particular, Dispatch uses function `Response => A` to transform the response from the beginning, while with WS API, you would map over the returned `Future`. Gigahorse allows both styles, but the docs emphasizes the `http.run(r, f)`:
 94
 95```scala
 96val f = http.run(r, Gigahorse.asString andThen {_.take(60)})
 97```
 98
 99### using Either
100
101Another influence from Dispatch is lifting of `Future[A]` to `Future[Either[Throwable, A]]`. To avoid LGPL, I didn't look at the implementation, but Dispatch adds extention method `either` on `Future` using implicits that does that.
102I wanted to avoid implicits here, so instead I created a hacky solution called `FutureLifter` that looks like this:
103
104```scala
105val f = http.run(r, Gigahorse.asEither map { Gigahorse.asString })
106```
107
108`asEither` kind of feels like a function, but in addition to mapping to `Right(...)` it also does `recoverWith(...)` to `Left(...)`. This is fine, but you also would end up with multiple `Future[Either[Throwable, A]]`, so you might need Cats ([Stacking Future and Either][stacking]), Scalaz, and/or [@wheaties][@wheaties]'s [AutoLift][AutoLift] to compose them sanely.
109
110### naming
111
112<img src="/images/gigahorse-800.jpeg">
113
114Gigahorse is named after [the custon vehicle][thegigahorse] driven by Immortan Joe in Mad Max Fury Road. I was thinking around the concept of modified stock cars of the moonshine runners. That lead me to the post-apocalyptic rat rod mayhem that is Mad Max Fury Road, which I've seen multiple times. The fact that it has two working Big Block V8 mortors felt right for this project.
115
116### summary
117
118Gigahorse is a new HTTP client for Scala, but it's based on the foundation of existing works like [AHC][AHC]. Let me know if you try it.