app/story_packages/model/Cached.scala (45 lines of code) (raw):

package story_packages.model import org.joda.time.format.DateTimeFormat import org.joda.time.{DateTime, DateTimeZone, Period} import play.api.mvc.{Action, Request, Result} import scala.concurrent.{ExecutionContext, Future} object Cached { private val HTTPDateFormat = DateTimeFormat.forPattern("EEE, dd MMM yyyy HH:mm:ss 'GMT'").withZone(DateTimeZone.UTC) implicit class DateTime2ToCommonDateFormats(date: DateTime) { lazy val toHttpDateTimeString: String = date.toString(HTTPDateFormat) } private val cacheableStatusCodes = Seq(200, 404) private val tenDaysInSeconds = 864000 def apply(seconds: Int)(result: Result): Result = { if (cacheableStatusCodes.exists(_ == result.header.status)) cacheHeaders(seconds, result) else result } private def cacheHeaders(seconds: Int, result: Result) = { val now = DateTime.now val expiresTime = now.plus(Period.seconds(seconds)) val maxAge = seconds // NOTE, if you change these headers make sure they are compatible with our Edge Cache // see // http://tools.ietf.org/html/rfc5861 // http://www.fastly.com/blog/stale-while-revalidate // http://docs.fastly.com/guides/22966608/40347813 val staleWhileRevalidateSeconds = math.max(maxAge / 10, 1) val cacheControl = s"max-age=$maxAge, stale-while-revalidate=$staleWhileRevalidateSeconds, stale-if-error=$tenDaysInSeconds" result.withHeaders( "Surrogate-Control" -> cacheControl, "Cache-Control" -> cacheControl, "Expires" -> expiresTime.toHttpDateTimeString, "Date" -> now.toHttpDateTimeString ) } } object NoCache { def apply(result: Result): Result = result.withHeaders("Cache-Control" -> "no-cache", "Pragma" -> "no-cache") } case class NoCache[A](action: Action[A])(implicit ec: ExecutionContext) extends Action[A] { override def executionContext = ec override def apply(request: Request[A]): Future[Result] = { action(request) map { response => response.withHeaders( ("Cache-Control", "no-cache, no-store, must-revalidate"), ("Pragma", "no-cache"), ("Expires", "0") ) } } lazy val parser = action.parser }