通过代理

时间:2017-07-28 09:20:23

标签: scala akka-http

我正在使用scalaj到akka-http重写scala中的一些应用程序层代码 为了减少项目中第三方依赖项的数量(我们已经在同一个项目中使用了akka用于其他内容。)代码只是将常见类型的请求包装到库提供的基础常规请求中

大多数情况下一切都很好,但我仍然坚持可选择为请求添加代理的问题。

请求应直接发送到目标或通过代理,由运行时的参数确定。

在我的scalaj实现中,我有以下帮助器类和方法

object HttpUtils {
  private def request(
               host: Host,
               method: HttpMethod,
               params: Map[String, String],
               postData: Option[String],
               timeout: Duration,
               headers: Seq[(String, String)],
               proxy: Option[ProxyConfig]
             ): HttpResponse[String] = {
    // most general request builder. Other methods in the object fill in parameters and wrap this in a Future
    val baseRequest = Http(host.url)
    val proxiedRequest = addProxy(proxy, baseRequest)
    val fullRequest = addPostData(postData)(proxiedRequest)
      .method(method.toString)
      .params(params)
      .headers(headers)
      .option(HttpOptions.connTimeout(timeout.toMillis.toInt))
      .option(HttpOptions.readTimeout(timeout.toMillis.toInt))
    fullRequest.asString  // scalaj for send off request and block until response
  }

      // Other methods ...

   private def addProxy(proxy: Option[ProxyConfig], request: HttpRequest): HttpRequest =
     proxy.fold(request)((p: ProxyConfig) => request.proxy(p.host, p.port))
}

case class ProxyConfig(host: String, port: Int)

有没有办法用akka-http构建类似的构造?

2 个答案:

答案 0 :(得分:1)

Akka HTTP确实有proxy support,从版本10.0.9开始,仍然不稳定。请记住,API可能会发生变化,您可以执行以下操作来处理可选的代理设置:

import java.net.InetSocketAddress

import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.http.scaladsl.{ClientTransport, Http}

implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()

case class ProxyConfig(host: String, port: Int)

val proxyConfig = Option(ProxyConfig("localhost", 8888))
val clientTransport =
  proxyConfig.map(p => ClientTransport.httpsProxy(InetSocketAddress.createUnresolved(p.host, p.port)))
             .getOrElse(ClientTransport.TCP)

val settings = ConnectionPoolSettings(system).withTransport(clientTransport)
Http().singleRequest(HttpRequest(uri = "https://google.com"), settings = settings)

答案 1 :(得分:0)

在Akka Http 10.2.0中,对具有Flowshape的RunnableGraph定义的Flow [HttpRequest,HttpResponse,NotUsed]使用bindflow。在RunnableGraph的内部,使用Http()外向连接来连接到远程代理。一些示例代码:

import akka.actor.typed.ActorSystem
import akka.actor.typed.scaladsl.Behaviors
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.{HttpRequest, HttpResponse}
import akka.stream._
import akka.stream.scaladsl.{Broadcast, Flow, GraphDSL, Merge}

import scala.concurrent.ExecutionContextExecutor
import scala.concurrent.duration.DurationInt
import scala.io.StdIn
import scala.util.{Failure, Success}

object Main {

  def main(args: Array[String]) {

    implicit val system: ActorSystem[Nothing] = ActorSystem(Behaviors.empty, "testproxy")
    implicit val executionContext: ExecutionContextExecutor = system.executionContext
    system.log.info("TestAkkaHttpProxy Main started...")
    val remoteHost = "xxx.xxx.xxx.x"
    val remotePort = 8000
    val proxyHost = "0.0.0.0"
    val proxyPort = 8080

    val gateway = Flow.fromGraph(GraphDSL.create() { implicit b =>
      import GraphDSL.Implicits._

      // Broadcast for flow input
      val broadcaster = b.add(Broadcast[HttpRequest](1))
      // Merge for flow output
      val responseMerge = b.add(Merge[HttpResponse](1))
      // outgoing client for remote proxy
      val remote = Http().outgoingConnection(remoteHost, remotePort)
      // filter out header that creates Akka Http warning
      val requestConvert = Flow[HttpRequest]
        .map(req => { req.mapHeaders(headers => headers.filter(h => h.isNot("timeout-access")))
        })
      // connect graph
      broadcaster.out(0) ~> requestConvert ~> remote ~> responseMerge
      // expose ports
      FlowShape(broadcaster.in, responseMerge.out)
    })

    // Akka Http server that binds to Flow (for remote proxy)
    Http().newServerAt(proxyHost, proxyPort).bindFlow(gateway)
      .onComplete({
        case Success(binding) ⇒
          println(s"Server is listening on 0.0.0.0:8080")
          binding.addToCoordinatedShutdown(hardTerminationDeadline = 10.seconds)
        case Failure(e) ⇒
          println(s"Binding failed with ${e.getMessage}")
          system.terminate()
      })

    system.log.info("Press RETURN to stop...")
    StdIn.readLine()
    system.terminate()
  }
}