Set Finatra HTTP request timeout - scala

Is it possible to set HTTP request-response timeout in a Finatra server?
The http controller callback typically returns a Future, that once resolved the response is transmitted. I would like to define, within Finatra, how long the server should wait before returning a 500 or 400 response.

You can extend the HttpServer and define your own timeout
trait CustomServer extends HttpServer with Tls {
then you overwrite the configureHttpServer method and you define timeout, requests sites and other attributes
override def configureHttpServer(server: Http.Server): Http.Server = {
server.withAdmissionControl.concurrencyLimit(maxConcurrentRequests = 2000, maxWaiters = 0)
.withResponseClassifier(HttpResponseClassifier.ServerErrorsAsFailures)
.withMaxRequestSize(StorageUnit.fromMegabytes(200))
.withRequestTimeout(50.seconds)
}

I think, you are looking for Future.within

Related

Is there a way to timeout functions inside mapAsync for akka?

I am trying to do asynchronous http calls with akka streams.
This is what I tried.
Source(listEndpoints)
.mapAsync(20)(endpoint => Future(Await.result(request(HttpMethods.POST, endpoint, List(authHeader)), timeout)))
.runWith(Sink.seq[HttpResponse])
I am using akka-http within the request method and it returns Future[HttpResponse]
I think I am abusing Future here. The code above would give me a Future[List[HttpResponse]] and I have to use Await again to get a List[HttpResponse]. Is there a more elegant way to timeout functions within mapAsync?
Assuming your request method at some point does
Http().singleRequest
to get a Future[HttpResponse], you can pass a timeout for the request through:
// inside def request(...), will probably need to add a timeout argument here
val request = ??? // Build the HttpRequest
Http().singleRequest(
request = request,
settings = ConnectionPoolSettings.default.withMaxConnectionLifetime(timeout)
Then your stream would just be
Source(listEndpoints)
.mapAsync(request(...))
.runWith(Sink.seq[HttpResponse])
and you'd only need to Await at the "end of the world" for the Future[List[HttpResponse]] to complete.
You can also change the default max connection lifetime with akka.http.host-connection-pool.max-connection-lifetime in application.conf

spray.io client configuration

I have a basic client which I use to test my server. For the configuration I am using application.json
"spray": {
"can": {
"client": {
"idle-timeout": "120 s",
"request-timeout": "180 s"
},
"host-connector": {
"max-retries": "1",
"max-connections": "64"
}
}
}
however in the sendrecieve method i see that the timeout is always 60 sec , as according to the documantation , if I use request-timeout it suppose to be the implicit value
def sendReceive(implicit refFactory: ActorRefFactory, executionContext: ExecutionContext,
futureTimeout: Timeout = 60.seconds): SendReceive =
sendReceive(IO(Http)(actorSystem))
Do I need explicitly to load the configuration?
This is a confusing aspect of spary's various timeout values, for a detailed explanation see: Understanding Spray Client Timeout Settings
A couple of points about the method definition above, the timeout is just used to satisfy the timeout required by the ask made to the transport actor, it does not relate to a request timeout for this connection. futureTimeout: Timeout = 60.seconds means that this default value of is used if none is provided, not that it is unconditionally used.
You can programmatically configure the requestTimeout by passing a HostConnectorSetup to either the host or request level API's, as you already have this in your spray.can.client configuration though you should not need to make further changes.

Spray.io log leaks sensitive information

I'm using Spray client to consume a third-party API. Unfortunately, the API I'm consuming is not very secure and utilizes an authentication method using GET query parameters.
Sometimes we're getting timeouts or connection issues which we know to deal with applicatively. The problem is that Spray logs this at a WARN log-level, and the URL including the sensitive query parameters () are being written in our log files.
Here's an example of the log file.
2015-05-19 12:23:17,024 WARN HttpHostConnectionSlot - Connection attempt to 10.10.10.10:443 failed in response to GET request to /api/?type=keygen&user=test_user&password=S3kret! with 2 retries left, retrying...
2015-05-19 12:23:17,084 WARN HttpHostConnectionSlot - Connection attempt to 10.10.10.10:443 failed in response to GET request to /api/?type=keygen&user=test_user&password=S3kret! with 1 retries left, retrying...
Is there any way to filter this? (Maybe in Akka?)
Spray reuses akka-logging for doing all logging groundwork.
In akka you can redeclare a custom event logger in application config:
akka {
# event-handlers = ["akka.event.Logging$DefaultLogger"] // default one
event-handlers = ["com.example.PrivacyLogger"] // custom one
# Options: ERROR, WARNING, INFO, DEBUG
loglevel = "DEBUG"
}
It may look like this:
class PrivacyLogger extends DefaultLogger {
override def receive: Receive = {
case InitializeLogger(_) ⇒ sender() ! LoggerInitialized
case event: LogEvent ⇒ print(stripSecret(event))
}
private def stripSecret(event:LogEvent) = ...
}
But you always can implement your own message processing logic here instead of simple printing.
PS. If you use slf4j for logging, the solution will mostly look the same, but with some minor differences like overriding akka.event.slf4j.Slf4jEventHandler instead of DefaultLogger.

Disable http in finatra app

I am deploying a Finatra app to Heroku. Thanks to Twitter guys together with Heroku this is a very easy task. The thing is that Heorku gives you https out of the box (if im trying to reach my service through https it just works). Nevertheless it also works with http requests. Is there any way to disable http requests and leave only https?
Thanks
You can disable the http request by override the defaultHttpPort value to an empty String (and do not pass a value for the -http.port flag)
import com.twitter.finagle.Http
import com.twitter.finatra.http.HttpServer
import com.twitter.finatra.http.routing.HttpRouter
object ExampleHttpsServerMain extends ExampleHttpsServer
class ExampleHttpsServer
extends HttpServer
with Tls {
override val defaultHttpPort: String = "" // disable the default HTTP port
override val defaultHttpsPort: String = ":443"
override def configureHttp(router: HttpRouter): Unit = {
router
.add[ExampleController]
}
}

Play framework make http request from play server to "somesite.com" and send the response back to the browser

I'm developing an application using Play framework in scala. I have to handle the below use case in my application.
For a particular request from the browser to the play server the Play server should make an http request to some external server (for Eg: somesite.com) and send the response from this request back to the web browser.
I have written the below code to send the request to external serever in the controller.
val holder = WS.url("http://somesite.com")
val futureResponse = holder.get
Now how do I send back the response recieved from "somesite.com" back to the browser?
There's an example in the Play documentation for WS, under Using in a controller; I've adapted it to your scenario:
def showSomeSiteContent = Action.async {
WS.url("http://somesite.com").get().map { response =>
Ok(response.body)
}
}
The key thing to note is the idiomatic use of map() on the Future that you get back from the get call - code inside this map block will be executed once the Future has completed successfully.
The Action.async "wrapper" tells the Play framework that you'll be returning a Future[Response] and that you want it to do the necessary waiting for things to happen, as explained in the Handling Asynchronous Results documentation.
You may also be interested in dynamically returning the status and content type:
def showSomeSiteContent = Action.async {
WS.url("http://somesite.com").get().map { response =>
Status(response.status)(response.body).as(response.ahcResponse.getContentType)
}
}
Dynamic status could help if the URL/service you call fails to answer correctly.
Dynamic content type can be handy if your URL/service can return different content HTML/XML... depending on some dynamic parameter.