如何使用 http4s 服务器和客户端库作为代理



我想使用http4s作为代理(如nginx(,如何将所有数据从我的http4s服务器转发到另一个http服务器?

我真正想做的是在执行转发功能之前在每个请求上附加一个验证函数。希望像这样:

HttpService[IO] {
  case request =>
    val httpClient: Client[IO] = Http1Client[IO]().unsafeRunSync
    if(verifySuccess(request)) { // forward all http data to host2 and
                                 // get a http response.
      val result = httpClient.forward(request, "http://host2")
      result
    } else {
      Forbidden //403
    }
}

如何使用http4s及其客户端执行此操作?
谢谢

更新

在@TheInnerLight的帮助下,我尝试了一下代码片段:

  val httpClient = Http1Client[IO]()
  val service: HttpService[IO] = HttpService[IO] {
    case req =>
      if(true) {
        for {
          client <- httpClient
          newAuthority = req.uri.authority.map(_.copy(host = RegName("scala-lang.org"), port = Some(80)))
          proxiedReq = req.withUri(req.uri.copy(authority = newAuthority))
          response <- client.fetch(proxiedReq)(IO.pure(_))
        } yield response
      } else {
        Forbidden("Some forbidden message...")
      }
  }

请求:http://localhost:28080(http4s 服务器侦听 28080(:
但发生错误:

[ERROR] org.http4s.client.PoolManager:102 - Error establishing client connection for key RequestKey(Scheme(http),localhost) 
java.net.ConnectException: Connection refused
    at sun.nio.ch.UnixAsynchronousSocketChannelImpl.checkConnect(Native Method)
    at sun.nio.ch.UnixAsynchronousSocketChannelImpl.finishConnect(UnixAsynchronousSocketChannelImpl.java:252)
    at sun.nio.ch.UnixAsynchronousSocketChannelImpl.finish(UnixAsynchronousSocketChannelImpl.java:198)
    at sun.nio.ch.UnixAsynchronousSocketChannelImpl.onEvent(UnixAsynchronousSocketChannelImpl.java:213)
    at sun.nio.ch.KQueuePort$EventHandlerTask.run(KQueuePort.java:301)
    at java.lang.Thread.run(Thread.java:748)
[ERROR] org.http4s.server.service-errors:88 - Error servicing request: GET / from 0:0:0:0:0:0:0:1 
java.net.ConnectException: Connection refused
    at sun.nio.ch.UnixAsynchronousSocketChannelImpl.checkConnect(Native Method)
    at sun.nio.ch.UnixAsynchronousSocketChannelImpl.finishConnect(UnixAsynchronousSocketChannelImpl.java:252)
    at sun.nio.ch.UnixAsynchronousSocketChannelImpl.finish(UnixAsynchronousSocketChannelImpl.java:198)
    at sun.nio.ch.UnixAsynchronousSocketChannelImpl.onEvent(UnixAsynchronousSocketChannelImpl.java:213)
    at sun.nio.ch.KQueuePort$EventHandlerTask.run(KQueuePort.java:301)
    at java.lang.Thread.run(Thread.java:748)

最新版本

 val httpClient: IO[Client[IO]] = Http1Client[IO]()
  override val service: HttpService[IO] = HttpService[IO] {
    case req =>
      val hostName = "scala-lang.org"
      val myPort = 80
      if(true) {
        val newHeaders = {
          val filterHeader = req.headers.filterNot{h =>
            h.name == CaseInsensitiveString("Connection") ||
            h.name == CaseInsensitiveString("Keep-Alive") ||
            h.name == CaseInsensitiveString("Proxy-Authenticate") ||
            h.name == CaseInsensitiveString("Proxy-Authorization") ||
            h.name == CaseInsensitiveString("TE") ||
            h.name == CaseInsensitiveString("Trailer") ||
            h.name == CaseInsensitiveString("Transfer-Encoding") ||
            h.name == CaseInsensitiveString("Upgrade")
          }
          filterHeader.put(Header("host", hostName))
        }
        for {
          client <- httpClient
          newAuthority = req.uri.authority
            .map(_.copy(host = RegName(hostName), port = Some(myPort)))
            .getOrElse( Authority(host = RegName(hostName), port = Some(myPort)))
          proxiedReq = req.withUri(req.uri.copy(authority = Some(newAuthority)))
            .withHeaders(newHeaders)
          response <- client.fetch(proxiedReq)(x => IO.pure(x))
        } yield {
          val rst = response
          rst
        }
      } else {
        Forbidden("Some forbidden message...")
      }
  }

它对于我的 REST API Web 服务器来说足够好用。
代理scala-lang.org进行测试时出现一些错误:

[ERROR] org.http4s.blaze.pipeline.Stage:226 - Error writing body 
org.http4s.InvalidBodyException: Received premature EOF.

像这样的事情怎么样:

HttpService[IO] {
  case req =>
    if(verifyRequest(req)) {
      for {
        client <- Http1Client[IO]()
        newHost = "host2"
        newAuthority = Authority(host = RegName("host2"), port = Some(80))
        proxiedReq =
          req.withUri(req.uri.copy(authority = Some(newAuthority)))
           .withHeaders(req.headers.put(Header("host", newHost)))
        response <- client.fetch(proxiedReq)(IO.pure(_))
      } yield response
    } else {
      Forbidden("Some forbidden message...")
    }
}

请注意,您绝对应该避免在代码中乱扔对 unsafeRunSync 的调用。 您通常应该在程序中最多使用它一次(Main年(。 在其他情况下,您应该专注于将效果提升到您正在工作的单体中。

最新更新