org.http4s.util.CaseInsensitiveString Scala Examples

The following examples show how to use org.http4s.util.CaseInsensitiveString. 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: HmacAuthMiddleware.scala    From iotchain   with MIT License 5 votes vote down vote up
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 2
Source File: HttpAttributes.scala    From opencensus-scala   with Apache License 2.0 5 votes vote down vote up
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 3
Source File: JdkHttpClient.scala    From http4s-jdk-http-client   with Apache License 2.0 5 votes vote down vote up
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 4
Source File: AuthedHttp4s.scala    From pure-movie-server   with Apache License 2.0 5 votes vote down vote up
package pms.algebra.http

import pms.core.Fail
import pms.effects._
import pms.effects.implicits._
import pms.algebra.user._

import org.http4s._
import org.http4s.dsl._
import org.http4s.server._
import org.http4s.util.CaseInsensitiveString


object AuthedHttp4s {

  def userTokenAuthMiddleware[F[_]: Async](authAlgebra: UserAuthAlgebra[F]): AuthMiddleware[F, AuthCtx] = {
    val tokenVerification: Kleisli[F, Request[F], Attempt[AuthCtx]] = verifyToken[F](authAlgebra)
    AuthMiddleware(tokenVerification, onFailure)
  }

  private val `X-Auth-Token` = CaseInsensitiveString("X-AUTH-TOKEN")

  private val challenges: NonEmptyList[Challenge] = NonEmptyList.of(
    Challenge(
      scheme = "Basic",
      realm  = "Go to POST /pms/api/user/login to get valid token",
    )
  )

  private val wwwHeader = headers.`WWW-Authenticate`(challenges)

  private def onFailure[F[_]: Async]: AuthedRoutes[Throwable, F] =
    Kleisli[OptionT[F, *], AuthedRequest[F, Throwable], Response[F]] { _: AuthedRequest[F, Throwable] =>
      val fdsl = Http4sDsl[F]
      import fdsl._
      OptionT.liftF[F, Response[F]](Unauthorized(wwwHeader))
    }

  private def verifyToken[F[_]: Async](authAlgebra: UserAuthAlgebra[F]): Kleisli[F, Request[F], Attempt[AuthCtx]] =
    Kleisli { req: Request[F] =>
      val optHeader = req.headers.get(`X-Auth-Token`)
      optHeader match {
        case None         =>
          Attempt
            .raiseError[AuthCtx](Fail.unauthorized(s"No ${`X-Auth-Token`} provided"))
            .pure[F]
        case Some(header) =>
          authAlgebra
            .authenticate(AuthenticationToken(header.value))
            .map(Attempt.pure)
            .handleError(Attempt.raiseError)
      }
    }
} 
Example 5
Source File: SessionManagerSpec.scala    From pizza-auth-3   with MIT License 5 votes vote down vote up
package moe.pizza.auth.webapp

import moe.pizza.auth.interfaces.UserDatabase
import org.http4s._
import org.http4s.dsl._
import org.http4s.util.CaseInsensitiveString
import org.scalatest.mock.MockitoSugar
import org.scalatest.{FlatSpec, MustMatchers}
import moe.pizza.auth.webapp.Utils._

class SessionManagerSpec extends FlatSpec with MustMatchers with MockitoSugar {

  val emptyservice = HttpService {
    case req @ GET -> Root =>
      Ok(req.getSession.toString)
    case req @ GET -> Root / "flash" =>
      val newsession = req.flash(Alerts.info, "this is an alert")
      Ok(req.getSession.toString).attachSessionifDefined(newsession)
    case req @ GET -> Root / "logout" =>
      Ok(req.getSession.toString).clearSession()
  }

  val ud = mock[UserDatabase]

  val svc = new SessionManager("keygoeshere", ud).apply(emptyservice)

  "when wrapping a service it" should "add a session cookie" in {
    val r = svc.apply(new Request(uri = Uri.uri("/"))).run
    r.status must equal(Ok)
    val session = r.headers.get(CaseInsensitiveString("set-cookie")).get
    session.value.startsWith("authsession=") must equal(true)
    val bodytxt = EntityDecoder.decodeString(r)(Charset.`UTF-8`).run
    bodytxt must equal("Some(HydratedSession(List(),None,None,None))")
  }

  "when wrapping a service it" should "add a session cookie and use it to store state between calls" in {
    val r = svc.apply(new Request(uri = Uri.uri("/flash"))).run
    r.status must equal(Ok)
    val session = r.headers.get(CaseInsensitiveString("set-cookie")).get
    session.value.startsWith("authsession=") must equal(true)
    val cookie = session.value
    val r2 = svc
      .apply(
        new Request(uri = Uri.uri("/"),
                    headers = Headers(Header("Cookie", cookie))))
      .run
    r.status must equal(Ok)
    val bodytxt = EntityDecoder.decodeString(r2)(Charset.`UTF-8`).run
    bodytxt must equal(
      "Some(HydratedSession(List(Alert(info,this is an alert)),None,None,None))")
  }

  "when wrapping a service it" should "be able to remove sessions" in {
    val r = svc.apply(new Request(uri = Uri.uri("/logout"))).run
    r.status must equal(Ok)
    val removal = r.headers.get(CaseInsensitiveString("set-cookie")).get
    assert(removal.value.startsWith("authsession=;"))
  }

} 
Example 6
Source File: Http4sExtras.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.cli.utils

import cats.implicits._
import ch.epfl.bluebrain.nexus.cli.sse._
import org.http4s.Credentials.Token
import org.http4s.Request
import org.http4s.ServerSentEvent.EventId
import org.http4s.dsl.impl.{/, Root}
import org.http4s.headers.{`Content-Type`, `Last-Event-Id`, Authorization}
import org.http4s.util.CaseInsensitiveString

import scala.util.{Success, Try}


trait Http4sExtras {

  protected class Var[A](cast: String => Try[A]) {
    def unapply(str: String): Option[A] =
      if (!str.isEmpty)
        cast(str).toOption
      else
        None
  }

  object OrgUuidVar     extends Var(str => Try(java.util.UUID.fromString(str)).map(OrgUuid.apply))
  object ProjectUuidVar extends Var(str => Try(java.util.UUID.fromString(str)).map(ProjectUuid.apply))

  object OrgLabelVar     extends Var(str => Success(OrgLabel(str)))
  object ProjectLabelVar extends Var(str => Success(ProjectLabel(str)))

  object optbearer {
    def unapply[F[_]](request: Request[F]): Option[(Request[F], Option[BearerToken])] =
      request.headers.get(Authorization) match {
        case Some(Authorization(Token(authScheme, token))) if authScheme === CaseInsensitiveString("bearer") =>
          Some((request, Some(BearerToken(token))))
        case _                                                                                               => Some((request, None))
      }
  }

  object bearer {
    def unapply[F[_]](request: Request[F]): Option[(Request[F], BearerToken)] =
      optbearer.unapply(request) match {
        case Some((_, Some(token))) => Some((request, token))
        case _                      => None
      }
  }

  object db {
    def unapply[F[_]](request: Request[F]): Option[(Request[F], String)] =
      request.uri.params.get("db").map(request -> _)
  }

  object contentType {
    def unapply[F[_]](request: Request[F]): Option[(Request[F], `Content-Type`)] =
      request.headers.get(`Content-Type`) match {
        case Some(ct: `Content-Type`) => Some((request, ct))
        case _                        => None
      }
  }

  object optLastEventId {
    def unapply[F[_]](request: Request[F]): Option[(Request[F], Option[Offset])] =
      request.headers.get(`Last-Event-Id`) match {
        case Some(`Last-Event-Id`(EventId(value))) => Some((request, Offset(value)))
        case _                                     => Some((request, None))
      }
  }

  object lastEventId {
    def unapply[F[_]](request: Request[F]): Option[(Request[F], Offset)] =
      optLastEventId.unapply(request) match {
        case Some((_, Some(offset))) => Some((request, offset))
        case _                       => None
      }
  }

  val v1: / = Root / "v1"

}

object Http4sExtras extends Http4sExtras 
Example 7
Source File: AuthExampleApp.scala    From caliban   with Apache License 2.0 5 votes vote down vote up
package caliban.http4s

import caliban.GraphQL._
import caliban.schema.GenericSchema
import caliban.{ Http4sAdapter, RootResolver }
import org.http4s.HttpRoutes
import org.http4s.dsl.Http4sDsl
import org.http4s.implicits._
import org.http4s.server.blaze.BlazeServerBuilder
import org.http4s.server.{ Router, ServiceErrorHandler }
import org.http4s.util.CaseInsensitiveString
import zio._
import zio.interop.catz._
import zio.interop.catz.implicits._

