Created
August 27, 2018 12:42
-
-
Save Slakah/670e932dace0b669c55eae3d6b386a54 to your computer and use it in GitHub Desktop.
http4s caching middleware
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
package com.gubbns | |
import cats.data.{Kleisli, OptionT} | |
import cats.effect._ | |
import cats.implicits._ | |
import com.github.blemale.scaffeine.{Cache, Scaffeine} | |
import org.http4s._ | |
import org.http4s.headers._ | |
import org.http4s.server.Middleware | |
/** Caching middleware, based on request + method */ | |
class HttpCaching[F[_], G[_]] private ()(implicit F: Sync[F]) { | |
private val cache: Cache[(Uri, Method), Response[G]] = Scaffeine().build() | |
private def getCached(request: Request[G], http: Http[F, G]) = { | |
if (request.method.isSafe) { | |
getIfPresent(request) | |
.getOrElseF(httpAndCache(request, http)) | |
} else { | |
http(request) | |
} | |
} | |
private def getIfPresent(request: Request[G]) = { | |
OptionT(F.delay(cache.getIfPresent(request.uri -> request.method))) | |
} | |
private def httpAndCache(request: Request[G], http: Http[F, G]) = { | |
for { | |
resp <- http(request) | |
shouldCache = isCacheable(resp) | |
_ <- if (shouldCache) { | |
F.delay(cache.put(request.uri -> request.method, resp)) | |
} else { | |
F.suspend(F.unit) | |
} | |
} yield resp | |
} | |
private def isCacheable(resp: Response[G]): Boolean = { | |
import Status._ | |
!resp.headers | |
.get(`Cache-Control`) | |
.exists(_.values.exists(_ == CacheDirective.`no-store`)) && | |
(resp.status match { | |
case Ok => true | |
case NonAuthoritativeInformation => true | |
case NoContent => true | |
case PartialContent => true | |
case MultipleChoices => true | |
case MovedPermanently => true | |
case NotFound => true | |
case MethodNotAllowed => true | |
case Gone => true | |
case UriTooLong => true | |
case NotImplemented => true | |
case _ => false | |
}) | |
} | |
def caching: Middleware[F, Request[G], Response[G], Request[G], Response[G]] = http => Kleisli { request => | |
getCached(request, http) | |
} | |
} | |
object HttpCaching { | |
def apply[F[_]: Sync, G[_]]( | |
http: Http[F, G] | |
): Http[F, G] = new HttpCaching().caching(http) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment