Allowing illegal requests to webserver - scala

I am making a akka webserver, and running into an issue. When receiving a request that contains a % character, I get the following error: Illegal request, responding with status '400 Bad Request': Illegal request-target: Invalid input '%', expected HEXDIG (line 1, column 12): /path?val=%%test%%. While I understand that %% is a bad encoding, I would like to log and process the request, since I do receive them. Is there anyway to stop the auto 400 response, and receive the data as received? Here is a minimal working example of my program:
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.HttpMethods._
import akka.http.scaladsl.model.{ ContentTypes, HttpEntity }
import akka.http.scaladsl.server.Directives._
import scala.io.StdIn
import akka.http.scaladsl.server._
import akka.actor.Props
import akka.http.scaladsl.model._
import akka.http.scaladsl.model.HttpMethods._
import akka.stream.scaladsl.Sink
import akka.stream.scaladsl.Flow
import Directives._
object Main extends App {
implicit def myRejectionHandler =
RejectionHandler.newBuilder()
.handle {
case _: IllegalUriException =>
println("Illegal URI rejection")
complete(HttpResponse(200))
}
.handleAll[MethodRejection] { methodRejections =>
println("Handle all")
complete(HttpResponse(200))
}
.result()
implicit def myExceptionHandler: ExceptionHandler =
ExceptionHandler {
case _: IllegalUriException =>
println ("Illegal URI Exception")
complete(HttpResponse(200))
}
implicit val system = ActorSystem()
implicit val executionContext = system.dispatcher
val route = {
handleExceptions(myExceptionHandler) {
get {
path("path") {
complete("path!")
}
}
}
}
val bindingFuture = Http().newServerAt("0.0.0.0", 80).bind(route)
println(s"Server online at http://localhost:80/\nPress RETURN to stop...")
StdIn.readLine()
bindingFuture
.flatMap(_.unbind())
.onComplete(_ => system.terminate())
}
I also did attempt to set
akka.http {
parsing {
uri-parsing-mode = relaxed
}
}
But did not solve this issue

Related

Eventually didn't tolerate unsuccessful attempts before giving up

I have a simple test case which is using TestActorRef and eventually to verify a timeout call of a method. Here is details of 3 file sources:
TestRestUtility.scala
import play.api.http.HttpVerbs
import play.api.libs.ws.WSClient
import javax.inject.Inject
import scala.concurrent.Future
class TestRestUtility #Inject()(ws: WSClient) extends HttpVerbs {
import scala.concurrent.ExecutionContext.Implicits.global
def getHealthStatus(): Future[Int] = {
ws.url("https://www.google.com").get().map { response =>
response.status
}
}
}
TestHealthCheckActor.scala
import akka.actor.{Actor, ActorLogging, Props}
import play.api.http.Status
import javax.inject.Inject
import scala.concurrent.Future
import scala.concurrent.duration.{DurationInt, FiniteDuration}
import scala.util.{Failure, Success}
object TestHealthCheckActor {
def props(testRestUtility: TestRestUtility): Props = {
Props(new TestHealthCheckActor(testRestUtility: TestRestUtility))
}
}
class TestHealthCheckActor #Inject()(testRestUtility: TestRestUtility)
extends Actor with ActorLogging with Status {
import context.dispatcher
val checkPeriod: FiniteDuration = 1.seconds
var apiStatus: Int = _
override def preStart(): Unit = {
context.system.scheduler.scheduleWithFixedDelay(
0.milliseconds,
checkPeriod,
self,
RefreshHealthStatus
)
}
override def receive: Receive = {
case RefreshHealthStatus =>
val health: Future[Int] = testRestUtility.getHealthStatus()
health.onComplete({
case Success(result) =>
result match {
case OK => apiStatus = Status.OK
case _ => apiStatus = Status.REQUEST_TIMEOUT
}
case Failure(e) =>
println(e)
})
}
}
TestSpec.scala
import akka.actor.{ActorSystem, PoisonPill}
import akka.testkit.{ImplicitSender, TestActorRef, TestKit}
import org.mockito.Mockito
import org.mockito.Mockito.{times, verify, when}
import org.scalatest
import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach}
import org.scalatest.concurrent.Eventually
import org.scalatest.matchers.should.Matchers
import org.scalatest.time.Span
import org.scalatest.wordspec.AnyWordSpecLike
import org.scalatestplus.mockito.MockitoSugar
import play.api.http.Status
import scala.concurrent.Future
class TestSpec extends TestKit(ActorSystem("HealthCheckActorSpec")) with AnyWordSpecLike with Matchers
with BeforeAndAfterEach with MockitoSugar with ImplicitSender with Eventually with BeforeAndAfterAll with Status with TestHarnessConstants {
import scala.concurrent.ExecutionContext.Implicits.global
var mockTestRestUtility: TestRestUtility = _
"HealthCheckActor" should {
"time out and call subscription api" in {
mockTestRestUtility = mock[TestRestUtility]
when(mockTestRestUtility.getHealthStatus()).thenReturn(Future(OK)).thenReturn(Future(OK)).thenReturn(Future(OK))
val healthCheckActorShortPeriod: TestActorRef[TestHealthCheckActor] = TestActorRef(TestHealthCheckActor.props(mockTestRestUtility))
eventually(timeout(Span(9, scalatest.time.Seconds)), interval(Span(1, scalatest.time.Seconds))) {
verify(mockTestRestUtility, Mockito.atLeast(3)).getHealthStatus()
}
healthCheckActorShortPeriod ! PoisonPill
}
}
}
As description about eventually in "scalatest-core_2.12-3.2.3-sources.jar", it tolerates unsuccessful attempts before giving up, so the test case is expected to have 3 calls the method getHealthStatus() to be successful as the returned value from mock. But I got a failed test case with below error message. I don't know why the method was called only one time:
testRestUtility.getHealthStatus();
Wanted *at least* 3 times:
-> at com.deere.isg.ingest.supporttool.testharness.TestSpec.$anonfun$new$8(TestSpec.scala:44)
But was 1 time:
-> at com.deere.isg.ingest.supporttool.testharness.TestHealthCheckActor$$anonfun$receive$1.applyOrElse(TestHealthCheckActor.scala:36)
org.mockito.exceptions.verification.TooFewActualInvocations:
testRestUtility.getHealthStatus();
Wanted *at least* 3 times:
-> at com.deere.isg.ingest.supporttool.testharness.TestSpec.$anonfun$new$8(TestSpec.scala:44)
But was 1 time:
-> at com.deere.isg.ingest.supporttool.testharness.TestHealthCheckActor$$anonfun$receive$1.applyOrElse(TestHealthCheckActor.scala:36)
Future(OK) will be considered successful by eventually because it doesn't throw an exception (technically neither would a failed Future, unless you unwrapped it in the eventually). Since the first call succeeded, eventually won't make any attempts after the first.

Stuck in infinite loop with simple akka-http route test with actors

I have a simple example where I have a route that invokes an actor, however it seems to get stuck in an infinite loop and the http response never comes. I am using akka-actor version 2.6.15 and akka-http version 10.2.4. Here is the sample code, any help is appreciated.
package test
import akka.actor.{Actor, ActorRef, Props}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.{Route, _}
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
import akka.pattern.ask
import akka.util.Timeout
import org.scalatest.Matchers
import org.scalatest.wordspec.AnyWordSpec
import scala.concurrent.duration.DurationInt
case class TestMessage()
class TestActor extends Actor {
def receive: Receive = {
case _ => "response"
}
}
class AkkaHttpTest extends AnyWordSpec with Matchers with ScalatestRouteTest {
val testActor: ActorRef = system.actorOf(Props(new TestActor()), name = "TestActor")
implicit val timeout: Timeout = 15.seconds
implicit val defaultTimeout = RouteTestTimeout(15.seconds)
val route: Route = {
get {
pathSingleSlash {
complete((testActor ? TestMessage()).mapTo[String])
}
}
}
"Test" should {
"Return text" in {
Get() ~> route ~> check {
println(responseAs[String])
}
}
}
}
To reply to a message in Akka, you have to explicitly send the reply.
In your example:
def receive: Receive = {
case _ =>
sender ! "response"
}

Get the chosen actor in a RoundRobinPool?