import scala.concurrent.ExecutionContext

object AuthExampleApp extends CatsApp {

  // Simple service that returns the token coming from the request
  type Auth = Has[Auth.Service]
  object Auth {
    trait Service {
      def token: String
    }
  }
  type AuthTask[A] = RIO[Auth, A]

  case class MissingToken() extends Throwable

  // http4s middleware that extracts a token from the request and eliminate the Auth layer dependency
  object AuthMiddleware {
    def apply(route: HttpRoutes[AuthTask]): HttpRoutes[Task] =
      Http4sAdapter.provideLayerFromRequest(
        route,
        _.headers.get(CaseInsensitiveString("token")) match {
          case Some(value) => ZLayer.succeed(new Auth.Service { override def token: String = value.value })
          case None        => ZLayer.fail(MissingToken())
        }
      )
  }

  // http4s error handler to customize the response for our throwable
  object dsl extends Http4sDsl[Task]
  import dsl._
  val errorHandler: ServiceErrorHandler[Task] = _ => { case MissingToken() => Forbidden() }

  // our GraphQL API
  val schema: GenericSchema[Auth] = new GenericSchema[Auth] {}
  import schema._
  case class Query(token: RIO[Auth, String])
  private val resolver = RootResolver(Query(ZIO.access[Auth](_.get[Auth.Service].token)))
  private val api      = graphQL(resolver)

  override def run(args: List[String]): ZIO[ZEnv, Nothing, ExitCode] =
    (for {
      interpreter <- api.interpreter
      route       = AuthMiddleware(Http4sAdapter.makeHttpService(interpreter))
      _ <- BlazeServerBuilder[Task](ExecutionContext.global)
            .withServiceErrorHandler(errorHandler)
            .bindHttp(8088, "localhost")
            .withHttpApp(Router[Task]("/api/graphql" -> route).orNotFound)
            .resource
            .toManaged
            .useForever
    } yield ()).exitCode
} 
Example 8
Source File: StatusService.scala    From zio-telemetry   with Apache License 2.0 5 votes vote down vote up
package zio.telemetry.opentelemetry.example.http

import io.circe.Encoder
import io.circe.syntax._
import io.opentelemetry.OpenTelemetry
import io.opentelemetry.context.propagation.HttpTextFormat
import io.opentelemetry.context.propagation.HttpTextFormat.Getter
import io.opentelemetry.trace.Span
import org.http4s._
import org.http4s.circe.jsonEncoderOf
import org.http4s.dsl.Http4sDsl
import org.http4s.util.CaseInsensitiveString
import zio.interop.catz._
import zio.telemetry.opentelemetry.Tracing
import zio.telemetry.opentelemetry.TracingSyntax._
import zio.telemetry.opentelemetry.example.http.{ Status => ServiceStatus }

object StatusService {

  val dsl: Http4sDsl[AppTask] = Http4sDsl[AppTask]
  import dsl._

  implicit def encoder[A: Encoder]: EntityEncoder[AppTask, A] = jsonEncoderOf[AppTask, A]

  val httpTextFormat: HttpTextFormat = OpenTelemetry.getPropagators.getHttpTextFormat
  val getter: Getter[Headers]        = (carrier, key) => carrier.get(CaseInsensitiveString(key)).map(_.value).orNull

  val routes: HttpRoutes[AppTask] = HttpRoutes.of[AppTask] {
    case request @ GET -> Root / "status" =>
      val response = for {
        _        <- Tracing.addEvent("event from backend before response")
        response <- Ok(ServiceStatus.up("backend").asJson)
        _        <- Tracing.addEvent("event from backend after response")
      } yield response

      response.spanFrom(httpTextFormat, request.headers, getter, "/status", Span.Kind.SERVER)

  }

} 
Example 9
Source File: Http4sDecodeInputsContext.scala    From tapir   with Apache License 2.0 5 votes vote down vote up
package sttp.tapir.server.http4s

import org.http4s.Request
import org.http4s.util.CaseInsensitiveString
import sttp.model.{Method, QueryParams}
import sttp.tapir.model.ServerRequest
import sttp.tapir.server.internal.DecodeInputsContext

