All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hammock.asynchttpclient.AsyncHttpClientInterpreter.scala Maven / Gradle / Ivy

package hammock
package asynchttpclient

import cats._
import cats.implicits._
import cats.data.Kleisli
import cats.effect._
import org.asynchttpclient._
import java.util.{concurrent => jc}
import scala.util._
import scala.collection.JavaConverters._

object AsyncHttpClientInterpreter {

  def apply[F[_]](implicit F: InterpTrans[F]): InterpTrans[F] = F

  implicit def instance[F[_]: Async](
      implicit client: AsyncHttpClient = new DefaultAsyncHttpClient()
  ): InterpTrans[F] = new InterpTrans[F] {
    override def trans: HttpF ~> F = transK andThen λ[Kleisli[F, AsyncHttpClient, ?] ~> F](_.run(client))
  }

  def transK[F[_]: Async]: HttpF ~> Kleisli[F, AsyncHttpClient, ?] = {

    def toF[A](future: jc.Future[A]): F[A] =
      Async[F].async(_(Try(future.get) match {
        case Failure(err) => Left(err)
        case Success(a)   => Right(a)
      }))

    λ[HttpF ~> Kleisli[F, AsyncHttpClient, ?]] {
      case reqF @ (Get(_) | Options(_) | Delete(_) | Head(_) | Options(_) | Trace(_) | Post(_) | Put(_) | Patch(_)) =>
        Kleisli { implicit client =>
          for {
            req             <- mapRequest[F](reqF)
            ahcResponse     <- toF(req.execute())
            hammockResponse <- mapResponse[F](ahcResponse)
          } yield hammockResponse
        }
    }
  }

  def mapRequest[F[_]: Async](reqF: HttpF[HttpResponse])(implicit client: AsyncHttpClient): F[BoundRequestBuilder] = {

    def putHeaders(req: BoundRequestBuilder, headers: Map[String, String]): F[Unit] =
      Async[F].delay {
        req.setSingleHeaders(headers.map(kv => kv._1.asInstanceOf[CharSequence] -> kv._2).asJava)
      } *> ().pure[F]

    def getBuilder(reqF: HttpF[HttpResponse]): BoundRequestBuilder = reqF match {
      case Get(_)     => client.prepareGet(reqF.req.uri.show)
      case Delete(_)  => client.prepareDelete(reqF.req.uri.show)
      case Head(_)    => client.prepareHead(reqF.req.uri.show)
      case Options(_) => client.prepareOptions(reqF.req.uri.show)
      case Post(_)    => client.preparePost(reqF.req.uri.show)
      case Put(_)     => client.preparePut(reqF.req.uri.show)
      case Trace(_)   => client.prepareTrace(reqF.req.uri.show)
      case Patch(_)   => client.preparePatch(reqF.req.uri.show)
    }

    for {
      req <- getBuilder(reqF).pure[F]
      _   <- putHeaders(req, reqF.req.headers)
      _ = reqF.req.entity
        .foreach(_.cata(str => req.setBody(str.content), bytes => req.setBody(bytes.content), Function.const(())))
    } yield req
  }

  def mapResponse[F[_]: Applicative](ahcResponse: Response): F[HttpResponse] = {

    def createEntity(r: Response): Entity = r.getContentType match {
      case AsyncHttpClientContentType.`application/octet-stream` => Entity.ByteArrayEntity(r.getResponseBodyAsBytes)
      case _                                                     => Entity.StringEntity(r.getResponseBody)
    }

    HttpResponse(
      Status.Statuses(ahcResponse.getStatusCode),
      ahcResponse.getHeaders.names.asScala.map(name => (name, ahcResponse.getHeaders.get(name))).toMap,
      createEntity(ahcResponse)
    ).pure[F]
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy