Last active
June 25, 2023 15:33
-
-
Save dacr/24d90e8158a09ea928b214506012b605 to your computer and use it in GitHub Desktop.
Fully asynchronous http client call with json response using akka-http will work in all cases, even with chunked responses, this example add automatic http proxy support. / published by https://github.com/dacr/code-examples-manager #a9b24ee9-78bd-4810-bda8-df5182ea01c7/dde96c15b0d3e7ae35ac094ce0b93ea4d328cff2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// summary : Fully asynchronous http client call with json response using akka-http will work in all cases, even with chunked responses, this example add automatic http proxy support. | |
// keywords : scala, actors, akka, http-client, client, json, json4s, http-proxy | |
// publish : gist | |
// authors : David Crosson | |
// license : Apache NON-AI License Version 2.0 (https://raw.githubusercontent.com/non-ai-licenses/non-ai-licenses/main/NON-AI-APACHE2) | |
// id : a9b24ee9-78bd-4810-bda8-df5182ea01c7 | |
// created-on : 2019-01-25T14:06:58Z | |
// managed-by : https://github.com/dacr/code-examples-manager | |
// execution : scala ammonite script (http://ammonite.io/) - run as follow 'amm scriptname.sc' | |
/* | |
Fully asynchronous json http client calls (get and post) using akka-http and json4s | |
with automatic proxy configuration | |
--- | |
This script execute the corresponding curl JSON http requests : | |
```bash | |
curl -s -X GET -H "Content-Type: application/json" http://httpbin.org/json | jq .slideshow.title | |
curl -s -X POST -d '{"name":"John Doe", "age":42}' -H "Content-Type: application/json" http://httpbin.org/post | jq .json | |
``` | |
for local execution : | |
``` | |
docker run -d -p 8044:80 kennethreitz/httpbin | |
curl -v -X POST -d '{"name":"John Doe", "age":42}' -H "Content-Type: application/json" http://127.0.0.1:8044/post | jq .json | |
``` | |
--- | |
scala in script mode using ammonite REPL (http://ammonite.io/) | |
`amm scriptname.scala` to start it | |
*/ | |
import $ivy.`com.typesafe.akka::akka-http:10.2.4` | |
import $ivy.`com.typesafe.akka::akka-stream:2.6.13` | |
import $ivy.`de.heikoseeberger::akka-http-json4s:1.35.3` | |
import $ivy.`org.json4s::json4s-jackson:3.6.11` | |
import java.net.InetSocketAddress | |
import akka.http.scaladsl._ | |
import akka.http.scaladsl.marshalling.Marshal | |
import akka.http.scaladsl.model._ | |
import akka.http.scaladsl.settings.{ClientConnectionSettings, ConnectionPoolSettings} | |
import akka.http.scaladsl.unmarshalling.Unmarshal | |
import akka.util.ByteString | |
import scala.concurrent._ | |
import scala.concurrent.duration._ | |
import scala.util.{Failure, Success} | |
import de.heikoseeberger.akkahttpjson4s.Json4sSupport._ | |
import org.json4s.{DefaultFormats, JValue} | |
import org.json4s.jackson.JsonMethods._ | |
import org.json4s._ | |
import scala.concurrent.ExecutionContext.Implicits.global | |
import scala.util.Properties.{envOrNone, propOrNone} | |
case class HttpProxyConfig(host: String, port: Int) | |
object HttpProxyConfig { | |
def fromPropOrEnv(): Option[HttpProxyConfig] = { | |
val keys = Stream("http_proxy", "https_proxy") | |
keys.flatMap{key => | |
propOrNone(key).orElse(envOrNone(key)).flatMap(fromProvidedString) | |
}.headOption | |
} | |
def fromProvidedString(spec: String): Option[HttpProxyConfig] = { | |
val extractor = """^https?://([^:/]+)(?::(\d+))?/?$""".r | |
extractor.findFirstIn(spec.trim.toLowerCase()).collect { | |
case extractor(host, null) => HttpProxyConfig(host, 80) | |
case extractor(host, port) => HttpProxyConfig(host, port.toInt) | |
} | |
} | |
} | |
object TestThat { | |
implicit val system = akka.actor.ActorSystem("MySystem") | |
implicit val executionContext = system.dispatcher | |
implicit val serialization = jackson.Serialization | |
implicit val formats = DefaultFormats | |
private val transport = | |
HttpProxyConfig.fromPropOrEnv().collect { | |
case proxy@HttpProxyConfig(proxyHost, proxyPort) => | |
println(s"using proxy $proxy") | |
ClientTransport.httpsProxy(InetSocketAddress.createUnresolved(proxyHost, proxyPort)) | |
} | |
private val connectionSettings = | |
ClientConnectionSettings(system) | |
.withTransport(transport.getOrElse(ClientTransport.TCP)) | |
.withIdleTimeout(10.seconds) | |
.withConnectingTimeout(5.seconds) | |
private val settings = | |
ConnectionPoolSettings(system) | |
.withConnectionSettings(connectionSettings) | |
.withMaxConnections(10) | |
.withMaxRetries(1) | |
def jsonGet(requestUri: String): Future[JValue] = { | |
val request = HttpRequest(uri = requestUri) | |
val futureResponse = Http().singleRequest(request, settings = settings) | |
futureResponse.flatMap { response => Unmarshal(response.entity).to[JValue] } | |
} | |
def jsonPost(requestUri: String, jvalue: JValue): Future[JValue] = { | |
Marshal(jvalue).to[RequestEntity].flatMap { entity => | |
val request = HttpRequest(method = HttpMethods.POST, uri = requestUri, entity=entity) | |
val futureResponse = Http().singleRequest(request, settings = settings) | |
futureResponse.flatMap { response => Unmarshal(response.entity).to[JValue] } | |
} | |
} | |
def printJson(message:String, jvalue: JValue): Unit = { | |
println(message+compact(render(jvalue))) | |
System.out.flush() | |
} | |
val httpbin="http://httpbin.org" | |
//val httpbin="http://localhost:8044" // if 'docker run -d -p 8044:80 kennethreitz/httpbin' is running | |
val f1 = jsonGet(s"$httpbin/json") | |
f1.onComplete { | |
case Success(jvalue) => printJson("f1=", jvalue \ "slideshow" \ "title") | |
case Failure(ex) => ex.printStackTrace() | |
} | |
val jsonData = Extraction.decompose(Map("age" -> 42)) | |
val f2 = jsonPost(s"$httpbin/post", jsonData) | |
f2.onComplete { | |
case Success(jvalue) => printJson("f2=", jvalue \ "json") | |
case Failure(ex) => ex.printStackTrace() | |
} | |
// Do not exit before the future has completed ;) | |
def andWait(): Unit = Await.ready(Future.sequence(List(f1, f2)), 10.seconds) | |
} | |
TestThat.andWait() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment