-
Notifications
You must be signed in to change notification settings - Fork 96
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
dec0db9
commit 13ccf34
Showing
40 changed files
with
1,143 additions
and
1,026 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
version = "2.0.1" | ||
version = "2.4.2" | ||
maxColumn = 140 | ||
align = most | ||
continuationIndent.defnSite = 2 | ||
|
86 changes: 69 additions & 17 deletions
86
common/src/main/scala/hmda/api/http/directives/HmdaTimeDirectives.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,78 @@ | ||
package hmda.api.http.directives | ||
|
||
import akka.event.LoggingAdapter | ||
import akka.http.scaladsl.server.Directive0 | ||
import akka.http.scaladsl.model.{ HttpRequest, HttpResponse, StatusCodes } | ||
import akka.http.scaladsl.server.{ Directive0, Route, RouteResult } | ||
import akka.http.scaladsl.server.Directives._ | ||
import akka.http.scaladsl.server.RouteResult.{ Complete, Rejected } | ||
import akka.stream.scaladsl.Flow | ||
import akka.util.ByteString | ||
import org.slf4j.{ Logger, LoggerFactory } | ||
|
||
trait HmdaTimeDirectives { | ||
import scala.concurrent.ExecutionContext | ||
import scala.util.{ Failure, Success, Try } | ||
|
||
val log: LoggingAdapter | ||
object HmdaTimeDirectives { | ||
def timed(route: Route)(implicit ec: ExecutionContext): Route = | ||
aroundRequest(timeRequest)(ec)(route) | ||
|
||
def timedGet = get & time & extractUri | ||
def timedPost = post & time & extractUri | ||
def timedPut = put & time & extractUri | ||
def timedDelete = delete & time & extractUri | ||
def timedOptions = options & time & extractUri | ||
private val log: Logger = LoggerFactory.getLogger(getClass) | ||
|
||
def time: Directive0 = { | ||
val startTime = System.currentTimeMillis() | ||
mapResponse { response => | ||
val endTime = System.currentTimeMillis() | ||
val responseTime = endTime - startTime | ||
log.debug(s"Request took $responseTime ms") | ||
response | ||
private val timeoutResponse = HttpResponse(StatusCodes.NetworkReadTimeout, entity = "Unable to serve response within time limit.") | ||
|
||
// Reference: https://blog.softwaremill.com/measuring-response-time-in-akka-http-7b6312ec70cf | ||
private def timeRequest(request: HttpRequest): Try[RouteResult] => Unit = { | ||
val start = System.currentTimeMillis() | ||
|
||
{ | ||
case Success(Complete(resp)) => | ||
val end = System.currentTimeMillis() | ||
val responseTime = end - start | ||
log.info(s"[${resp.status.intValue()}] ${request.method.name} ${request.uri} took: $responseTime ms") | ||
|
||
case Success(Rejected(_)) => | ||
log.debug("Request was rejected, not timing it") | ||
|
||
case Failure(_) => | ||
log.debug("Request failed, not timing it") | ||
} | ||
} | ||
} | ||
|
||
// Reference: https://blog.softwaremill.com/measuring-response-time-in-akka-http-7b6312ec70cf | ||
private def aroundRequest(onRequest: HttpRequest => Try[RouteResult] => Unit)(implicit ec: ExecutionContext): Directive0 = | ||
extractRequestContext.flatMap { ctx => | ||
val onDone = onRequest(ctx.request) | ||
mapInnerRoute { inner => | ||
withRequestTimeoutResponse { _ => | ||
onDone(Success(Complete(timeoutResponse))) | ||
timeoutResponse | ||
} { | ||
inner.andThen { resultFuture => | ||
resultFuture.map { | ||
case c @ Complete(response) => | ||
Complete(response.mapEntity { entity => | ||
if (entity.isKnownEmpty()) { | ||
onDone(Success(c)) | ||
entity | ||
} else { | ||
// On an empty entity, `transformDataBytes` unsets `isKnownEmpty`. | ||
// Call onDone right away, since there's no significant amount of | ||
// data to send, anyway. | ||
entity.transformDataBytes(Flow[ByteString].watchTermination() { | ||
case (m, f) => | ||
f.map(_ => c).onComplete(onDone) | ||
m | ||
}) | ||
} | ||
}) | ||
case other => | ||
onDone(Success(other)) | ||
other | ||
}.andThen { // skip this if you use akka.http.scaladsl.server.handleExceptions, put onDone there | ||
case Failure(ex) => | ||
onDone(Failure(ex)) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.