Spray(12)spray-can - Http Client HttpDialog and Spray Client
诸葛乐逸
2023-12-01
Spray(12)spray-can - Http Client HttpDialog and Spray Client
3. HttpClient
HttpsExample
The difference between Http and Https for HttpDialog is that, what tags we bring to it.
val responseFuture =
HttpDialog(httpClient, "github.com", port = 443, HttpClient.SslEnabled)
.send(HttpRequest(uri = "/spray/spray"))
.end
Google Query Example
Check all the configuration in spray-can/src/main/resources/reference.conf
What I need to for the spray-can is as follow:
spray.can {
client {
idle-timeout = 60 s
request-timeout = 30 s
}
server {
request-timeout = 50s
}
}
We can do the requests in queue.
4. Http Dialog
package com.sillycat.easyspray.httpdialog
import java.util.concurrent.TimeUnit._
import scala.concurrent.Future
import scala.concurrent.duration.Duration
import spray.can.client.{ HttpDialog, HttpClient }
import spray.io._
import spray.http._
import HttpMethods._
import akka.actor.ActorSystem
import akka.actor.Props
import spray.httpx.RequestBuilding._
object SimpleExampleApp extends App {
implicit valsystem = ActorSystem("simple-example")
import system.log
//different way to get the httpClient object
val ioBridge = IOExtension(system).ioBridge()
val client = system.actorOf(Props(new HttpClient(ioBridge)))
//simple request
val response: Future[HttpResponse] =
HttpDialog(client, "www.google.com", 80)
.send(HttpRequest(method = GET, uri = "/"))
.end
//multiple requests, fire one by one
val responses1: Future[Seq[HttpResponse]] =
HttpDialog(client, "www.google.com", 80)
.send(Post("/shout", "yeah!"))
.awaitResponse
.send(Put("/count", "42"))
.end
//multiple requests, fire at same time
val responses2: Future[Seq[HttpResponse]] =
HttpDialog(client, "www.google.com", 80)
.send(Get("/img/a.gif"))
.send(Get("/img/b.gif"))
.send(Get("/img/c.gif"))
.end
//multiple requests, fire follow this request -> response ->request
val response3: Future[HttpResponse] =
HttpDialog(client, "www.google.com", 80)
.send(Get("/ping"))
.reply(response => Get("/ping2", response.entity))
.end
}
5. Spray Client
The default configuration file will be spray-client/src/main/resources/reference.conf
My first Simplest Example
package com.sillycat.easyspray.sprayclient
import spray.client._
import scala.concurrent.Future
import akka.actor._
import spray.can.client.HttpClient
import spray.client.HttpConduit
import spray.io._
import spray.util._
import spray.http._
import HttpMethods._
import scala.util.{Success, Failure}
import scala.concurrent.duration._
import akka.actor.{Props, ActorSystem}
import akka.pattern.ask
import akka.util.Timeout
import akka.event.Logging
import spray.json.{JsonFormat, DefaultJsonProtocol}
import spray.httpx.SprayJsonSupport
import spray.http._
import spray.util._
object SprayClientApp extends App {
implicit val system = ActorSystem()
val ioBridge = IOExtension(system).ioBridge()
val httpClient = system.actorOf(Props(new HttpClient(ioBridge)))
val conduit = system.actorOf(
props = Props(new HttpConduit(httpClient, "maps.googleapis.com", 80)),
name = "http-conduit")
val pipeline = HttpConduit.sendReceive(conduit)
val response: Future[HttpResponse] = pipeline(HttpRequest(method = GET, uri = "/maps/api/elevation/json?locations=27.988056,86.925278&sensor=false"))
response onComplete {
case Success(responseContent) =>
println("The elevation of Mt. Everest is: {} m" + responseContent)
shutdown()
case Failure(error) =>
println("Couldn't get elevation")
shutdown()
}
def shutdown(): Unit = {
system.shutdown()
}
}
Let's take a look at the the Example from official website
First of all, the json Marshall things.
package com.sillycat.easyspray.sprayclient.version11m7
import spray.json.{JsonFormat, DefaultJsonProtocol}
case class Elevation(location: Location, elevation: Double)
case class Location(lat: Double, lng: Double)
case class GoogleApiResult[T](status: String, results: List[T])
object ElevationJsonProtocol extends DefaultJsonProtocol {
implicit val locationFormat = jsonFormat2(Location)
implicit val elevationFormat = jsonFormat2(Elevation)
implicit def googleApiResultFormat[T :JsonFormat] = jsonFormat2(GoogleApiResult.apply[T])
}
Here is the main part.
package com.sillycat.easyspray.sprayclient.version11m7
import scala.util.{Success, Failure}
import akka.actor.{Props, ActorSystem}
import spray.can.client.DefaultHttpClient
import spray.client.HttpConduit
import spray.httpx.SprayJsonSupport
import spray.http._
import spray.util._
object Main extends App {
// we need an ActorSystem to host our application in
implicit val system = ActorSystem("simple-spray-client")
import system.log
// create and start the default spray-can HttpClient
val httpClient = DefaultHttpClient(system)
startExample1()
def startExample1() {
log.info("Getting http://github.com ...")
// an HttpConduit gives us access to an HTTP server,
// it manages a pool of connections to _one_ host/port combination
// Get a Conduit
val conduit = system.actorOf(
props = Props(new HttpConduit(httpClient, "github.com")),
name = "http-conduit-1"
)
// send a simple request
val pipeline = HttpConduit.sendReceive(conduit)
val responseFuture = pipeline(HttpRequest(method = HttpMethods.GET, uri = "/"))
responseFuture onComplete {
case Success(response) =>
log.info(
"""|Response for GET request to github.com:
|status : {}
|headers: {}
|body : {}""".stripMargin,
response.status.value, response.headers.mkString("\n ", "\n ", ""), response.entity.asString
)
system.stop(conduit) // the conduit can be stopped when all operations on it have been completed
startExample2()
case Failure(error) =>
log.error(error, "Couldn't get http://github.com")
system.shutdown() // also stops all conduits (since they are actors)
}
}
//Build the Second conduit
def startExample2() {
log.info("Requesting the elevation of Mt. Everest from Googles Elevation API...")
val conduit = system.actorOf(
props = Props(new HttpConduit(httpClient, "maps.googleapis.com")),
name = "http-conduit-2"
)
import HttpConduit._
import ElevationJsonProtocol._
import SprayJsonSupport._
//send the request and unmarshal
val pipeline = sendReceive(conduit) ~> unmarshal[GoogleApiResult[Elevation]]
val responseF = pipeline(Get("/maps/api/elevation/json?locations=27.988056,86.925278&sensor=false"))
responseF onComplete {
case Success(response) =>
log.info("The elevation of Mt. Everest is: {} m", response.results.head.elevation)
system.shutdown() // also stops all conduits (since they are actors)
case Failure(error) =>
log.error(error, "Couldn't get elevation")
system.shutdown() // also stops all conduits (since they are actors)
}
}
}
Some other magic Not in the Example
case class Order(id: Int)
case class OrderConfirmation(id: Int)
object MyJsonProtocol extends DefaultJsonProtocol {
implicit val orderFormat = jsonFormat1(Order)
implicit val orderConfirmationFormat = jsonFormat1(OrderConfirmation)
}
import HttpConduit._
import MyJsonProtocol._
val pipeline: HttpRequest => Future[OrderConfirmation] = (
addHeader("X-My-Special-Header", "fancy-value")
~> addCredentials(BasicHttpCredentials("bob", "secret"))
~> encode(Gzip)
~> sendReceive(conduit)
~> decode(Deflate)
~> unmarshal[OrderConfirmation]
)
val confirmation: Future[OrderConfirmation] =
pipeline(Post("/orders", Order(42))
Till now, I got everything from the document. I plan to integrate that in the project.
Tips:
References:
spray(12)spray-can - Http Client
http://sillycat.iteye.com/blog/1882919
Http Dialog
http://spray.io/documentation/spray-can/http-dialog/