org.http4s.Response Scala Examples
The following examples show how to use org.http4s.Response.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example.
Example 1
Source File: NatchezHttp4sModule.scala From skunk with MIT License | 8 votes |
// Copyright (c) 2018-2020 by Rob Norris // This software is licensed under the MIT License (MIT). // For more information see LICENSE or https://opensource.org/licenses/MIT package natchez.http4s import cats.~> import cats.data.{ Kleisli, OptionT } import cats.effect.Bracket import cats.implicits._ import natchez.{ EntryPoint, Kernel, Span } import org.http4s.HttpRoutes import natchez.Trace import natchez.Tags import scala.util.control.NonFatal import org.http4s.Response import cats.effect.Resource import cats.Defer import natchez.TraceValue import cats.Monad object implicits { // Given an entry point and HTTP Routes in Kleisli[F, Span[F], ?] return routes in F. A new span // is created with the URI path as the name, either as a continuation of the incoming trace, if // any, or as a new root. This can likely be simplified, I just did what the types were saying // and it works so :shrug: private def liftT[F[_]: Bracket[?[_], Throwable]]( entryPoint: EntryPoint[F])( routes: HttpRoutes[Kleisli[F, Span[F], ?]] ): HttpRoutes[F] = Kleisli { req => type G[A] = Kleisli[F, Span[F], A] val lift = λ[F ~> G](fa => Kleisli(_ => fa)) val kernel = Kernel(req.headers.toList.map(h => (h.name.value -> h.value)).toMap) val spanR = entryPoint.continueOrElseRoot(req.uri.path, kernel) OptionT { spanR.use { span => val lower = λ[G ~> F](_(span)) routes.run(req.mapK(lift)).mapK(lower).map(_.mapK(lower)).value } } } implicit class EntryPointOps[F[_]](self: EntryPoint[F]) { private def dummySpan( implicit ev: Monad[F] ): Span[F] = new Span[F] { val kernel: F[Kernel] = Kernel(Map.empty).pure[F] def put(fields: (String, TraceValue)*): F[Unit] = Monad[F].unit def span(name: String): Resource[F, Span[F]] = Monad[Resource[F, ?]].pure(this) } def liftT(routes: HttpRoutes[Kleisli[F, Span[F], ?]])( implicit ev: Bracket[F, Throwable] ): HttpRoutes[F] = implicits.liftT(self)(routes) def natchezMiddleware[F[_]: Bracket[?[_], Throwable]: Trace](routes: HttpRoutes[F]): HttpRoutes[F] = Kleisli { req => val addRequestFields: F[Unit] = Trace[F].put( Tags.http.method(req.method.name), Tags.http.url(req.uri.renderString) ) def addResponseFields(res: Response[F]): F[Unit] = Trace[F].put( Tags.http.status_code(res.status.code.toString) ) def addErrorFields(e: Throwable): F[Unit] = Trace[F].put( Tags.error(true), "error.message" -> e.getMessage, "error.stacktrace" -> e.getStackTrace.mkString("\n"), ) OptionT { routes(req).onError { case NonFatal(e) => OptionT.liftF(addRequestFields *> addErrorFields(e)) } .value.flatMap { case Some(handler) => addRequestFields *> addResponseFields(handler).as(handler.some) case None => Option.empty[Response[F]].pure[F] } } } }
Example 2
Source File: ResponseSyntax.scala From http4s-poc-api with MIT License | 5 votes |
package external package library package syntax import cats.effect.Sync import cats.syntax.applicativeError._ import org.http4s.Response import scala.language.implicitConversions import scala.reflect.ClassTag private[syntax] trait ResponseSyntax { implicit def responseSyntax[F[_]](r: F[Response[F]]): ResponseOps[F] = new ResponseOps(r) } private[syntax] class ResponseOps[F[_]](private val r: F[Response[F]]) extends AnyVal { def handlingFailures[E <: Throwable: ClassTag](hf: E => F[Response[F]])( implicit ev: Sync[F] ): F[Response[F]] = r recoverWith { case e: E => hf(e) } }
Example 3
Source File: Utils.scala From pizza-auth-3 with MIT License | 5 votes |
package moe.pizza.auth.webapp import moe.pizza.auth.interfaces.UserDatabase import moe.pizza.auth.models.Pilot import moe.pizza.auth.webapp.Types.{HydratedSession, Session2, Session} import moe.pizza.auth.webapp.Utils.Alerts.Alerts import org.http4s.{Uri, Response, Request} import org.http4s.dsl.{Root, _} import scalaz.concurrent.Task object Utils { object Alerts extends Enumeration { type Alerts = Value val success = Value("success") val info = Value("info") val warning = Value("warning") val danger = Value("danger") } implicit class PimpedSession2(s: Session2) { def hydrate(u: UserDatabase): HydratedSession = { new HydratedSession(s.alerts, s.redirect, s.uid.flatMap(u.getUser), s.signupData) } } implicit class PimpedHydratedSession(hs: HydratedSession) { def dehydrate(): Session2 = { new Session2(hs.alerts, hs.redirect, hs.pilot.map(_.uid), hs.signupData) } } implicit class PimpedRequest(r: Request) { def flash(level: Alerts, message: String): Option[HydratedSession] = { getSession.map(s => s.copy(alerts = s.alerts :+ Types.Alert(level.toString, message))) } def getSession = r.attributes.get(SessionManager.HYDRATEDSESSION) def setSession(s: Types.HydratedSession): Unit = r.attributes.put(SessionManager.HYDRATEDSESSION, s) def sessionResponse( f: ((HydratedSession, Pilot) => Task[Response]), error: String = "You must be signed in to do that"): Task[Response] = { (getSession, getSession.flatMap(_.pilot)) match { case (Some(s), Some(p)) => f(s, p) case _ => TemporaryRedirect(Uri(path = "/")) } } } implicit class PimpedResponse(r: Response) { def withSession(s: HydratedSession): Response = r.withAttribute(SessionManager.HYDRATEDSESSION, s) def getSession(): Option[HydratedSession] = r.attributes.get(SessionManager.HYDRATEDSESSION) def withNoSession(): Response = r.withAttribute(SessionManager.LOGOUT, "") } implicit class PimpedTaskResponse(r: Task[Response]) { def attachSessionifDefined(s: Option[HydratedSession]): Task[Response] = r.map(res => s.foldLeft(res) { (resp, sess) => resp.withSession(sess) }) def clearSession(): Task[Response] = r.map(res => res.withNoSession()) } def sanitizeUserName(name: String) = name.toLowerCase.replace("'", "").replace(" ", "_") }
Example 4
Source File: AbstractHttpClient.scala From nexus with Apache License 2.0 | 5 votes |
package ch.epfl.bluebrain.nexus.cli.clients import cats.effect.{Sync, Timer} import cats.implicits._ import ch.epfl.bluebrain.nexus.cli.CliError.ClientError import ch.epfl.bluebrain.nexus.cli.CliError.ClientError.{SerializationError, Unexpected} import ch.epfl.bluebrain.nexus.cli.config.EnvConfig import ch.epfl.bluebrain.nexus.cli.{logRetryErrors, ClientErrOr, Console} import io.circe.Decoder import org.http4s.circe.CirceEntityDecoder._ import org.http4s.client.Client import org.http4s.{Request, Response} import retry.CatsEffect._ import retry.RetryPolicy import retry.syntax.all._ import scala.reflect.ClassTag import scala.util.control.NonFatal class AbstractHttpClient[F[_]: Timer](client: Client[F], env: EnvConfig)(implicit protected val F: Sync[F], protected val console: Console[F] ) { protected val retry = env.httpClient.retry protected def successCondition[A] = retry.condition.notRetryFromEither[A] _ implicit protected val retryPolicy: RetryPolicy[F] = retry.retryPolicy implicit protected def logOnError[A] = logRetryErrors[F, A]("interacting with an HTTP API") protected def executeDiscard[A](req: Request[F], returnValue: => A): F[ClientErrOr[A]] = execute(req, _.body.compile.drain.as(Right(returnValue))) protected def executeParse[A: Decoder](req: Request[F])(implicit A: ClassTag[A]): F[ClientErrOr[A]] = execute( req, _.attemptAs[A].value.map( _.leftMap(err => SerializationError(err.message, s"The response payload was not of type '${A.runtimeClass.getSimpleName}'") ) ) ) private def execute[A](req: Request[F], f: Response[F] => F[ClientErrOr[A]]): F[ClientErrOr[A]] = client .fetch(req)(ClientError.errorOr[F, A](r => f(r))) .recoverWith { case NonFatal(err) => F.delay(Left(Unexpected(Option(err.getMessage).getOrElse("").take(30)))) } .retryingM(successCondition[A]) }
Example 5
Source File: SparqlClientSpec.scala From nexus with Apache License 2.0 | 5 votes |
package ch.epfl.bluebrain.nexus.cli.clients import cats.effect.IO import cats.implicits._ import ch.epfl.bluebrain.nexus.cli.{AbstractCliSpec, Console} import ch.epfl.bluebrain.nexus.cli.CliError.ClientError.{ClientStatusError, ServerStatusError} import ch.epfl.bluebrain.nexus.cli.config.{AppConfig, EnvConfig} import ch.epfl.bluebrain.nexus.cli.sse._ import ch.epfl.bluebrain.nexus.cli.utils.Http4sExtras import izumi.distage.model.definition.ModuleDef import org.http4s.circe.CirceEntityEncoder._ import org.http4s.client.Client import org.http4s.dsl.io._ import org.http4s.headers.`Content-Type` import org.http4s.{HttpApp, Response, Status, Uri} import org.scalatest.OptionValues class SparqlClientSpec extends AbstractCliSpec with Http4sExtras with OptionValues { private val sparqlResultsJson = jsonContentOf("/templates/sparql-results.json") private val sparqlResults = sparqlResultsJson.as[SparqlResults].toOption.value private val query = "SELECT * {?s ?p ?o} LIMIT 10" override def overrides: ModuleDef = new ModuleDef { include(defaultModules) make[Client[IO]].from { cfg: AppConfig => val token = cfg.env.token val ct = `Content-Type`(SparqlClient.`application/sparql-query`) val view = cfg.env.defaultSparqlView.renderString val httpApp = HttpApp[IO] { // success case req @ POST -> `v1` / "views" / OrgLabelVar(`orgLabel`) / ProjectLabelVar( `projectLabel` ) / `view` / "sparql" contentType `ct` optbearer `token` => req.as[String].flatMap { case `query` => Response[IO](Status.Ok).withEntity(sparqlResultsJson).pure[IO] case _ => Response[IO](Status.BadRequest).pure[IO] } // unknown view id case req @ POST -> `v1` / "views" / OrgLabelVar(`orgLabel`) / ProjectLabelVar( `projectLabel` ) / (_: String) / "sparql" contentType `ct` optbearer `token` => req.as[String].flatMap { case `query` => Response[IO](Status.NotFound).withEntity(notFoundJson).pure[IO] case _ => Response[IO](Status.BadRequest).pure[IO] } // unknown token case req @ POST -> `v1` / "views" / OrgLabelVar(`orgLabel`) / ProjectLabelVar( `projectLabel` ) / `view` / "sparql" contentType `ct` optbearer (_: Option[ BearerToken ]) => req.as[String].flatMap { case `query` => Response[IO](Status.Forbidden).withEntity(authFailedJson).pure[IO] case _ => Response[IO](Status.BadRequest).pure[IO] } // other - internal error case req @ POST -> "v1" /: (_: Path) contentType `ct` optbearer `token` => req.as[String].flatMap { case `query` => Response[IO](Status.InternalServerError).withEntity(internalErrorJson).pure[IO] case _ => Response[IO](Status.BadRequest).pure[IO] } } Client.fromHttpApp(httpApp) } } "A SparqlClient" should { "return sparql results" in { (client: Client[IO], console: Console[IO], env: EnvConfig) => val cl = SparqlClient(client, env, console) for { results <- cl.query(orgLabel, projectLabel, query) _ = results shouldEqual Right(sparqlResults) } yield () } "return not found" in { (client: Client[IO], console: Console[IO], env: EnvConfig) => val cl = SparqlClient(client, env, console) for { results <- cl.query(orgLabel, projectLabel, Uri.unsafeFromString(genString()), query) _ = results shouldEqual Left(ClientStatusError(Status.NotFound, notFoundJson.noSpaces)) } yield () } "return internal error" in { (client: Client[IO], console: Console[IO], env: EnvConfig) => val cl = SparqlClient(client, env, console) for { results <- cl.query(orgLabel, ProjectLabel(genString()), Uri.unsafeFromString(genString()), query) _ = results shouldEqual Left(ServerStatusError(Status.InternalServerError, internalErrorJson.noSpaces)) } yield () } "return bad token" in { (client: Client[IO], console: Console[IO], env: EnvConfig) => val cl = SparqlClient(client, env.copy(token = Some(BearerToken("bad"))), console) for { results <- cl.query(orgLabel, projectLabel, query) _ = results shouldEqual Left(ClientStatusError(Status.Forbidden, authFailedJson.noSpaces)) } yield () } } }
Example 6
Source File: ProjectClientSpec.scala From nexus with Apache License 2.0 | 5 votes |
package ch.epfl.bluebrain.nexus.cli.clients import java.util.UUID import cats.effect.IO import cats.effect.concurrent.Ref import cats.implicits._ import ch.epfl.bluebrain.nexus.cli.{AbstractCliSpec, Console} import ch.epfl.bluebrain.nexus.cli.CliError.ClientError.ClientStatusError import ch.epfl.bluebrain.nexus.cli.config.{AppConfig, EnvConfig} import ch.epfl.bluebrain.nexus.cli.sse._ import ch.epfl.bluebrain.nexus.cli.utils.Http4sExtras import izumi.distage.model.definition.ModuleDef import org.http4s.circe.CirceEntityEncoder._ import org.http4s.client.Client import org.http4s.dsl.io._ import org.http4s.{HttpApp, Response, Status} class ProjectClientSpec extends AbstractCliSpec with Http4sExtras { private val projectJson = jsonContentOf("/templates/project.json", replacements) type Cache = Map[(OrgUuid, ProjectUuid), (OrgLabel, ProjectLabel)] type CacheRef = Ref[IO, Cache] override def overrides: ModuleDef = new ModuleDef { include(defaultModules) make[Client[IO]].from { cfg: AppConfig => val token = cfg.env.token val httpApp = HttpApp[IO] { case GET -> `v1` / "projects" / OrgUuidVar(`orgUuid`) / ProjectUuidVar(`projectUuid`) optbearer `token` => Response[IO](Status.Ok).withEntity(projectJson).pure[IO] case GET -> `v1` / "projects" / OrgUuidVar(_) / ProjectUuidVar(_) optbearer `token` => Response[IO](Status.NotFound).withEntity(notFoundJson).pure[IO] case GET -> `v1` / "projects" / OrgUuidVar(_) / ProjectUuidVar(_) bearer (_: BearerToken) => Response[IO](Status.Forbidden).withEntity(authFailedJson).pure[IO] } Client.fromHttpApp(httpApp) } make[CacheRef].fromEffect { Ref.of[IO, Cache](Map.empty) } } "A ProjectClient" should { "resolve a known (orgUuid, projUuid) pair" in { (client: Client[IO], console: Console[IO], cache: CacheRef, env: EnvConfig) => val cl = ProjectClient[IO](client, env, cache, console) for { labels <- cl.labels(orgUuid, projectUuid) _ = labels shouldEqual Right((orgLabel, projectLabel)) } yield () } "resolve from cache a known (orgUuid, projUuid) pair" in { (client: Client[IO], console: Console[IO], cache: CacheRef, env: EnvConfig) => val errClient = Client.fromHttpApp(HttpApp[IO] { case GET -> Root => IO.pure(Response[IO](Status.NotFound)) }) for { _ <- ProjectClient[IO](client, env, cache, console).labels(orgUuid, projectUuid) labels <- ProjectClient[IO](errClient, env, cache, console).labels(orgUuid, projectUuid) _ = labels shouldEqual Right((orgLabel, projectLabel)) } yield () } "fail to resolve an unknown (orgUuid, projUuid) pair" in { (client: Client[IO], console: Console[IO], cache: CacheRef, env: EnvConfig) => val cl = ProjectClient[IO](client, env, cache, console) for { labels <- cl.labels(OrgUuid(UUID.randomUUID()), projectUuid) _ = labels shouldEqual Left(ClientStatusError(Status.NotFound, notFoundJson.noSpaces)) } yield () } "fail to resolve a known (orgUuid, projUuid) pair with bad credentials" in { (client: Client[IO], console: Console[IO], cache: CacheRef, env: EnvConfig) => val cl = ProjectClient[IO](client, env.copy(token = Some(BearerToken("bad"))), cache, console) for { labels <- cl.labels(orgUuid, projectUuid) _ = labels shouldEqual Left(ClientStatusError(Status.Forbidden, authFailedJson.noSpaces)) } yield () } } }
Example 7
Source File: Auth.scala From scala-pet-store with Apache License 2.0 | 5 votes |
package io.github.pauljamescleary.petstore.domain.authentication import cats.MonadError import cats.effect._ import io.github.pauljamescleary.petstore.domain.users.{Role, User} import org.http4s.Response import tsec.authentication.{ AugmentedJWT, BackingStore, IdentityStore, JWTAuthenticator, SecuredRequest, TSecAuthService, } import tsec.authorization.BasicRBAC import tsec.common.SecureRandomId import tsec.jws.mac.JWSMacCV import tsec.jwt.algorithms.JWTMacAlgo import tsec.mac.jca.MacSigningKey import scala.concurrent.duration._ object Auth { def jwtAuthenticator[F[_]: Sync, Auth: JWTMacAlgo]( key: MacSigningKey[Auth], authRepo: BackingStore[F, SecureRandomId, AugmentedJWT[Auth, Long]], userRepo: IdentityStore[F, Long, User], )(implicit cv: JWSMacCV[F, Auth]): JWTAuthenticator[F, Long, User, Auth] = JWTAuthenticator.backed.inBearerToken( expiryDuration = 1.hour, maxIdle = None, tokenStore = authRepo, identityStore = userRepo, signingKey = key, ) private def _allRoles[F[_], Auth]( implicit F: MonadError[F, Throwable], ): BasicRBAC[F, Role, User, Auth] = BasicRBAC.all[F, Role, User, Auth] def allRoles[F[_], Auth]( pf: PartialFunction[SecuredRequest[F, User, AugmentedJWT[Auth, Long]], F[Response[F]]], )(implicit F: MonadError[F, Throwable]): TSecAuthService[User, AugmentedJWT[Auth, Long], F] = TSecAuthService.withAuthorization(_allRoles[F, AugmentedJWT[Auth, Long]])(pf) def allRolesHandler[F[_], Auth]( pf: PartialFunction[SecuredRequest[F, User, AugmentedJWT[Auth, Long]], F[Response[F]]], )( onNotAuthorized: TSecAuthService[User, AugmentedJWT[Auth, Long], F], )(implicit F: MonadError[F, Throwable]): TSecAuthService[User, AugmentedJWT[Auth, Long], F] = TSecAuthService.withAuthorizationHandler(_allRoles[F, AugmentedJWT[Auth, Long]])( pf, onNotAuthorized.run, ) private def _adminOnly[F[_], Auth]( implicit F: MonadError[F, Throwable], ): BasicRBAC[F, Role, User, Auth] = BasicRBAC[F, Role, User, Auth](Role.Admin) def adminOnly[F[_], Auth]( pf: PartialFunction[SecuredRequest[F, User, AugmentedJWT[Auth, Long]], F[Response[F]]], )(implicit F: MonadError[F, Throwable]): TSecAuthService[User, AugmentedJWT[Auth, Long], F] = TSecAuthService.withAuthorization(_adminOnly[F, AugmentedJWT[Auth, Long]])(pf) }
Example 8
Source File: LoginTest.scala From scala-pet-store with Apache License 2.0 | 5 votes |
package io.github.pauljamescleary.petstore package infrastructure.endpoint import cats.data.Kleisli import cats.effect.IO import domain.authentication.{LoginRequest, SignupRequest} import domain.users.{Role, User} import org.http4s.circe.{jsonEncoderOf, jsonOf} import org.http4s.client.dsl.Http4sClientDsl import org.http4s.{EntityDecoder, EntityEncoder, HttpApp, Request, Response} import org.http4s.implicits._ import org.http4s.headers.Authorization import io.circe.generic.auto._ import org.http4s.dsl.Http4sDsl trait LoginTest extends Http4sClientDsl[IO] with Http4sDsl[IO] { implicit val userEnc: EntityEncoder[IO, User] = jsonEncoderOf implicit val userDec: EntityDecoder[IO, User] = jsonOf implicit val signUpRequestEnc: EntityEncoder[IO, SignupRequest] = jsonEncoderOf implicit val signUpRequestDec: EntityDecoder[IO, SignupRequest] = jsonOf implicit val loginRequestEnc: EntityEncoder[IO, LoginRequest] = jsonEncoderOf implicit val loginRequestDec: EntityDecoder[IO, LoginRequest] = jsonOf def signUpAndLogIn( userSignUp: SignupRequest, userEndpoint: HttpApp[IO], ): IO[(User, Option[Authorization])] = for { signUpRq <- POST(userSignUp, uri"/users") signUpResp <- userEndpoint.run(signUpRq) user <- signUpResp.as[User] loginBody = LoginRequest(userSignUp.userName, userSignUp.password) loginRq <- POST(loginBody, uri"/users/login") loginResp <- userEndpoint.run(loginRq) } yield { user -> loginResp.headers.get(Authorization) } def signUpAndLogInAsAdmin( userSignUp: SignupRequest, userEndpoint: Kleisli[IO, Request[IO], Response[IO]], ): IO[(User, Option[Authorization])] = signUpAndLogIn(userSignUp.copy(role = Role.Admin), userEndpoint) def signUpAndLogInAsCustomer( userSignUp: SignupRequest, userEndpoint: Kleisli[IO, Request[IO], Response[IO]], ): IO[(User, Option[Authorization])] = signUpAndLogIn(userSignUp.copy(role = Role.Customer), userEndpoint) }
Example 9
Source File: PriceRoutes.scala From http4s-poc-api with MIT License | 5 votes |
package server import cats.effect.Sync import cats.syntax.applicativeError._ import cats.syntax.flatMap._ import cats.syntax.functor._ import cats.syntax.show._ import errors.PriceServiceError import errors.PriceServiceError._ import external.library.syntax.response._ import model.DomainModel._ import org.http4s.dsl.Http4sDsl import org.http4s.{EntityDecoder, EntityEncoder, HttpRoutes, Method, Request, Response} import service.PriceService sealed abstract class PriceRoutes[F[_]: Sync]( implicit requestDecoder: EntityDecoder[F, PricesRequestPayload], responseEncoder: EntityEncoder[F, List[Price]] ) extends Http4sDsl[F] { def make(priceService: PriceService[F]): HttpRoutes[F] = HttpRoutes.of[F] { case req @ Method.POST -> Root => postResponse(req, priceService) handlingFailures priceServiceErrors handleErrorWith unhandledThrowable } private[this] def postResponse(request: Request[F], priceService: PriceService[F]): F[Response[F]] = for { payload <- request.as[PricesRequestPayload] prices <- priceService.prices(payload.userId, payload.productIds) resp <- Ok(prices) } yield resp private[this] def priceServiceErrors: PriceServiceError => F[Response[F]] = { case UserErr(r) => FailedDependency(r) case PreferenceErr(r) => FailedDependency(r) case ProductErr(r) => FailedDependency(r) case ProductPriceErr(r) => FailedDependency(r) case CacheLookupError(r) => FailedDependency(r) case CacheStoreError(r) => FailedDependency(r) case InvalidShippingCountry(r) => BadRequest(r) } private[this] def unhandledThrowable: Throwable => F[Response[F]] = { th => import external.library.instances.throwable._ InternalServerError(th.show) } } object PriceRoutes { def apply[ F[_]: Sync: EntityDecoder[*[_], PricesRequestPayload]: EntityEncoder[*[_], List[Price]] ]: PriceRoutes[F] = new PriceRoutes[F] {} }
Example 10
Source File: TracedHttpRoute.scala From http4s-tracer with Apache License 2.0 | 5 votes |
package dev.profunktor.tracer import cats.Monad import cats.data.{Kleisli, OptionT} import cats.syntax.flatMap._ import cats.syntax.functor._ import dev.profunktor.tracer.Tracer.TraceId import org.http4s.{HttpRoutes, Request, Response} object TracedHttpRoute { case class TracedRequest[F[_]](traceId: TraceId, request: Request[F]) def apply[F[_]: Monad: Tracer]( pf: PartialFunction[TracedRequest[F], F[Response[F]]] ): HttpRoutes[F] = Kleisli[OptionT[F, ?], Request[F], Response[F]] { req => OptionT { Tracer[F] .getTraceId(req) .map(x => TracedRequest[F](x.getOrElse(TraceId("-")), req)) .flatMap { tr => val rs: OptionT[F, Response[F]] = pf.andThen(OptionT.liftF(_)).applyOrElse(tr, Function.const(OptionT.none)) rs.value } } } }
Example 11
Source File: ResponseVerificationSyntax.scala From http4s-poc-api with MIT License | 5 votes |
package syntax import java.nio.charset.StandardCharsets import cats.data.Validated import cats.instances.string._ import cats.syntax.eq._ import cats.syntax.show._ import cats.syntax.validated._ import cats.{Eq, Show} import org.http4s.{EntityDecoder, Response, Status} import typeclasses.RunSync import zio.Task import zio.interop.catz._ import scala.language.implicitConversions private[syntax] trait ResponseVerificationSyntax { implicit def verifiedSyntax[A](a: A): VerifiedOps[A] = new VerifiedOps(a) implicit def verifiedOptionSyntax[A](a: Option[A]): VerifiedOptionOps[A] = new VerifiedOptionOps(a) implicit def responseVerificationSyntax(response: Task[Response[Task]]) = new IoResponseResultOps(response) } private[syntax] class IoResponseResultOps(private val response: Task[Response[Task]]) extends AnyVal { import syntax.responseVerification._ def verify[A: EntityDecoder[Task, *]](status: Status, check: A => Verified[A])( implicit ev1: Eq[Status], ev2: Show[Status], run: RunSync[Task] ): Verified[A] = run .syncUnsafe(response) .fold( err => s"Should succeed but returned the error $err".invalidNel, res => res.status isSameAs status andThen { _ => verifiedResponse[A](res, check) } ) def verifyResponseText(status: Status, expected: String)( implicit ev1: Eq[Status], ev2: Show[Status], run: RunSync[Task] ): Verified[String] = run .syncUnsafe(response) .fold( err => s"Should succeed but returned the error $err".invalidNel, res => res.status isSameAs status andThen { _ => verifiedResponseText(res, expected) } ) private def verifiedResponse[A: EntityDecoder[Task, *]](res: Response[Task], check: A => Verified[A])( implicit run: RunSync[Task] ): Verified[A] = run .syncUnsafe(res.as[A]) .fold( respErr => s"Response should succeed but returned the error $respErr".invalidNel, respRes => check(respRes) ) private def verifiedResponseText[A](res: Response[Task], expected: String)( implicit run: RunSync[Task] ): Verified[String] = run .syncUnsafe(res.body.compile.toVector) .map(_.toArray) .fold( respErr => s"Response should succeed but returned the error $respErr".invalidNel, respMsg => new String(respMsg, StandardCharsets.UTF_8) isSameAs expected ) } private[syntax] class VerifiedOps[A](private val a: A) extends AnyVal { def isNotSameAs(expected: =>A)(implicit ev1: Eq[A], ev2: Show[A]): Verified[A] = Validated.condNel( a =!= expected, a, s"Unexpected value. Expected different from ${expected.show} but was ${a.show}" ) def isSameAs(expected: =>A)(implicit ev1: Eq[A], ev2: Show[A]): Verified[A] = Validated.condNel(a === expected, a, s"Unexpected value. Expected ${expected.show} but was ${a.show}") def is(p: A => Boolean, reason: =>String = "")(implicit ev: Show[A]): Verified[A] = Validated.condNel(p(a), a, s"Unexpected value ${a.show}: Reason $reason") } private[syntax] class VerifiedOptionOps[A](private val a: Option[A]) extends AnyVal { def isNotEmpty: Verified[Option[A]] = Validated.condNel(a.isDefined, a, s"Unexpected empty option value") }
Example 12
Source File: EndpointToHttp4sServer.scala From tapir with Apache License 2.0 | 5 votes |
package sttp.tapir.server.http4s import cats.data._ import cats.effect.{ContextShift, Sync} import cats.implicits._ import org.http4s.{EntityBody, HttpRoutes, Request, Response} import org.log4s._ import sttp.tapir.monad.MonadError import sttp.tapir.server.internal.{DecodeInputsResult, InputValues, InputValuesResult} import sttp.tapir.server.{DecodeFailureContext, DecodeFailureHandling, ServerDefaults, ServerEndpoint, internal} import sttp.tapir.{DecodeResult, Endpoint, EndpointIO, EndpointInput} class EndpointToHttp4sServer[F[_]: Sync: ContextShift](serverOptions: Http4sServerOptions[F]) { private val outputToResponse = new OutputToHttp4sResponse[F](serverOptions) def toRoutes[I, E, O](se: ServerEndpoint[I, E, O, EntityBody[F], F]): HttpRoutes[F] = { val service: HttpRoutes[F] = HttpRoutes[F] { req: Request[F] => def decodeBody(result: DecodeInputsResult): F[DecodeInputsResult] = { result match { case values: DecodeInputsResult.Values => values.bodyInput match { case Some(bodyInput @ EndpointIO.Body(bodyType, codec, _)) => new Http4sRequestToRawBody(serverOptions).apply(req.body, bodyType, req.charset, req).map { v => codec.decode(v) match { case DecodeResult.Value(bodyV) => values.setBodyInputValue(bodyV) case failure: DecodeResult.Failure => DecodeInputsResult.Failure(bodyInput, failure): DecodeInputsResult } } case None => (values: DecodeInputsResult).pure[F] } case failure: DecodeInputsResult.Failure => (failure: DecodeInputsResult).pure[F] } } def valueToResponse(value: Any): F[Response[F]] = { val i = value.asInstanceOf[I] se.logic(new CatsMonadError)(i) .map { case Right(result) => outputToResponse(ServerDefaults.StatusCodes.success, se.endpoint.output, result) case Left(err) => outputToResponse(ServerDefaults.StatusCodes.error, se.endpoint.errorOutput, err) } .flatTap { response => serverOptions.logRequestHandling.requestHandled(se.endpoint, response.status.code) } .onError { case e: Exception => serverOptions.logRequestHandling.logicException(se.endpoint, e) } } OptionT(decodeBody(internal.DecodeInputs(se.endpoint.input, new Http4sDecodeInputsContext[F](req))).flatMap { case values: DecodeInputsResult.Values => InputValues(se.endpoint.input, values) match { case InputValuesResult.Value(params, _) => valueToResponse(params.asAny).map(_.some) case InputValuesResult.Failure(input, failure) => handleDecodeFailure(se.endpoint, input, failure) } case DecodeInputsResult.Failure(input, failure) => handleDecodeFailure(se.endpoint, input, failure) }) } service } def toRoutes[I, E, O](serverEndpoints: List[ServerEndpoint[_, _, _, EntityBody[F], F]]): HttpRoutes[F] = { NonEmptyList.fromList(serverEndpoints.map(se => toRoutes(se))) match { case Some(routes) => routes.reduceK case None => HttpRoutes.empty } } private def handleDecodeFailure[I]( e: Endpoint[_, _, _, _], input: EndpointInput[_], failure: DecodeResult.Failure ): F[Option[Response[F]]] = { val decodeFailureCtx = DecodeFailureContext(input, failure) val handling = serverOptions.decodeFailureHandler(decodeFailureCtx) handling match { case DecodeFailureHandling.NoMatch => serverOptions.logRequestHandling.decodeFailureNotHandled(e, decodeFailureCtx).map(_ => None) case DecodeFailureHandling.RespondWithResponse(output, value) => serverOptions.logRequestHandling .decodeFailureHandled(e, decodeFailureCtx, value) .map(_ => Some(outputToResponse(ServerDefaults.StatusCodes.error, output, value))) } } private class CatsMonadError(implicit F: cats.MonadError[F, Throwable]) extends MonadError[F] { override def unit[T](t: T): F[T] = F.pure(t) override def map[T, T2](fa: F[T])(f: T => T2): F[T2] = F.map(fa)(f) override def flatMap[T, T2](fa: F[T])(f: T => F[T2]): F[T2] = F.flatMap(fa)(f) override def error[T](t: Throwable): F[T] = F.raiseError(t) override protected def handleWrappedError[T](rt: F[T])(h: PartialFunction[Throwable, F[T]]): F[T] = F.recoverWith(rt)(h) } } object EndpointToHttp4sServer { private[http4s] val log: Logger = getLogger }
Example 13
Source File: Http4sServerTests.scala From tapir with Apache License 2.0 | 5 votes |
package sttp.tapir.server.http4s import cats.data.{Kleisli, NonEmptyList} import cats.effect._ import cats.implicits._ import org.http4s.server.Router import org.http4s.server.blaze.BlazeServerBuilder import org.http4s.syntax.kleisli._ import org.http4s.{EntityBody, HttpRoutes, Request, Response} import sttp.tapir.server.tests.ServerTests import sttp.tapir.Endpoint import sttp.tapir._ import sttp.client._ import sttp.tapir.server.{DecodeFailureHandler, ServerDefaults, ServerEndpoint} import sttp.tapir.tests.{Port, PortCounter} import scala.concurrent.ExecutionContext import scala.reflect.ClassTag class Http4sServerTests extends ServerTests[IO, EntityBody[IO], HttpRoutes[IO]] { implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global implicit val contextShift: ContextShift[IO] = IO.contextShift(ec) implicit val timer: Timer[IO] = IO.timer(ec) override def pureResult[T](t: T): IO[T] = IO.pure(t) override def suspendResult[T](t: => T): IO[T] = IO.apply(t) override def route[I, E, O]( e: ServerEndpoint[I, E, O, EntityBody[IO], IO], decodeFailureHandler: Option[DecodeFailureHandler] = None ): HttpRoutes[IO] = { implicit val serverOptions: Http4sServerOptions[IO] = Http4sServerOptions .default[IO] .copy( decodeFailureHandler = decodeFailureHandler.getOrElse(ServerDefaults.decodeFailureHandler) ) e.toRoutes } override def routeRecoverErrors[I, E <: Throwable, O](e: Endpoint[I, E, O, EntityBody[IO]], fn: I => IO[O])(implicit eClassTag: ClassTag[E] ): HttpRoutes[IO] = { e.toRouteRecoverErrors(fn) } override def server(routes: NonEmptyList[HttpRoutes[IO]], port: Port): Resource[IO, Unit] = { val service: Kleisli[IO, Request[IO], Response[IO]] = routes.reduceK.orNotFound BlazeServerBuilder[IO](ExecutionContext.global) .bindHttp(port, "localhost") .withHttpApp(service) .resource .void } override lazy val portCounter: PortCounter = new PortCounter(56000) if (testNameFilter.isEmpty) { test("should work with a router and routes in a context") { val e = endpoint.get.in("test" / "router").out(stringBody).serverLogic(_ => IO.pure("ok".asRight[Unit])) val routes = e.toRoutes val port = portCounter.next() BlazeServerBuilder[IO](ExecutionContext.global) .bindHttp(port, "localhost") .withHttpApp(Router("/api" -> routes).orNotFound) .resource .use { _ => basicRequest.get(uri"http://localhost:$port/api/test/router").send().map(_.body shouldBe Right("ok")) } .unsafeRunSync() } } }
Example 14
Source File: InvoicesApi.scala From event-sourcing-kafka-streams with MIT License | 5 votes |
package org.amitayh.invoices.web import java.util.UUID import cats.effect.{Concurrent, Timer} import cats.implicits._ import fs2.Stream import fs2.concurrent.Topic import io.circe._ import io.circe.generic.auto._ import io.circe.syntax._ import org.amitayh.invoices.common.domain.CommandResult.{Failure, Success} import org.amitayh.invoices.common.domain.{Command, CommandResult} import org.amitayh.invoices.dao.InvoiceList import org.amitayh.invoices.web.CommandDto._ import org.amitayh.invoices.web.PushEvents.CommandResultRecord import org.http4s.circe._ import org.http4s.dsl.Http4sDsl import org.http4s.{EntityDecoder, HttpRoutes, Response} import scala.concurrent.duration._ class InvoicesApi[F[_]: Concurrent: Timer] extends Http4sDsl[F] { private val maxQueued = 16 implicit val commandEntityDecoder: EntityDecoder[F, Command] = jsonOf[F, Command] def service(invoiceList: InvoiceList[F], producer: Kafka.Producer[F, UUID, Command], commandResultsTopic: Topic[F, CommandResultRecord]): HttpRoutes[F] = HttpRoutes.of[F] { case GET -> Root / "invoices" => invoiceList.get.flatMap(invoices => Ok(invoices.asJson)) case request @ POST -> Root / "execute" / "async" / UuidVar(invoiceId) => request .as[Command] .flatMap(producer.send(invoiceId, _)) .flatMap(metaData => Accepted(Json.fromLong(metaData.timestamp))) case request @ POST -> Root / "execute" / UuidVar(invoiceId) => request.as[Command].flatMap { command => val response = resultStream(commandResultsTopic, command.commandId) merge timeoutStream producer.send(invoiceId, command) *> response.head.compile.toList.map(_.head) } } private def resultStream(commandResultsTopic: Topic[F, CommandResultRecord], commandId: UUID): Stream[F, Response[F]] = commandResultsTopic.subscribe(maxQueued).collectFirst { case Some((_, CommandResult(_, `commandId`, outcome))) => outcome }.flatMap { case Success(_, _, snapshot) => Stream.eval(Ok(snapshot.asJson)) case Failure(cause) => Stream.eval(UnprocessableEntity(cause.message)) } private def timeoutStream: Stream[F, Response[F]] = Stream.eval(Timer[F].sleep(5.seconds) *> RequestTimeout("timeout")) } object InvoicesApi { def apply[F[_]: Concurrent: Timer]: InvoicesApi[F] = new InvoicesApi[F] }
Example 15
Source File: Http4sClientCircuitBreakerModule.scala From scala-server-toolkit with MIT License | 5 votes |
package com.avast.sst.http4s.client.monix.catnap import cats.effect.{Resource, Sync} import cats.syntax.applicativeError._ import cats.syntax.flatMap._ import monix.catnap.CircuitBreaker import org.http4s.Response import org.http4s.client.Client object Http4sClientCircuitBreakerModule { def make[F[_]: Sync]( client: Client[F], circuitBreaker: CircuitBreaker[F], httpStatusClassifier: HttpStatusClassifier = HttpStatusClassifier.default ): Client[F] = { val F = Sync[F] class ServerFailure(val response: Response[F], val close: F[Unit]) extends Exception Client[F] { request => val raisedInternal = client.run(request).allocated.flatMap { case tuple @ (response, _) if !httpStatusClassifier.isServerFailure(response.status) => F.pure(tuple) case (response, close) => F.raiseError[(Response[F], F[Unit])](new ServerFailure(response, close)) } val lifted = circuitBreaker.protect(raisedInternal).recover { case serverFailure: ServerFailure => (serverFailure.response, serverFailure.close) } Resource(lifted) } } }
Example 16
Source File: RouteMetrics.scala From scala-server-toolkit with MIT License | 5 votes |
package com.avast.sst.http4s.server.micrometer import cats.effect.Sync import cats.effect.syntax.bracket._ import cats.syntax.flatMap._ import cats.syntax.functor._ import io.micrometer.core.instrument.{MeterRegistry, Timer} import org.http4s.Response def wrap(name: String)(route: => F[Response[F]]): F[Response[F]] = { for { start <- F.delay(Timer.start(meterRegistry)) response <- route.bracket(F.pure) { response => F.delay( start.stop( meterRegistry .timer(s"http.$name", "status", s"${response.status.code}", "status-class", s"${response.status.code / 100}xx") ) ).as(()) } } yield response } }
Example 17
Source File: RouteMetricsTest.scala From scala-server-toolkit with MIT License | 5 votes |
package com.avast.sst.http4s.server.micrometer import java.util.concurrent.TimeUnit import cats.effect.SyncIO import io.micrometer.core.instrument.simple.SimpleMeterRegistry import org.http4s.Response import org.scalatest.funsuite.AnyFunSuite class RouteMetricsTest extends AnyFunSuite { test("Single route metrics") { val registry = new SimpleMeterRegistry() val target = new RouteMetrics[SyncIO](registry) target.wrap("test")(SyncIO.pure(Response.notFound[SyncIO])).unsafeRunSync() assert(registry.get("http.test").timer().count() === 1) assert(registry.get("http.test").timer().totalTime(TimeUnit.MILLISECONDS) > 0) assert(registry.get("http.test").tags("status", "404").timer().count() === 1) } }
Example 18
Source File: CorrelationIdMiddleware.scala From scala-server-toolkit with MIT License | 5 votes |
package com.avast.sst.http4s.server.middleware import java.util.UUID import cats.data.{Kleisli, OptionT} import cats.effect.Sync import cats.syntax.functor._ import com.avast.sst.http4s.server.middleware.CorrelationIdMiddleware.CorrelationId import io.chrisdavenport.vault.Key import org.http4s.util.CaseInsensitiveString import org.http4s.{Header, HttpRoutes, Request, Response} import org.slf4j.LoggerFactory class CorrelationIdMiddleware[F[_]: Sync]( correlationIdHeaderName: CaseInsensitiveString, attributeKey: Key[CorrelationId], generator: () => String ) { private val logger = LoggerFactory.getLogger(this.getClass) private val F = Sync[F] def wrap(routes: HttpRoutes[F]): HttpRoutes[F] = Kleisli[OptionT[F, *], Request[F], Response[F]] { request => request.headers.get(correlationIdHeaderName) match { case Some(header) => val requestWithAttribute = request.withAttribute(attributeKey, CorrelationId(header.value)) routes(requestWithAttribute).map(r => r.withHeaders(r.headers.put(header))) case None => for { newCorrelationId <- OptionT.liftF(F.delay(generator())) _ <- log(newCorrelationId) requestWithAttribute = request.withAttribute(attributeKey, CorrelationId(newCorrelationId)) response <- routes(requestWithAttribute) } yield response.withHeaders(response.headers.put(Header(correlationIdHeaderName.value, newCorrelationId))) } } def retrieveCorrelationId(request: Request[F]): Option[CorrelationId] = request.attributes.lookup(attributeKey) private def log(newCorrelationId: String) = { OptionT.liftF { F.delay { if (logger.isDebugEnabled()) { logger.debug(s"Generated new correlation ID: $newCorrelationId") } } } } } object CorrelationIdMiddleware { final case class CorrelationId(value: String) extends AnyVal @SuppressWarnings(Array("scalafix:Disable.toString")) def default[F[_]: Sync]: F[CorrelationIdMiddleware[F]] = { Key.newKey[F, CorrelationId].map { attributeKey => new CorrelationIdMiddleware(CaseInsensitiveString("Correlation-ID"), attributeKey, () => UUID.randomUUID().toString) } } }
Example 19
Source File: KamonSupport.scala From kamon-http4s with Apache License 2.0 | 5 votes |
package kamon.http4s package middleware.client import cats.effect.{Effect, Resource} import cats.implicits._ import com.typesafe.config.Config import kamon.Kamon import kamon.context.Context import kamon.instrumentation.http.HttpClientInstrumentation import org.http4s.{Request, Response} import org.http4s.client.Client object KamonSupport { private var _instrumentation = instrumentation(Kamon.config()) private def instrumentation(kamonConfig: Config): HttpClientInstrumentation = { val httpClientConfig = kamonConfig.getConfig("kamon.instrumentation.http4s.client") HttpClientInstrumentation.from(httpClientConfig, "http4s.client") } Kamon.onReconfigure(newConfig => _instrumentation = instrumentation(newConfig)) def apply[F[_]](underlying: Client[F])(implicit F:Effect[F]): Client[F] = Client { request => for { ctx <- Resource.liftF(F.delay(Kamon.currentContext())) k <- kamonClient(underlying)(request)(ctx)(_instrumentation) } yield k } private def kamonClient[F[_]](underlying: Client[F]) (request: Request[F]) (ctx: Context) (instrumentation: HttpClientInstrumentation) (implicit F:Effect[F]): Resource[F, Response[F]] = for { requestHandler <- Resource.liftF(F.delay(instrumentation.createHandler(getRequestBuilder(request), ctx))) response <- underlying.run(requestHandler.request).attempt trackedResponse <- Resource.liftF(handleResponse(response, requestHandler, instrumentation.settings)) } yield trackedResponse def handleResponse[F[_]]( response: Either[Throwable, Response[F]], requestHandler: HttpClientInstrumentation.RequestHandler[Request[F]], settings: HttpClientInstrumentation.Settings )(implicit F:Effect[F]): F[Response[F]] = response match { case Right(res) => if(res.status.code == 404) requestHandler.span.name(settings.defaultOperationName) requestHandler.processResponse(getResponseBuilder(res)) F.delay(res) case Left(error) => requestHandler.span.fail(error).finish() F.raiseError(error) } }
Example 20
Source File: Server.scala From zio-metrics with Apache License 2.0 | 5 votes |
package zio.metrics.dropwizard import scala.util.Properties.envOrNone import cats.data.Kleisli import org.http4s.server.blaze._ import org.http4s.{ Request, Response } import zio.{ RIO, ZIO } import zio.system.System import zio.clock.Clock import zio.console.Console import zio.random.Random import zio.blocking.Blocking import zio.interop.catz._ import io.circe.Json import org.http4s.circe._ import org.http4s.dsl.impl.Root import org.http4s.dsl.io._ import org.http4s.{ HttpRoutes, Response } import zio.RIO import zio.interop.catz._ import zio.metrics.dropwizard.typeclasses._ import zio.metrics.dropwizard.DropwizardExtractor._ import cats.instances.list._ import com.codahale.metrics.MetricRegistry object Server { val port: Int = envOrNone("HTTP_PORT").fold(9090)(_.toInt) type HttpEnvironment = Clock with Console with System with Random with Blocking type HttpTask[A] = RIO[HttpEnvironment, A] type KleisliApp = Kleisli[HttpTask, Request[HttpTask], Response[HttpTask]] //type HttpApp[R <: Registry] = R => KleisliApp def builder[Ctx]: KleisliApp => HttpTask[Unit] = (app: KleisliApp) => ZIO .runtime[HttpEnvironment] .flatMap { implicit rts => BlazeServerBuilder[HttpTask] .bindHttp(port) .withHttpApp(app) .serve .compile .drain } def serveMetrics: MetricRegistry => HttpRoutes[Server.HttpTask] = registry => HttpRoutes.of[Server.HttpTask] { case GET -> Root / filter => { println(s"filter: $filter") val optFilter = if (filter == "ALL") None else Some(filter) RegistryPrinter .report[List, Json](registry, optFilter)( (k: String, v: Json) => Json.obj((k, v)) ) .map(m => Response[Server.HttpTask](Ok).withEntity(m)) } } }
Example 21
Source File: HttpAttributes.scala From opencensus-scala with Apache License 2.0 | 5 votes |
package io.opencensus.scala.http4s import io.opencensus.scala.http.{RequestExtractor, ResponseExtractor} import org.http4s.util.CaseInsensitiveString import org.http4s.{Request, Response} private[http4s] object HttpAttributes { implicit def requestExtractor[F[_]]: RequestExtractor[Request[F]] = new RequestExtractor[Request[F]] { override def method(req: Request[F]): String = req.method.name override def userAgent(req: Request[F]): Option[String] = req.headers.get(CaseInsensitiveString("User-Agent")).map(_.value) override def path(req: Request[F]): String = req.uri.path.toString override def host(req: Request[F]): String = { val hostHeader = req.headers .get(CaseInsensitiveString("Host")) .map(_.value) req.uri.authority .map(_.host.value) .getOrElse( hostHeader // Having no Host header with a relative URL is invalid according to rfc2616, // but http4s still allows to create such HttpRequests. .getOrElse("") ) } } implicit def responseExtractor[F[_]]: ResponseExtractor[Response[F]] = (res: Response[F]) => res.status.code.toLong }
Example 22
Source File: TracingUtils.scala From opencensus-scala with Apache License 2.0 | 5 votes |
package io.opencensus.scala.http4s import cats.effect.Effect import io.opencensus.scala.Tracing import io.opencensus.scala.http.{ StatusTranslator, HttpAttributes => BaseHttpAttributes } import io.opencensus.scala.http4s.HttpAttributes._ import io.opencensus.trace.Span import org.http4s.Response object TracingUtils { def recordResponse[F[_]: Effect](span: Span, tracing: Tracing)( response: Response[F] ): Response[F] = { BaseHttpAttributes.setAttributesForResponse(span, response) tracing.setStatus(span, StatusTranslator.translate(response.status.code)) response.copy( body = response.body.onFinalize(Effect[F].delay(tracing.endSpan(span))) ) } }
Example 23
Source File: TracingClient.scala From opencensus-scala with Apache License 2.0 | 5 votes |
package io.opencensus.scala.http4s import cats.effect.{Effect, Resource} import cats.implicits._ import io.opencensus.scala.Tracing import io.opencensus.scala.http.propagation.Propagation import io.opencensus.scala.http.{HttpAttributes => BaseHttpAttributes} import io.opencensus.scala.http4s.HttpAttributes._ import io.opencensus.scala.http4s.TracingUtils.recordResponse import io.opencensus.scala.http4s.propagation.Http4sFormatPropagation import io.opencensus.trace.{Span, Status} import org.http4s.client.Client import org.http4s.{Header, Request, Response} abstract class TracingClient[F[_]: Effect] { protected val tracing: Tracing protected val propagation: Propagation[Header, Request[F]] def trace(client: Client[F], parentSpan: Option[Span] = None): Client[F] = { val tracedOpen: Request[F] => Resource[F, Response[F]] = req => for { span <- Resource.liftF(startSpan(parentSpan, req)) enrichedReq = addTraceHeaders(req, span) res <- client .run(enrichedReq) .onError(traceError(span).andThen(x => Resource.liftF(x))) } yield recordResponse(span, tracing)(res) Client(tracedOpen) } private def traceError(span: Span): PartialFunction[Throwable, F[Unit]] = { case _ => recordException(span) } private def startSpan(parentSpan: Option[Span], req: Request[F]) = Effect[F].delay(startAndEnrichSpan(req, parentSpan)) private def startAndEnrichSpan( req: Request[F], parentSpan: Option[Span] ): Span = { val name = req.uri.path.toString val span = parentSpan.fold(tracing.startSpan(name))(span => tracing.startSpanWithParent(name, span) ) BaseHttpAttributes.setAttributesForRequest(span, req) span } private def addTraceHeaders(request: Request[F], span: Span): Request[F] = request.withHeaders( request.headers.put(propagation.headersWithTracingContext(span): _*) ) private def recordException(span: Span) = Effect[F].delay(tracing.endSpan(span, Status.INTERNAL)) } object TracingClient { def apply[F[_]: Effect]: TracingClient[F] = new TracingClient[F] { override protected val tracing: Tracing = Tracing override protected val propagation: Propagation[Header, Request[F]] = new Http4sFormatPropagation[F] {} } }
Example 24
Source File: Http4sAttributesSpec.scala From opencensus-scala with Apache License 2.0 | 5 votes |
package io.opencensus.scala.http4s import cats.Id import io.opencensus.scala.http.HttpAttributesSpec import org.http4s.{Header, Headers, Request, Response, Status, Uri} class Http4sAttributesSpec extends HttpAttributesSpec { import HttpAttributes._ "Http4s attributes extraction" should behave like httpAttributes( request, response ) def request: BuildRequest => Request[Id] = (request: BuildRequest) => Request[Id]( uri = Uri.unsafeFromString(request.host ++ request.path), headers = Headers( List(Header("User-Agent", request.userAgent)) ++ request.hostHeader .map(Header("Host", _)) ) ) def response: Int => Response[Id] = (code: Int) => Response[Id](Status(code)) }
Example 25
Source File: http4s.scala From sup with Apache License 2.0 | 5 votes |
package sup.modules import cats.effect.Sync import cats.Monad import cats.Reducible import org.http4s.dsl.Http4sDsl import org.http4s.EntityEncoder import org.http4s.HttpRoutes import org.http4s.Response import sup.HealthCheck import sup.HealthResult import cats.implicits._ object http4s { def healthCheckRoutes[F[_]: Sync, H[_]: Reducible]( healthCheck: HealthCheck[F, H], path: String = "health-check" )( implicit encoder: EntityEncoder[F, HealthResult[H]] ): HttpRoutes[F] = { val dsl = new Http4sDsl[F] {} import dsl._ HttpRoutes.of[F] { case GET -> Root / `path` => healthCheckResponse(healthCheck) } } def healthCheckResponse[F[_]: Monad, H[_]: Reducible]( healthCheck: HealthCheck[F, H] )( implicit encoder: EntityEncoder[F, HealthResult[H]] ): F[Response[F]] = { val dsl = new Http4sDsl[F] {} import dsl._ healthCheck.check.flatMap { check => if (check.value.reduce.isHealthy) Ok(check) else ServiceUnavailable(check) } } }
Example 26
Source File: JdkHttpClient.scala From http4s-jdk-http-client with Apache License 2.0 | 5 votes |
package org.http4s.client.jdkhttpclient import java.net.URI import java.net.http.HttpRequest.BodyPublishers import java.net.http.HttpResponse.BodyHandlers import java.net.http.{HttpClient, HttpRequest, HttpResponse} import java.nio.ByteBuffer import java.util import java.util.concurrent.Flow import cats.ApplicativeError import cats.effect._ import cats.implicits._ import fs2.concurrent.SignallingRef import fs2.interop.reactivestreams._ import fs2.{Chunk, Stream} import org.http4s.client.Client import org.http4s.client.jdkhttpclient.compat.CollectionConverters._ import org.http4s.internal.fromCompletionStage import org.http4s.util.CaseInsensitiveString import org.http4s.{Header, Headers, HttpVersion, Request, Response, Status} import org.reactivestreams.FlowAdapters object JdkHttpClient { def simple[F[_]](implicit F: ConcurrentEffect[F], CS: ContextShift[F]): F[Client[F]] = F.delay(HttpClient.newHttpClient()).map(apply(_)) def convertHttpVersionFromHttp4s[F[_]]( version: HttpVersion )(implicit F: ApplicativeError[F, Throwable]): F[HttpClient.Version] = version match { case HttpVersion.`HTTP/1.1` => HttpClient.Version.HTTP_1_1.pure[F] case HttpVersion.`HTTP/2.0` => HttpClient.Version.HTTP_2.pure[F] case _ => F.raiseError(new IllegalArgumentException("invalid HTTP version")) } // see jdk.internal.net.http.common.Utils#DISALLOWED_HEADERS_SET private val restrictedHeaders = Set( "connection", "content-length", "date", "expect", "from", "host", "upgrade", "via", "warning" ).map(CaseInsensitiveString(_)) }
Example 27
Source File: package.scala From tsec with MIT License | 5 votes |
package tsec import cats.data.OptionT import org.bouncycastle.util.encoders.Hex import org.http4s.server.Middleware import org.http4s.{Request, Response} import tsec.common.ManagedRandom package object csrf { type CSRFToken = CSRFToken.Token object CSRFToken extends ManagedRandom { type Token <: String def apply(s: String): CSRFToken = s.asInstanceOf[CSRFToken] def subst[F[_]](value: F[String]): F[CSRFToken] = value.asInstanceOf[F[CSRFToken]] def generateHexBase(tokenLength: Int = 32): String = { val tokenBytes = new Array[Byte](tokenLength) nextBytes(tokenBytes) Hex.toHexString(tokenBytes) } } type CSRFMiddleware[F[_]] = Middleware[OptionT[F, ?], Request[F], Response[F], Request[F], Response[F]] }
Example 28
Source File: HmacAuthMiddleware.scala From iotchain with MIT License | 5 votes |
package jbok.network.http.server.middleware import java.time.{Duration, Instant} import cats.data.{Kleisli, OptionT} import cats.effect.Sync import jbok.network.http.server.authentication.HMAC import org.http4s.headers.Authorization import org.http4s.util.CaseInsensitiveString import org.http4s.{AuthScheme, Credentials, HttpRoutes, Request, Response, Status} import tsec.mac.jca.{HMACSHA256, MacSigningKey} import scala.concurrent.duration.{FiniteDuration, _} sealed abstract class HmacAuthError(val message: String) extends Exception(message) object HmacAuthError { case object NoAuthHeader extends HmacAuthError("Could not find an Authorization header") case object NoDatetimeHeader extends HmacAuthError("Could not find an X-Datetime header") case object BadMAC extends HmacAuthError("Bad MAC") case object InvalidMacFormat extends HmacAuthError("The MAC is not a valid Base64 string") case object InvalidDatetime extends HmacAuthError("The datetime is not a valid UTC datetime string") case object Timeout extends HmacAuthError("The request time window is closed") } object HmacAuthMiddleware { val defaultDuration: FiniteDuration = 5.minutes private def verifyFromHeader[F[_]]( req: Request[F], key: MacSigningKey[HMACSHA256], duration: FiniteDuration ): Either[HmacAuthError, Unit] = for { authHeader <- req.headers .get(Authorization) .flatMap { t => t.credentials match { case Credentials.Token(scheme, token) if scheme == AuthScheme.Bearer => Some(token) case _ => None } } .toRight(HmacAuthError.NoAuthHeader) datetimeHeader <- req.headers .get(CaseInsensitiveString("X-Datetime")) .toRight(HmacAuthError.NoDatetimeHeader) instant <- HMAC.http.verifyFromHeader( req.method.name, req.uri.renderString, datetimeHeader.value, authHeader, key ) _ <- Either.cond( Instant.now().isBefore(instant.plus(Duration.ofNanos(duration.toNanos))), (), HmacAuthError.Timeout ) } yield () def apply[F[_]: Sync](key: MacSigningKey[HMACSHA256], duration: FiniteDuration = defaultDuration)(routes: HttpRoutes[F]): HttpRoutes[F] = Kleisli { req: Request[F] => verifyFromHeader(req, key, duration) match { case Left(error) => OptionT.some[F](Response[F](Status.Forbidden).withEntity(error.message)) case Right(_) => routes(req) } } }
Example 29
Source File: KamonSupport.scala From kamon-http4s with Apache License 2.0 | 5 votes |
package kamon.http4s package middleware.server import cats.data.{Kleisli, OptionT} import cats.effect.{Resource, Sync} import cats.implicits._ import kamon.Kamon import kamon.context.Storage import kamon.instrumentation.http.HttpServerInstrumentation.RequestHandler import kamon.instrumentation.http.HttpServerInstrumentation import org.http4s.{HttpRoutes, Request, Response} object KamonSupport { def apply[F[_]: Sync](service: HttpRoutes[F], interface: String, port: Int): HttpRoutes[F] = { val httpServerConfig = Kamon.config().getConfig("kamon.instrumentation.http4s.server") val instrumentation = HttpServerInstrumentation.from(httpServerConfig, "http4s.server", interface, port) Kleisli(kamonService[F](service, instrumentation)(_)) } private def kamonService[F[_]](service: HttpRoutes[F], instrumentation: HttpServerInstrumentation) (request: Request[F]) (implicit F: Sync[F]): OptionT[F, Response[F]] = OptionT { getHandler(instrumentation)(request).use { handler => for { resOrUnhandled <- service(request).value.attempt respWithContext <- kamonServiceHandler(handler, resOrUnhandled, instrumentation.settings) } yield respWithContext } } private def processRequest[F[_]](requestHandler: RequestHandler)(implicit F: Sync[F]): Resource[F, RequestHandler] = Resource.make(F.delay(requestHandler.requestReceived()))(h => F.delay(h.responseSent())) private def withContext[F[_]](requestHandler: RequestHandler)(implicit F: Sync[F]): Resource[F, Storage.Scope] = Resource.make(F.delay(Kamon.storeContext(requestHandler.context)))( scope => F.delay(scope.close())) private def getHandler[F[_]](instrumentation: HttpServerInstrumentation)(request: Request[F])(implicit F: Sync[F]): Resource[F, RequestHandler] = for { handler <- Resource.liftF(F.delay(instrumentation.createHandler(buildRequestMessage(request)))) _ <- processRequest(handler) _ <- withContext(handler) } yield handler private def kamonServiceHandler[F[_]](requestHandler: RequestHandler, e: Either[Throwable, Option[Response[F]]], settings: HttpServerInstrumentation.Settings) (implicit F: Sync[F]): F[Option[Response[F]]] = e match { case Left(e) => F.delay { requestHandler.span.fail(e.getMessage) Some(requestHandler.buildResponse(errorResponseBuilder, requestHandler.context)) } *> F.raiseError(e) case Right(None) => F.delay { requestHandler.span.name(settings.unhandledOperationName) val response: Response[F] = requestHandler.buildResponse[Response[F]]( notFoundResponseBuilder, requestHandler.context ) Some(response) } case Right(Some(response)) => F.delay { val a = requestHandler.buildResponse(getResponseBuilder(response), requestHandler.context) Some(a) } } }
Example 30
Source File: package.scala From temperature-machine with Apache License 2.0 | 5 votes |
package bad.robot.temperature import cats.effect.IO import org.http4s.{Header, Response, Status} import org.specs2.matcher.{Expectable, MatchResult, Matcher} package object test { def haveStatus(status: Status) = new Matcher[Response[IO]] { def apply[S <: Response[IO]](e: Expectable[S]): MatchResult[S] = result( e.value.status == status, s"""Status of [${e.value.status}] | |is | |$status""".stripMargin, s"""Status of |[${e.value.status}] | |is not | |[$status] | |(${e.value.as[String]})""".stripMargin, e) } def containsHeader(name: String, value: String) = new Matcher[Response[IO]] { def apply[S <: Response[IO]](e: Expectable[S]): MatchResult[S] = result( e.value.headers.toList.map(_.toRaw) contains Header(name, value), s"""${e.value.headers} | |contains | |$name""".stripMargin, s"""The response headers '${e.value.headers.toList.mkString("\n")}' | |do not contain | |[$name: $value] |""".stripMargin, e) } }
Example 31
Source File: Service.scala From Learn-Scala-Programming with MIT License | 5 votes |
package ch14 import cats.effect.IO import ch14.Model.{Inventory, Purchase, Restock} import org.http4s.{HttpService, MediaType, Response} import org.http4s.dsl.Http4sDsl import org.http4s.circe._ import io.circe.generic.auto._ import io.circe.syntax._ import org.http4s.headers.`Content-Type` import fs2.Stream class Service(repo: Repository) extends Http4sDsl[IO] { val service = HttpService[IO] { case DELETE -> Root / "articles" / name => repo.deleteArticle(name).flatMap { if (_) NoContent() else NotFound() } case POST -> Root / "articles" / name => repo.createArticle(name).flatMap { if (_) NoContent() else Conflict() } case GET -> Root / "articles" / name => renderInventory(repo.getArticle(name)) case req @ POST -> Root / "purchase" => val changes: Stream[IO, Inventory] = for { purchase <- Stream.eval(req.decodeJson[Purchase]) before <- repo.getInventory _ <- repo.updateStock(purchase.inventory) after <- repo.getInventory } yield diff(purchase.order.keys, before, after) renderInventory(changes) case req @ POST -> Root / "restock" => val newState = for { purchase <- Stream.eval(req.decodeJson[Restock]) _ <- repo.updateStock(purchase.inventory) inventory <- repo.getInventory } yield inventory renderInventory(newState) case GET -> Root / "inventory" => getInventory } private def diff[A](keys: Iterable[A], before: Map[A, Int], after: Map[A, Int]): Map[A, Int] = keys.filter(before.contains).map { key => key -> (before(key) - after(key)) }.toMap private def getInventory: IO[Response[IO]] = renderInventory(repo.getInventory) private def renderInventory(inventory: Stream[IO, Inventory]) = Ok(inventory.map(_.asJson.noSpaces), `Content-Type`(MediaType.`application/json`)) }
Example 32
Source File: HttpErrorHandler.scala From codepropertygraph with Apache License 2.0 | 5 votes |
package io.shiftleft.cpgserver.route import cats.data.{Kleisli, OptionT} import cats.effect.IO import org.http4s.{HttpRoutes, Request, Response} trait HttpErrorHandler { def handle(routes: HttpRoutes[IO]): HttpRoutes[IO] } object HttpErrorHandler { def apply(routes: HttpRoutes[IO])(handler: PartialFunction[Throwable, IO[Response[IO]]]): HttpRoutes[IO] = { Kleisli { req: Request[IO] => OptionT { routes.run(req).value.handleErrorWith { e => if (handler.isDefinedAt(e)) handler(e).map(Option(_)) else IO.raiseError(e) } } } } }
Example 33
Source File: Http4sSpec.scala From codepropertygraph with Apache License 2.0 | 5 votes |
package io.shiftleft.cpgserver.route import cats.effect.IO import org.http4s.{EntityDecoder, Response, Status} import io.shiftleft.cpgserver.BaseSpec trait Http4sSpec extends BaseSpec { // Helpfully lifted from https://http4s.org/v0.20/testing/ def check[A](actual: IO[Response[IO]], expectedStatus: Status, expectedBody: Option[A] = None)( implicit ev: EntityDecoder[IO, A] ): Boolean = { val actualResp = actual.unsafeRunSync val statusCheck = actualResp.status == expectedStatus val bodyCheck = expectedBody.fold[Boolean](actualResp.body.compile.toVector.unsafeRunSync.isEmpty)( // Verify Response's body is empty. expected => actualResp.as[A].unsafeRunSync == expected) statusCheck && bodyCheck } }
Example 34
Source File: ResponseGenerator.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.restserver.http4s import cats.Applicative import cats.implicits._ import org.http4s.dsl.Http4sDsl import org.http4s.{EntityEncoder, Header, Response} trait ResponseGenerator[F[_]] { self: Http4sDsl[F] => implicit final class EitherResponses[A, B](e: Either[A, B]) { def toResponse(headers: Header*)(implicit F: Applicative[F], w0: EntityEncoder[F, A], w1: EntityEncoder[F, B] ): F[Response[F]] = e.fold( a => UnprocessableEntity(a), b => Ok(b) ).map(_.withHeaders(headers: _*)) } implicit final class OptionResponse[A](o: Option[A]) { def toResponse( headers: Header* )(implicit F: Applicative[F], w0: EntityEncoder[F, A]): F[Response[F]] = o.map(a => Ok(a)).getOrElse(NotFound()).map(_.withHeaders(headers: _*)) } } object ResponseGenerator {}
Example 35
Source File: AuthTracedHttpRoute.scala From http4s-tracer with Apache License 2.0 | 5 votes |
package dev.profunktor.tracer.auth import cats.Monad import cats.data.{Kleisli, OptionT} import cats.syntax.flatMap._ import cats.syntax.functor._ import dev.profunktor.tracer.Tracer import dev.profunktor.tracer.Tracer.TraceId import org.http4s.{AuthedRequest, AuthedRoutes, Response} object AuthTracedHttpRoute { case class AuthTracedRequest[F[_], T](traceId: TraceId, request: AuthedRequest[F, T]) def apply[T, F[_]: Monad: Tracer]( pf: PartialFunction[AuthTracedRequest[F, T], F[Response[F]]] ): AuthedRoutes[T, F] = Kleisli[OptionT[F, ?], AuthedRequest[F, T], Response[F]] { req => OptionT { Tracer[F] .getTraceId(req.req) .map(x => AuthTracedRequest[F, T](x.getOrElse(TraceId("-")), req)) .flatMap { tr => val rs: OptionT[F, Response[F]] = pf.andThen(OptionT.liftF(_)).applyOrElse(tr, Function.const(OptionT.none)) rs.value } } } }