How to implement Akka HTTP Request Post Client Headers Authorization - scala

I'am trying to implement Akka http client for post request where Authorization will be given in header from postman. I am not able to authorize, following is my code
def main(args: Array[String]) {
implicit val system: ActorSystem = ActorSystem()
implicit val materializer: ActorMaterializer = ActorMaterializer()
implicit val executionContext: ExecutionContextExecutor = system.dispatcher
val requestHeader = scala.collection.immutable.Seq(RawHeader("Authorization", "admin"))
val requestHandler: HttpRequest => HttpResponse = {
case HttpRequest(POST, Uri.Path("/GetTrackerData"), requestHeader, entity, _) =>
val chunk = Unmarshal(entity).to[DeviceLocationData]
val deviceLocationData = Await.result(chunk, 1.second)
val responseArray = "Authorized"
HttpResponse(entity = HttpEntity(ContentTypes.`application/json`, responseArray)
)
case r: HttpRequest =>
println(r.uri.toString())
r.discardEntityBytes() // important to drain incoming HTTP Entity stream
HttpResponse(404, entity = "Unknown resource!")
}
val bindingFuture = Http().bindAndHandleSync(requestHandler, "0.0.0.0", 7070)
println(s"iot engine api live at 0.0.0.0:7070")
sys.addShutdownHook({
println("ShutdownHook called")
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ => system.terminate()) // and shutdown when done
})
}
Whatever value I give from postman. It serves the request. What I am skipping ?
My use case is that result showed be displayed only after authorization

You are pattern matching on HttpRequest.
The requestHeader you use there is not the one you specified earlier but will be the headers from the HttpRequest itself.
One way to resolve it could be checking for the values in the headers:
case HttpRequest(HttpMethods.POST, Uri.Path("/GetTrackerData"), headers, entity, _)
if (headers.exists(h => h.name == "Authorization" && h.value == "admin")) =>

Here is one problem:
case HttpRequest(POST, Uri.Path("/GetTrackerData"), requestHeader, entity, _) =>
This does not match the current value of requestHeader, it creates a new value requestHeader containing the current headers of the HttpRequest. So this match will not check the Authorization field of the header. You will have to do this manually.
More generally, it would be worth looking at the Route support in Akka HTTP as a cleaner and more powerful way of implementing a server.

Related

Scala and Akka HTTP: How to get the entity content as string

I am very new to Akka HTTP so please accept my apologies beforehand for the very elementary question.
In the following piece of code, I want to retrieve the entity from the HTTP request (entity will be plain text), get the text out of the entity, and return it as a response.
implicit val system = ActorSystem("ActorSystem")
implicit val materializer = ActorMaterializer
import system.dispatcher
val requestHandler: Flow[HttpRequest, HttpResponse, _] = Flow[HttpRequest].map {
case HttpRequest(HttpMethods.POST, Uri.Path("/api"), _, entity, _) =>
val entityAsText = ... // <- get entity content as text
HttpResponse(
StatusCodes.OK,
entity = HttpEntity(
ContentTypes.`text/plain(UTF-8)`,
entityAsText
)
)
}
Http().bindAndHandle(requestHandler, "localhost", 8080)
How can I get the string content of the entity?
Many thanks in advance!
One approach is to call toStrict on the RequestEntity, which loads the entity into memory, and mapAsync on the Flow:
import scala.concurrent.duration._
val requestHandler: Flow[HttpRequest, HttpResponse, _] = Flow[HttpRequest].mapAsync(1) {
case HttpRequest(HttpMethods.GET, Uri.Path("/api"), _, entity, _) =>
val entityAsText: Future[String] = entity.toStrict(1 second).map(_.data.utf8String)
entityAsText.map { text =>
HttpResponse(
StatusCodes.OK,
entity = HttpEntity(
ContentTypes.`text/plain(UTF-8)`,
text
)
)
}
}
Adjust the timeout on the former and the parallelism level on the latter as needed.
Another approach would be to use the already Unmarshaller which is in scope (most likely it will be the akka.http.scaladsl.unmarshalling.PredefinedFromEntityUnmarshallers#stringUnmarshaller):
val entityAsText: Future[String] = Unmarshal(entity).to[String]
This approach ensures the consistent usage of the provided Unmarshaller for String and you wouldn't have to cope with timeouts.

Scala and Akka HTTP: Request inside a request & issue with threads

I am new to learning Scala, Akka Streams and Akka HTTP, so apologies beforehand if the question is too basic.
I want to do an HTTP request inside an HTTP request, just like in the following piece of code:
implicit val system = ActorSystem("ActorSystem")
implicit val materializer = ActorMaterializer
import system.dispatcher
val requestHandler: Flow[HttpRequest, HttpResponse, _] = Flow[HttpRequest].map {
case HttpRequest(HttpMethods.GET, Uri.Path("/api"), _, _, _) =>
val responseFuture = Http().singleRequest(HttpRequest(uri = "http://www.google.com"))
responseFuture.onComplete {
case Success(response) =>
response.discardEntityBytes()
println(s"The request was successful")
case Failure(ex) =>
println(s"The request failed with: $ex")
}
//Await.result(responseFuture, 10 seconds)
println("Reached HttpResponse")
HttpResponse(
StatusCodes.OK
)
}
Http().bindAndHandle(requestHandler, "localhost", 8080)
But in the above case the result looks like this, meaning that Reached HttpResponse is reached first before completing the request:
Reached HttpResponse
The request was successful
I tried using Await.result(responseFuture, 10 seconds) (currently commented out) but it made no difference.
What am I missing here? Any help will be greatly appreciated!
Many thanks in advance!
map is a function that takes request and produces a response:
HttpRequest => HttpResponse
The challenge is that response is a type of Future. Therefore, you need a function that deals with it. The function that takes HttpRequest and returns Future of HttpResponse.
HttpRequest => Future[HttpResponse]
And voila, mapAsync is exactly what you need:
val requestHandler: Flow[HttpRequest, HttpResponse, _] = Flow[HttpRequest].mapAsync(2) {
case HttpRequest(HttpMethods.GET, Uri.Path("/api"), _, _, _) =>
Http().singleRequest(HttpRequest(uri = "http://www.google.com")).map (resp => {
resp.discardEntityBytes()
println(s"The request was successful")
HttpResponse(StatusCodes.OK)
})
}

Akka-http: connect to websocket on localhost

I am trying to connect to some server through websocket on localhost. When I try to do it in JS by
ws = new WebSocket('ws://localhost:8137');
it succeeds. However, when I use akka-http and akka-streams I get "connection failed" error.
object Transmitter {
implicit val system: ActorSystem = ActorSystem()
implicit val materializer: ActorMaterializer = ActorMaterializer()
import system.dispatcher
object Rec extends Actor {
override def receive: Receive = {
case TextMessage.Strict(msg) =>
Log.info("Recevied signal " + msg)
}
}
// val host = "ws://echo.websocket.org"
val host = "ws://localhost:8137"
val sink: Sink[Message, NotUsed] = Sink.actorRef[Message](system.actorOf(Props(Rec)), PoisonPill)
val source: Source[Message, NotUsed] = Source(List("test1", "test2") map (TextMessage(_)))
val flow: Flow[Message, Message, Future[WebSocketUpgradeResponse]] =
Http().webSocketClientFlow(WebSocketRequest(host))
val (upgradeResponse, closed) =
source
.viaMat(flow)(Keep.right) // keep the materialized Future[WebSocketUpgradeResponse]
.toMat(sink)(Keep.both) // also keep the Future[Done]
.run()
val connected: Future[Done.type] = upgradeResponse.flatMap { upgrade =>
if (upgrade.response.status == StatusCodes.SwitchingProtocols) {
Future.successful(Done)
} else {
Future.failed(new Exception(s"Connection failed: ${upgrade.response.status}")
}
}
def test(): Unit = {
connected.onComplete(Log.info)
}
}
It works completely OK with ws://echo.websocket.org.
I think attaching code of my server is reasonless, because it works with JavaScript client and problem is only with connection, however if you would like to look at it I may show it.
What am I doing wrong?
I have tested your client implementation with a websocket server from akka documentation,
and I did not get any connection error. Your websocket client connects successfully. That is why I am guessing the problem is with your server implementation.
object WebSocketServer extends App {
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
import Directives._
val greeterWebSocketService = Flow[Message].collect {
case tm: TextMessage => TextMessage(Source.single("Hello ") ++ tm.textStream)
}
val route =
get {
handleWebSocketMessages(greeterWebSocketService)
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8137)
println(s"Server online at http://localhost:8137/\nPress RETURN to stop...")
StdIn.readLine()
import system.dispatcher // for the future transformations
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ => system.terminate()) // and shutdown when done
}
By the way, I noticed that your actor's receive method does not cover all possible messages. According to that akka issue,
every message, even very small, can end up as Streamed. If you want to print all text messages a better implementation of the actor would be:
object Rec extends Actor {
override def receive: Receive = {
case TextMessage.Strict(text) ⇒ println(s"Received signal $text")
case TextMessage.Streamed(textStream) ⇒ textStream.runFold("")(_ + _).foreach(msg => println(s"Received streamed signal: $msg"))
}
}
Please find a working project on my github.
I found the solution: the server I used was running on IPv6 (as ::1), but akka-http treats localhost as 127.0.0.1 and ignores ::1. I had to rewrite server to force it to use IPv4 and it worked.

Echo simple HTTP server with Akka Http in Scala

I am developing a simple HTTP server using Akka-Http in Scala.
My code is as given below:
object HttpServer extends App {
override def main(args: Array[String]): Unit = {
implicit val system = ActorSystem("my-system")
implicit val materializer = ActorMaterializer()
implicit val executionContext = system.dispatcher
val route : Route = post {
path("echo") {
val json = ???
complete((StatusCodes.OK, json))
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
StdIn.readLine()
bindingFuture.flatMap(_.unbind())
port.onComplete(_ => system.terminate())
}
}
I do not know Scala enough yet. For that, I need some help.
I do not know how I can get JSON from Http POST body to give back that json to client.
You only need to add an extractor to your route definition:
val route : Route = post {
path("echo") {
entity(as[String]) { json =>
complete(json)
}
}
Note that you don't need to set the status code explicitly, as akka-http will automatically set status 200 OK for you when you pass a value to complete

How to make htttp request with akka stream for 10K request

I build server with akka-http and akka-stream but it lost some request in 2K+ request. What I miss something or my understand for akka is wrong.
this is my code
implicit val actorRef = ActorSystem("system", testConf)
implicit val materializer = ActorMaterializer(ActorMaterializerSettings(actorRef).withInputBuffer(initialSize = 4, maxSize = 16))
implicit val requestTimeout = Timeout(8 seconds)
def response(req: HttpRequest): Future[Response] = {
val connectionFlow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] =
Http().outgoingConnection(host = "url").async
for {
res <- Source.single(req.copy(uri = s"${req.uri.path}?${req.uri.rawQueryString.get}")).via(connectionFlow).runWith(Sink.head)
data <- res.entity.toStrict(5 second)
} yield (data.getData().decodeString("UTF-8"), res.status.intValue())
}
Thank you.
Most likely there were either timeout or server-side errors in first part of your for-comprehension therefore you got only successful responses in res.
I recommend to create flow/graph that processes your requests with withSupervisionStrategy in your materializer so you can see what exactly went wrong. Details of implementation depend on your business logic.