Akka Http Server - How to have route respond with byte array binary response - scala

Using the high level routing api,
How does one complete() a request within a route using a byte array binary response,
For example:
complete(HttpResponse(entity = HttpEntity(MediaTypes.`application/octet-stream`, byteArray)
?
Thank you

Use the Strict entity type:
import akka.util.ByteString
import akka.http.scaladsl.model.{HttpResponse, MediaTypes,HttpEntity}
val byteArray : Array[Byte] = ???
val body = ByteString(byteArray)
val entity = HttpEntity.Strict(MediaTypes.`application/octet-stream`, body)
val httpResponse = HttpResponse(entity = entity)
complete(httpResponse)

Related

How do you parse string from arraybuffer to double using Scala?

I'm trying to map string to double from an ArrayBuffer that I parsed through Playframework but I keep getting the following error:
Exception in thread "main" java.lang.NumberFormatException: For input string: ""0.04245800""
I'm not sure why it's doing this and I'm new to Scala coming from Python background.
import org.apache.http.client.methods.HttpGet
import play.api.libs.json._
import org.apache.http.impl.client.DefaultHttpClient
object main extends App {
val url = "https://api.binance.com/api/v1/aggTrades?symbol=ETHBTC"
val httpClient = new DefaultHttpClient()
val httpResponse = httpClient.execute(new HttpGet(url))
val entity = httpResponse.getEntity()
val content = ""
if (entity !=null) {
val inputStream = entity.getContent()
val result = io.Source.fromInputStream(inputStream).getLines.mkString
inputStream.close
println("REST API: " + url)
val json: JsValue = Json.parse(result)
var prices = (json\\"p")
println(prices.map(_.toString()).map(_.toDouble))
}
}
If you know for sure your list contains only strings you can cast them like this, and use the 'original' value to get the Double value from:
println(prices.map(_.as[JsString].value.toDouble))
As JsString is not a String you cannot call toDouble on that.
Just for completeness: If you are not certain your list contains only strings you should add an instanceof check or pattern matching.

Complete akka-http response with an iterator

I have an iterator of mongodb query results and I want to stream those results to http response without loading the whole results set into memory.
Is it possible to complete akka http response with an iterator instead of a collection or future?
Given an Iterator of data:
type Data = ???
val dataIterator : () => Iterator[Data] = ???
You will first need a function to convert Data to ByteString representation, and the ContentType (e.g. json, binary, csv, xml, ...) of the representation:
import akka.util.ByteString
import akka.http.scaladsl.model.ContentType
val dataToByteStr : Data => ByteString = ???
//see akka.http.scaladsl.model.ContentTypes for possible values
val contentType : ContentType = ???
The Iterator and converter function can now be used to create an HttpResponse that will stream the results back to the http client without holding the entire set of Data in memory:
import akka.http.scaladsl.model.HttpEntity.{Chunked, ChunkStreamPart}
import akka.http.scaladsl.model.ResponseEntity
import akka.stream.scaladsl.Source
import akka.http.scaladsl.model.HttpResponse
val chunks : Source[ChunkStreamPart,_] =
Source.fromIterator(dataIterator)
.map(dataToByteStr)
.map(ChunkStreamPart.apply)
val entity : ResponseEntity = Chunked.fromData(contentType, chunks)
val httpResponse : HttpResponse = HttpResponse(entity=entity)
Note: Since a new Iterator is produced each time from dataIterator you don't have to create a new httpResponse for each incoming request; the same response can be used for all requests.
Take a look to Alpakka MongoDB connector. It allows to create one Source from a Mongo collection like:
val source: Source[Document, NotUsed] = MongoSource(numbersColl.find())
val rows: Future[Seq[Document]] = source.runWith(Sink.seq)
Or you maybe want your own source implementation as a GraphStage for example.

How I can make a request with params from finagle client?

I’m getting started with a Finagle server (twitter/finagle):
import com.twitter.finagle.{Http, Service}
import com.twitter.util.{Await, Future}
import java.net.InetSocketAddress
import org.jboss.netty.handler.codec.http._
object Server extends App {
val service = new Service[HttpRequest, HttpResponse] {
def apply(req: HttpRequest): Future[HttpResponse] =
Future.value(new DefaultHttpResponse(
req.getProtocolVersion, HttpResponseStatus.OK))
}
val server = Http.serve(":8080", service)
Await.ready(server)
}
Client (twitter/finagle):
import com.twitter.finagle.{Http, Service}
import com.twitter.util.{Await, Future}
import java.net.InetSocketAddress
import org.jboss.netty.handler.codec.http._
object Client extends App {
val client: Service[HttpRequest, HttpResponse] =
Http.newService("localhost:8080")
val request = new DefaultHttpRequest(
HttpVersion.HTTP_1_1, HttpMethod.GET, "/")
val response: Future[HttpResponse] = client(request)
response onSuccess { resp: HttpResponse =>
println("GET success: " + resp)
}
Await.ready(response)
}
How do I send data like Map("data_id" -> 5) from the client to the server? And where in the server do I receive it? Do I have to add a callback to the server?
I haven’t found it by searching. If you can give me a link with an example, that will be enough.
Finagle is a very thin library. That means that you'll have to handle most of the "magic" by yourself.
To make the request with parameters from the Client, I use these helper methods:
def buildUri(base: String, path: String, params: Map[String, String] = Map.empty): String = {
val p = if (params.isEmpty) ""
else params map { case (k,v) => urlEncode(k) + "=" + urlEncode(v) } mkString ("?", "&", "")
base + path + p
}
def urlEncode(url: String): String = URLEncoder.encode(url, "UTF-8")
And then I call it like this:
val url = buildUri(baseAddress, path, defaultParams ++ params)
val req = RequestBuilder().url(url).setHeader("Accept", "*/*").buildGet
client(req)
As for the server you have to do basically the same thing and parse the parameters by hand. Either using java.net.URI or even org.jboss.netty.handler.codec.http.QueryStringDecoder.
Of course you can also use URI and QueryStringEncoder to encode as well, instead of using my helper methods.
That said, if you want to do that on higher level, you can use one of these libraries above Finagle:
https://github.com/fwbrasil/zoot
http://finatra.info/ (this is for the server part only)

Spray Unzip HttpResponse

I'm using Spray API(spray-client) to hit an external URL and I'm getting gzipped HttpResponse. How do I unzip this HttpResponse to get its entity(json, in my case)?
val future: Future[HttpResponse] = (IO(Http) ? Get(uri)).mapTo[HttpResponse]
val response = Await.result(future, Duration.inf)
val json = response.entity
Here, json is gzipped. How do I unzip it?
You need to use pipelining and the decode directive. Like in this example.
Modifying that example your code would look something like this:
val pipeline: HttpRequest => Future[String] = (
sendReceive
~> decode(Gzip)
~> unmarshal[String]
)
val response: Future[String] =
pipeline(Get(uri))
You can then do Await on the response if you don't want the benefits of Futures.
On a side note you can use spray-json and create an object for your response and then unmarshal the http response directly into a case class without having to deal with the json.

Setting a cookie for HTTP POST in Scala with Dispatch

I can't seem to set a cookie using Dispatch. The server resends a new session ID implying that the one I tried to send didn't get sent in the right way. Here is the code:
val domain = "myhost.com"
val host_req = host(domain).secure
val request = host_req / "path" / "path"
def post = request << Map("key" -> "SomeValue")
val response: Either[Throwable, Map[String, String]] =
Http(post OK asHeaders).either()
//The cookie comes down in the form "Set-Cookie: SESSIONID=<somesession>; Path=/path/; HttpOnly"
//successfully retrieves the session id...
val sessionId = getSessionId(response)
println(sessionId)
val sessionCookie = new com.ning.http.client.Cookie(domain, "SESSIONID", sessionId, "/path/", -1, true)
request.POST.addCookie(sessionCookie)
def establishPost = request << Map("key" -> "SomeValue")
establishPost.addCookie(sessionCookie)
val establishResponse: Either[Throwable, Map[String, String]] =
Http(establishPost OK asHeaders).either()
//Server sends down new SESSIONID...
//sessionId != getSEssionId(establishPost)
This is using the newest version of Dispatch. I'm trying to learn Scala as I go, and the only thing I can't figure out how to do either is inspect the establishPost object for its headers before it is sent as a request.
This should be better:
def reqWithParams = request << Map("key" -> "SomeValue")
val reqWithCookies = reqWithParams.addCookie(sessionCookie)
addCookie method returns the new object (the one with a cookie), but your code didn't use it.