If I had a RoundRobinPool like this
val actorPoolRef = AkkaConfig.actorSystem.actorOf(RoundRobinPool(100).props(Props[MyService]))
and a handler
def requestHandler(request: HttpRequest): Future[HttpResponse] = {
val promise = Promise[HttpResponse]()
promise.completeWith(actorPoolRef ? request)
promise.future
}
Is there any way I can
get the exact actor reference from the scope of def requestHandler, or
send a follow-up message to the same actor that just handled the request
you can do by using the akka.pattern.ask to request the actor reference from a RoundRobinPool of actors, returning the self.path as an Option and wrapping the response as Option[ActorPath]. To clarify what I am saying I build this simple proof of concept:
import akka.actor.{Actor, ActorLogging, ActorPath, ActorSystem, Props}
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import akka.pattern.ask
import akka.routing.RoundRobinPool
import akka.util.Timeout
import scala.concurrent.Future
import scala.concurrent.duration._
object BasicRoundRobinHttpServer {
def main(args: Array[String]): Unit = {
run()
}
def run() = {
implicit val system = ActorSystem("BasicRoundRobinHttpServer")
import system.dispatcher
implicit val timeout = Timeout(3 seconds)
val myServiceActor = system.actorOf(RoundRobinPool(5).props(Props[MyService]), "myServiceActor")
val simpleRoute: Route =
(path("reference") & get) {
val validResponseFuture: Option[Future[HttpResponse]] = {
// construct the HTTP response
val actorPathResponse: Future[Option[ActorPath]] = (myServiceActor ? "reference").mapTo[Option[ActorPath]]
Option(actorPathResponse.map(ref => HttpResponse(
StatusCodes.OK,
entity = HttpEntity(
ContentTypes.`text/html(UTF-8)`,
s"""
|<html>
| <body>I got the actor reference: ${ref} </body>
|</html>
|""".stripMargin
))))
}
val entityFuture: Future[HttpResponse] = validResponseFuture.getOrElse(Future(HttpResponse(StatusCodes.BadRequest)))
complete(entityFuture)
}
println("http GET localhost:8080/reference")
Http().newServerAt("localhost", 8080).bind(simpleRoute)
}
}
class MyService extends Actor with ActorLogging {
override def receive: Receive = {
case "reference" =>
log.info(s"request reference at actor: ${self}")
sender() ! Option(self.path)
case message =>
log.info(s"unknown message: ${message.toString}")
}
}
requesting the address $ http GET localhost:8080/reference from the browser or using any HTTP requester several times you get actor reference $a, $b, etc...
// first time
<html>
<body>I got the actor reference: Some(akka://BasicRoundRobinHttpServer/user/myServiceActor/$a) </body>
</html>
// second time
<html>
<body>I got the actor reference: Some(akka://BasicRoundRobinHttpServer/user/myServiceActor/$b) </body>
</html>
...

how to mock external WS API calls in Scala Play framework

I have an existing Scala play application which has a REST API that calls another external REST API. I want to mock the external Web service returning fake JSON data for internal tests. Based on example from: https://www.playframework.com/documentation/2.6.x/ScalaTestingWebServiceClients
I followed example exactly as in Documentation and I'm getting compiler errors due to deprecated class Action.
import play.core.server.Server
import play.api.routing.sird._
import play.api.mvc._
import play.api.libs.json._
import play.api.test._
import scala.concurrent.Await
import scala.concurrent.duration._
import org.specs2.mutable.Specification
import product.services.market.common.GitHubClient
class GitHubClientSpec extends Specification {
import scala.concurrent.ExecutionContext.Implicits.global
"GitHubClient" should {
"get all repositories" in {
Server.withRouter() {
case GET(p"/repositories") => Action {
Results.Ok(Json.arr(Json.obj("full_name" -> "octocat/Hello-World")))
}
} { implicit port =>
WsTestClient.withClient { client =>
val result = Await.result(
new GitHubClient(client, "").repositories(), 10.seconds)
result must_== Seq("octocat/Hello-World")
}
}
}
}
}
object Action in package mvc is deprecated: Inject an ActionBuilder
(e.g. DefaultActionBuilder) or extend
BaseController/AbstractController/InjectedController
And this is the primary example from latest official docs which in fact contains a compile time error, given this example doesn't work how should be the proper way to easily mock an external API using Scala Play?
You may change your example to:
Server.withRouterFromComponents() { cs => {
case GET(p"/repositories") => cs.defaultActionBuilder {
Results.Ok(Json.arr(Json.obj("full_name" -> "octocat/Hello-World")))
}
}
} { implicit port =>
WsTestClient.withClient { client =>
val result = Await.result(
new GitHubClient(client, "").repositories(), 10.seconds)
result should be(Seq("octocat/Hello-World"))
}
}
To be honest, I'm not 100% sure if this is the nicest way. However I have submitted a PR to the play framework so you might watch that space for comments from the makers.
If you're using standalone version of play-ws you can use this library https://github.com/f100ded/play-fake-ws-standalone like this
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import org.f100ded.play.fakews._
import org.scalatest._
import play.api.libs.ws.JsonBodyWritables._
import scala.concurrent.duration.Duration
import scala.concurrent._
import scala.language.reflectiveCalls
/**
* Tests MyApi HTTP client implementation
*/
class MyApiClientSpec extends AsyncFlatSpec with BeforeAndAfterAll with Matchers {
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
import system.dispatcher
behavior of "MyApiClient"
it should "put access token to Authorization header" in {
val accessToken = "fake_access_token"
val ws = StandaloneFakeWSClient {
case request # GET(url"http://host/v1/foo/$id") =>
// this is here just to demonstrate how you can use URL extractor
id shouldBe "1"
// verify access token
request.headers should contain ("Authorization" -> Seq(s"Bearer $accessToken"))
Ok(FakeAnswers.foo)
}
val api = new MyApiClient(ws, baseUrl = "http://host/", accessToken = accessToken)
api.getFoo(1).map(_ => succeed)
}
// ... more tests
override def afterAll(): Unit = {
Await.result(system.terminate(), Duration.Inf)
}
}

Scala compiler can't find the unmarshalling implicits in route declaration

I'm trying to build a REST server using this tutorial:
https://spindance.com/reactive-rest-services-akka-http/
However, having reached the "Responding with JSON" section, I've noticed that my code doesn't compile, and I can't make POST requests. This is the error that I'm getting:
Error:(59, 18) could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[Health]
entity(as[Health]) { statusReport =>
^
Having looked at other tutorials, I've found out that you need to include an object containing an implicit variable for the class that I'm trying to unmarshall. I did that, and I even imported the httpx library, but I'm still getting this error. My code is given below.
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import akka.stream.ActorMaterializer
import akka.pattern.ask
import akka.util.Timeout
import spray.json._
import DefaultJsonProtocol._
import spray.httpx.SprayJsonSupport.sprayJsonUnmarshaller
import scala.concurrent.duration._
import scala.io.StdIn
object JsonImplicits extends DefaultJsonProtocol {
implicit val healthFormat = jsonFormat2(Health)
}
object MyApplication {
val host = "localhost"
val port = 8080
def main(args: Array[String]): Unit = {
implicit val system = ActorSystem("simple-rest-system")
// Something to do with flows
implicit val materializer = ActorMaterializer()
// A reference to a specific thread pool
// You can configure thread pool options through it
// It is the engine that executes the actors
implicit val executionContext = system.dispatcher
val requestHandler = system.actorOf(RequestHandler.props(), "requestHandler")
//Define the route
val route : Route = {
implicit val timeout = Timeout(20 seconds)
import JsonImplicits._
import spray.httpx.SprayJsonSupport._
path("health") {
get {
onSuccess(requestHandler ? GetHealthRequest) {
case response: HealthResponse =>
complete(StatusCodes.OK, s"Everything is ${response.health.status}!")
case _ =>
complete(StatusCodes.InternalServerError)
}
}
} ~ post {
// Entity extracts the body of the POST request and then converts it into a
// Health object
entity(as[Health]) { statusReport =>
onSuccess(requestHandler ? SetStatusRequest(statusReport)) {
case response: HealthResponse =>
complete(StatusCodes.OK,s"Posted health as ${response.health.status}!")
case _ =>
complete(StatusCodes.InternalServerError)
}
}
}
}
//Start up and listen for requests
val bindingFuture = Http().bindAndHandle(route, host, port)
println(s"Waiting for requests at http://$host:$port/...\nHit RETURN to terminate")
StdIn.readLine()
//Shutdown
bindingFuture.flatMap(_.unbind())
system.terminate()
}
}