基于Akka-Streams的HTTP代理的实现
来源:互联网 发布:arcgis java 开发手册 编辑:程序博客网 时间:2024/05/21 11:27
Akka-Streams是一个让人激动的Reactive Streams的框架,Akka-Http也是构建在其之上,除了内置背压模式的支持,使用其DSL构建一个Graph也是一个让人惊艳的过程。对于Akka-Streams的介绍会在后续的文章中逐一展开,本文只奉上一段代码,实现的是HTTP代理功能,同时可以对请求和响应做一些修饰。
import akka.NotUsedimport akka.actor.ActorSystemimport akka.http.scaladsl.Httpimport akka.http.scaladsl.coding.{Deflate, Gzip, NoCoding}import akka.http.scaladsl.model._import akka.http.scaladsl.model.headers.HttpEncodingsimport akka.stream._import akka.stream.scaladsl.{Flow, GraphDSL, Sink, Source}import akka.util.ByteStringimport io.circe._import io.circe.parser._import scala.concurrent.duration.Durationimport scala.concurrent.{Await, Future}object HttpProxy extends App { implicit val actorSystem = ActorSystem(name = "system") implicit val streamMaterializer = ActorMaterializer() implicit val executionContext = actorSystem.dispatcher // The handle flow contains 2 sub-flows: forward flow & decorate flow. // The forward flow will forward http request to destination server. // The decorate flow will decorate http response then return to clients. val handleFlow: Flow[HttpRequest, HttpResponse, _] = Flow.fromGraph(GraphDSL.create() { implicit builder: GraphDSL.Builder[NotUsed] => import GraphDSL.Implicits._ val forwardFlow = Http(actorSystem).outgoingConnection(host = "destination-server", port = 8080) val forwardRequestFlow: Flow[HttpRequest, (HttpRequest, Future[HttpResponse]), _] = { Flow[HttpRequest] .map { orgRequest => { val request = orgRequest.copy( // The uri string value looks like: "http://domain/path", // this is invalid for some http servers, the acceptance format is: "/path" // So, we need remove domain, now, The value 21 is the length of // string: "http://destination-server:8080", it's hard code for short-term, // later, it will be configurable. uri = orgRequest.uri.toString().substring(30), headers = for { header <- orgRequest.headers if header.isNot("timeout-access") } yield { if (header.is("host")) { akka.http.scaladsl.model.headers.Host(Uri.Host("destination-server"), 8080) } else { header } } ) val response = Source.single(request).via(forwardFlow).runWith(Sink.head) (request, response) } } } val decorateResponseFlow: Flow[(HttpRequest, Future[HttpResponse]), HttpResponse, _] = { Flow[(HttpRequest, Future[HttpResponse])] .mapAsync(1) { (tuple) => { val request = tuple._1 val responseFuture = tuple._2 val (path, body) = extractPathAndBody(request) path match { case "/path/to/decorate" if body.contains("some special key words") => responseFuture.flatMap { response => val coder = response.encoding match{ case HttpEncodings.gzip => Gzip case HttpEncodings.deflate => Deflate case HttpEncodings.identity => NoCoding case _ => throw new RuntimeException("akka not supported encoding "+response.encoding.toString()) } response.entity .dataBytes.runFold(ByteString(""))(_ ++ _) .map { byteString => val gzipDecodeFuture = coder.decode(byteString) val gzipDecodeByteString = Await.result(gzipDecodeFuture, Duration.Inf) val decodedByteString = gzipDecodeByteString.decodeString("utf-8") val json: Json = parse(decodedByteString).getOrElse(Json.Null) // do some transformation jobs based on origin json. val transformedEntity = ??? val entity = HttpEntity(ContentTypes.`application/json`, coder.encode(ByteString.fromString(transformedEntity))) response.copy(entity = entity) } } case _ => responseFuture } } } } val forwardRequest: FlowShape[HttpRequest, (HttpRequest, Future[HttpResponse])] = builder.add(forwardRequestFlow) val decorateResponse: FlowShape[(HttpRequest, Future[HttpResponse]), HttpResponse] = builder.add(decorateResponseFlow) forwardRequest ~> decorateResponse FlowShape(forwardRequest.in, decorateResponse.out) }) def extractPathAndBody(request:HttpRequest):(String,String) ={ val path = request.uri.path.toString() val body = request.entity.dataBytes.map(_.decodeString("utf-8")).runWith(Sink.head) val bodyStr = Await.result(body, Duration.Inf) (path,bodyStr) } val serverSource = Http(actorSystem).bind(interface = "0.0.0.0", port = 7000) serverSource.to(Sink.foreach { connection => connection.handleWith(handleFlow) }).run()}
2 0
- 基于Akka-Streams的HTTP代理的实现
- 基于http/1.1的代理
- 基于http/1.1的代理
- Scala基于Akka的Remote Actor实现的简单RPC
- 简易HTTP代理的实现
- 【Nginx】基于HTTP的反向代理
- scala通过akka的actor实现socket http server(NIO非阻塞模式)
- HTTP透明代理的java实现
- golang实现的http反向代理
- Spring基于代理的AOP实现
- 基于netty实现的socks5代理协议
- 基于netty实现的socks5代理协议
- cglib动态代理[基于类操作的动态代理实现]
- AKKA路由策略的简单实现
- Python基于BaseHTTPRequestHandler的HTTP代理V1.0
- 使用Scala的Akka HTTP,Akka Stream和Reactive Mongo建立REST服务
- Gatling-基于Scala,Akka&Netty的性能测试框架
- C#HTTP代理的实现之注册表实现
- ajax 请求实例
- 斯坦福机器学习公开课18
- 顶层视图DecorView添加到窗口的过程
- poj 1182
- Docker部署WordPress LNMP(Nginx PHP MySQL)环境实践
- 基于Akka-Streams的HTTP代理的实现
- ie hack总结
- 错误: 找不到或无法加载主类 Test.class
- Android LayoutInflater原理分析,带你一步步深入了解View(一)
- 10G SFP+光模块和40G QSFP+光模块的连接解决方案
- 1007. Maximum Subsequence Sum (25)
- python3+PyQt5 实现自定义窗口部件--流体混合窗口部件
- 08CMS汽车门户v7.0旗舰版完美破解:新增微信登陆|微信注册+微商家+车商城+短信通知+无限分站,无域名等任何限制
- 打印乘法口诀表的两种方法