From b3b426046a199307ce80d01b604115890724cb22 Mon Sep 17 00:00:00 2001 From: Sergey Nastich Date: Mon, 10 Sep 2018 19:05:40 -0400 Subject: Add `Cache-Control: no-cache` to all responses of DriverRoutes. (#190) This change is intended to battle IE (even IE 11) caching all GET responses (and not making any new ones) that don't list those headers --- src/main/scala/xyz/driver/core/app/DriverApp.scala | 16 +++++++++++----- .../scala/xyz/driver/core/rest/DriverRoute.scala | 20 ++++++++++++++------ .../scala/xyz/driver/core/rest/DriverAppTest.scala | 12 +++++++++++- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/main/scala/xyz/driver/core/app/DriverApp.scala b/src/main/scala/xyz/driver/core/app/DriverApp.scala index 2572d85..9214cea 100644 --- a/src/main/scala/xyz/driver/core/app/DriverApp.scala +++ b/src/main/scala/xyz/driver/core/app/DriverApp.scala @@ -125,14 +125,20 @@ class DriverApp( def appRoute: Route = { val serviceTypes = modules.flatMap(_.routeTypes) val swaggerService = new Swagger(baseUrl, Scheme.forValue(scheme) :: Nil, version, serviceTypes, config, log) - val swaggerRoute = swaggerService.routes ~ swaggerService.swaggerUI - val versionRt = versionRoute(version, gitHash, time.currentTime()) + val swaggerRoute = new DriverRoute { + def log: Logger = self.log + def route: Route = handleExceptions(ExceptionHandler(exceptionHandler)) { + swaggerService.routes ~ swaggerService.swaggerUI + } + } + val versionRt = versionRoute(version, gitHash, time.currentTime()) val basicRoutes = new DriverRoute { - override def log: Logger = self.log - override def route: Route = versionRt ~ healthRoute ~ swaggerRoute + def log: Logger = self.log + def route: Route = versionRt ~ healthRoute } val combinedRoute = - Route.seal(modules.map(_.route).foldLeft(basicRoutes.routeWithDefaults)(_ ~ _) ~ defaultOptionsRoute) + Route.seal( + modules.map(_.route).foldLeft(basicRoutes.routeWithDefaults)(_ ~ _) ~ swaggerRoute.route ~ defaultOptionsRoute) (extractHost & extractClientIP & trace(tracer) & handleRejections(authenticationRejectionHandler)) { case (origin, ip) => diff --git a/src/main/scala/xyz/driver/core/rest/DriverRoute.scala b/src/main/scala/xyz/driver/core/rest/DriverRoute.scala index 288245a..911e306 100644 --- a/src/main/scala/xyz/driver/core/rest/DriverRoute.scala +++ b/src/main/scala/xyz/driver/core/rest/DriverRoute.scala @@ -2,8 +2,9 @@ package xyz.driver.core.rest import java.sql.SQLException -import akka.http.scaladsl.model.{StatusCodes, _} +import akka.http.scaladsl.model.headers.CacheDirectives.`no-cache` import akka.http.scaladsl.model.headers._ +import akka.http.scaladsl.model.{StatusCodes, _} import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server._ import com.typesafe.scalalogging.Logger @@ -32,11 +33,7 @@ trait DriverRoute { val tracingHeader = RawHeader(ContextHeaders.TrackingIdHeader, trackingId) MDC.put("trackingId", trackingId) - // This header will eliminate the risk of LB trying to reuse a connection - // that already timed out on the server side by completely rejecting keep-alive - val rejectKeepAlive = Connection("close") - - respondWithHeaders(tracingHeader, rejectKeepAlive) + respondWithHeaders(tracingHeader +: DriverRoute.DefaultHeaders: _*) } } @@ -112,3 +109,14 @@ trait DriverRoute { } } + +object DriverRoute { + val DefaultHeaders: List[HttpHeader] = List( + // This header will eliminate the risk of envoy trying to reuse a connection + // that already timed out on the server side by completely rejecting keep-alive + Connection("close"), + // These 2 headers are the simplest way to prevent IE from caching GET requests + RawHeader("Pragma", "no-cache"), + `Cache-Control`(List(`no-cache`(Nil))) + ) +} diff --git a/src/test/scala/xyz/driver/core/rest/DriverAppTest.scala b/src/test/scala/xyz/driver/core/rest/DriverAppTest.scala index eda6a8c..8f552db 100644 --- a/src/test/scala/xyz/driver/core/rest/DriverAppTest.scala +++ b/src/test/scala/xyz/driver/core/rest/DriverAppTest.scala @@ -1,7 +1,8 @@ package xyz.driver.core.rest -import akka.http.scaladsl.model.{HttpMethod, StatusCodes} +import akka.http.scaladsl.model.headers.CacheDirectives.`no-cache` import akka.http.scaladsl.model.headers._ +import akka.http.scaladsl.model.{HttpMethod, StatusCodes} import akka.http.scaladsl.server.{Directives, Route} import akka.http.scaladsl.testkit.ScalatestRouteTest import com.typesafe.config.ConfigFactory @@ -81,4 +82,13 @@ class DriverAppTest extends AsyncFlatSpec with ScalatestRouteTest with Matchers header[`Access-Control-Allow-Methods`].get.methods should contain theSameElementsAs allowedMethods } } + + it should "respond with Pragma and Cache-Control (no-cache) headers" in { + val route = new TestApp(get(complete(StatusCodes.OK))) + Get(s"/api/v1/test") ~> route.appRoute ~> check { + status shouldBe StatusCodes.OK + header("Pragma").map(_.value()) should contain("no-cache") + header[`Cache-Control`].map(_.value()) should contain("no-cache") + } + } } -- cgit v1.2.3