class Http4sDecodeInputsContext[F[_]](req: Request[F]) extends DecodeInputsContext {
  override def method: Method = Method(req.method.name.toUpperCase)
  override def nextPathSegment: (Option[String], DecodeInputsContext) = {
    val nextStart = req.pathInfo.dropWhile(_ == '/')
    val segment = nextStart.split("/", 2) match {
      case Array("")   => None
      case Array(s)    => Some(s)
      case Array(s, _) => Some(s)
    }

    // if the routes are mounted within a context (e.g. using a router), we have to match against what comes
    // after the context. This information is stored in the the PathInfoCaret attribute
    val oldCaret = req.attributes.lookup(Request.Keys.PathInfoCaret).getOrElse(0)
    val segmentSlashLength = segment.map(_.length).getOrElse(0) + 1
    val reqWithNewCaret = req.withAttribute(Request.Keys.PathInfoCaret, oldCaret + segmentSlashLength)

    (segment, new Http4sDecodeInputsContext(reqWithNewCaret))
  }
  override def header(name: String): List[String] = req.headers.get(CaseInsensitiveString(name)).map(_.value).toList
  override def headers: Seq[(String, String)] = req.headers.toList.map(h => (h.name.value, h.value))
  override def queryParameter(name: String): Seq[String] = queryParameters.getMulti(name).getOrElse(Nil)
  override val queryParameters: QueryParams = QueryParams.fromMultiMap(req.multiParams)
  override def bodyStream: Any = req.body
  override def serverRequest: ServerRequest = new Http4sServerRequest(req)
} 
Example 10
Source File: CorrelationIdMiddleware.scala    From scala-server-toolkit   with MIT License 5 votes vote down vote up
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 11
Source File: CorrelationIdMiddlewareTest.scala    From scala-server-toolkit   with MIT License 5 votes vote down vote up
package com.avast.sst.http4s.server.middleware

import java.net.InetSocketAddress

import cats.effect.{ContextShift, IO, Resource, Timer}
import com.avast.sst.http4s.server.Http4sRouting
import org.http4s.client.blaze.BlazeClientBuilder
import org.http4s.dsl.Http4sDsl
import org.http4s.server.blaze.BlazeServerBuilder
import org.http4s.util.CaseInsensitiveString
import org.http4s.{Header, HttpRoutes, Request, Uri}
import org.scalatest.funsuite.AsyncFunSuite

import scala.concurrent.ExecutionContext

@SuppressWarnings(Array("scalafix:Disable.get", "scalafix:Disable.toString", "scalafix:Disable.createUnresolved"))
class CorrelationIdMiddlewareTest extends AsyncFunSuite with Http4sDsl[IO] {

  implicit private val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global)
  implicit private val timer: Timer[IO] = IO.timer(ExecutionContext.global)

  test("CorrelationIdMiddleware fills Request attributes and HTTP response header") {
    val test = for {
      middleware <- Resource.liftF(CorrelationIdMiddleware.default[IO])
      routes = Http4sRouting.make {
        middleware.wrap {
          HttpRoutes.of[IO] {
            case req @ GET -> Root / "test" =>
              val id = middleware.retrieveCorrelationId(req)
              Ok("test").map(_.withHeaders(Header("Attribute-Value", id.toString)))
          }
        }
      }
      server <- BlazeServerBuilder[IO](ExecutionContext.global)
        .bindSocketAddress(InetSocketAddress.createUnresolved("127.0.0.1", 0))
        .withHttpApp(routes)
        .resource
      client <- BlazeClientBuilder[IO](ExecutionContext.global).resource
    } yield (server, client)

    test
      .use {
        case (server, client) =>
          client
            .run(
              Request[IO](uri = Uri.unsafeFromString(s"http://${server.address.getHostString}:${server.address.getPort}/test"))
                .withHeaders(Header("Correlation-Id", "test-value"))
            )
            .use { response =>
              IO.delay {
                assert(response.headers.get(CaseInsensitiveString("Correlation-Id")).get.value === "test-value")
                assert(response.headers.get(CaseInsensitiveString("Attribute-Value")).get.value === "Some(CorrelationId(test-value))")
              }
            }
      }
      .unsafeToFuture()
  }

}