HTTP 代理服务器技术选型之旅

背景

长期以来,贴吧开发人员多,业务耦合大,需求变化频繁,因此容易产生 bug。而我所负责的广告相关业务,和 UI 密切相关,一旦因为某种原因(甚至是被别人改了代码)产生了 bug,必然大幅度影响广告收入。

解决问题的一种方法在于频繁的测试,既然避免不了代码层面的耦合,那总是可以通过定时的检查来避免问题。所以我们维护了一组核心 case,密切关注最核心的功能。选择核心 case 实际上是在覆盖面和测试成本之间的权衡,然而多个 case 有不同的测试步骤,测试效率始终难以提高。

因此,我们的目标是建立一个代理服务器,能够在运行时把任何包(包括线上包)的数据改成我希望的样子。换句话说,这个代理服务器也可以理解为一个私服,它能够获得客户端的请求数据并作出修改,也可以获得服务端的响应数据并做修改。

代理服务器工作模型

在早期版本中,我们选择了简单的 HTTP 协议。这种选择对技术的要求最低,我们自己实现了一个代理服务器,开启 socket,监听端口,然后将客户端的请求发送给服务器,再把服务器的返回数据传回客户端。这种模式也被称为:“中间人模式”(MITM: Man In The Middle)。

虽然道理很简单,但实现起来还是有些地方要注意。首先,当 socket 接受数据后,应该新开一个进程/线程 进行处理。既然涉及到新的进程/线程,就一定要注意它的释放时机,否则会导致内存无限制增加。

其次,对于 socket 来说,它并没有等待函数,也就是说我无从得知何时有数据可读,因此这个艰巨的任务就交给了 select。我们把需要监听的 socket 对象作为参数传入其中,函数会一直阻塞,直到有可读、可写的对象,或者达到超时时间。

Keep-Alive 字段可以复用 TCP 连接,是一种常见的 HTTP 协议的优化方式,在 HTTP 1.1 中已经是默认选项。填写这个字段后,Server 返回的数据可能是分批次的,这样能够改善用户体验,但也会增加代理服务器的实现难度。所以代理服务器在作为客户端,向真正服务器请求数据时,应该删除这个字段。

由于整套流程都是自己实现,因此可以比较容易的 HOOK 住上下行数据并做修改。只有注意在接收到全部数据后再做修改即,整个流程可以用下图简单表示:

代理服务器的工作模式

技术选型

短连接

由于长连接基于 TCP,不用每次新建连接,也省略了不必要的 HTTP 报文头部,效率明显优于 HTTP。所以各大公司基本上选择了长连接作为实际生产环境下的连接方式。然而由于不熟悉 WebSocket 协议,并且我们依然支持短连接,所以代理服务器最终选择了 HTTP 协议。

要想实现这一点, 就得在应用启动时,模拟后台向客户端发送一段控制信息,强制客户端选择 HTTP 请求。这样一来,即使是线上包也可以走代理服务器。

HTTPS

由于苹果强制要求使用 HTTPS,虽然已经延期,但也是明年的趋势。考虑到后续的使用,我们决定对之前实现的代理服务器进行升级。由于 HTTPS 涉及到请求协议的解析,以及加密解密和证书管理,上述自研方案很难 hold 住。经过一番调研,最后选择了一个比较知名的开源库 mitmproxy

Mitmproxy

选择这个库最主要的理由是它直接支持 HTTPS,不过没有中文文档,国内的使用相对来说比较少,所以在接入的时候可能会略花一点时间。

这是一个 python 库, 首先要安装 virtualenv,如果本地没装的话输入:

sudo pip install virtualenv
安装好了以后,进入 mitmproxy/venv3.5/bin 文件夹输入:
source ./active
这样就可以启用 virtualenv 环境了。

Hook 脚本

这个库可以理解为命令行中可交互版本的 Charles,不过我并不打算用它的这个功能。因为我的需求主要是利用脚本来 Hook 请求, 所以我选择了 mitmdump 这个工具。使用它的时候可以指定脚本:

mitmdump -s "xxx.py"

脚本也很简单,我们可以重写 requeest 或者 receive 函数:

def request(flow):
flow.response.content = "<p>hello world</p>"

运行脚本以后,把手机的代理设为本机 ip 地址,端口号改为 8080,然后用手机浏览器打开 http://mitm.it/,如果一切配置顺利,你会看到证书的安装界面。

安装好证书后,用手机访问任何一个网站(包括 HTTPS),你应该都会看到一个小小的 hello world,至此所有的配置就完成了。

bug 修改

这个开源库有一个很严重的 bug,在解析 multipart 类型的数据时可能会发生。它使用了 splitline 方法来分割换行符,然而如果数据中有 \n 的话,就会因此丢失。很不幸的是,很多 protobuf 编码后的数据都有 \n,一旦丢失就会导致解析失败。

如果你不幸遇到了和我一样的坑,可以把相关代码改成我的版本:

for i in content.split(b"--" + boundary):
parts = i.split(b'\r\n\r\n', )
if len(parts) > and parts[][:] != b"--":
match = rx.search(parts[])
if match:
key = match.group()
value = parts[][:len(parts[])-] # Remove last \r\n
r.append((key, value))

More

到了这一步,基本上已经成功实现支持 HTTPS 的代理服务器了。后续要处理的可能就是解析 protobuf,完善业务代码等等琐碎的事情,只要小心谨慎,基本上不会有问题。

HTTP 代理服务器技术选型之旅的更多相关文章

  1. 【SSM之旅】Spring+SpringMVC+MyBatis+Bootstrap整合基础篇(一)项目简介及技术选型相关介绍

    试水 一直想去搭建个自己的个人博客,苦于自己的技术有限,然后也个人也比较懒散.想动而不能动,想动而懒得动,就这么一直拖到了现在.总觉得应该把这几年来的所学总结一番,这样才能有所成长. 不知在何时,那就 ...

  2. #数据技术选型#即席查询Shib+Presto,集群任务调度HUE+Oozie

    郑昀 创建于2014/10/30 最后更新于2014/10/31   一)选型:Shib+Presto 应用场景:即席查询(Ad-hoc Query) 1.1.即席查询的目标 使用者是产品/运营/销售 ...

  3. 老王讲自制RPC框架.(一.前言与技术选型)

    (#)背景 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进. 单一应用架构 当网站流量很小时,只 ...

  4. Atitit 开发2d游戏的技术选型attilax总结

    Atitit 开发2d游戏的技术选型attilax总结 1.1. 跨平台跨平台:一定要使用跨平台的gui技术,目前最好的就是h5(canvas,webgl,dom) +js了..1 1.2. 游戏前后 ...

  5. 《2016ThoughtWorks技术雷达峰会----js爆炸下的技术选型》

    JS爆炸下的技术选型  刘尚奇    ThoughtWorks, 高级咨询师 JS每6个星期出现一个新框架,那么如何进行JS的选型.以下从四个方面来分析. 1.工具 NPM for all the t ...

  6. 手机web站点和手机app 技术选型的困惑于思考

    今年一直在关注移动端技术的发展,自己也用博客园的rss接口玩了半年,关于技术选型的困惑和大家说说 一 趋势 随着手机硬件不断的升级,外加4g牌照的发放,不出2年时间移动端web站点和手机app一定会进 ...

  7. atitit.技术选型方法总结为什么java就是比.net有前途

    atitit.技术选型方法总结为什么java就是比.net有前途 #----按照不同的需要有不铜的法... 一般有开发效率,稳定性上的需要.. 作者 老哇的爪子 Attilax 艾龙,  EMAIL: ...

  8. 消息中间件的技术选型心得-RabbitMQ、ActiveMQ和ZeroMQ

    消息中间件的技术选型心得-RabbitMQ.ActiveMQ和ZeroMQ 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs RabbitMQ.Active ...

  9. AutoLayout技术选型和应用

    前言:这篇文章是笔者在项目中对布局技术进行技术选型和应用的相关介绍,供大家参考. && [self.buttonscount] > 0) { UIButton *button = ...

随机推荐

  1. 安全删除linux旧内核的方法

    我们在用yum升级系统之后,希望往往会为我们保持旧的内核文件,这样以防在出现硬件或者软件冲突的时候我们能够返回到旧的内核文件继续使用,如果我们想要安全的删除旧的内核文件,可以follow下面的方法. ...

  2. find -exec 与xargs 区别

    find . -name "*.txt" -exec rm {} \;find . -name "*.txt" | xargs rm {} -exec    1 ...

  3. memcached全面剖析--3.memcached的删除机制和发展方向

    下面是<memcached全面剖析>的第三部分. 发表日:2008/7/16 作者:前坂徹(Toru Maesaka) 原文链接:http://gihyo.jp/dev/feature/0 ...

  4. 手动修改magento域名

    So it turns out the problem was that Apache didn't have write permissions to the WEBROOT/var directo ...

  5. kubernetes架构之二

    一.概述 IaaS:即基础设施即服务,通过虚拟化和分布式存储等技术,实现对包括服务器.存储设备.网络设备等各种物理资源的抽象:从而形成了一个可扩展.可按需分配的虚拟资源池.最具代表性的IaaS产品有A ...

  6. Mac 重建 Spotlight 索引

    前言 最近发现很多 mac 用户反映自己的 mac 系统显示内存占用高达 200 多 Gb,可是实际上自己下载的应用程序根本没那么多,使用专业的内存扫描工具扫的结果跟系统本身显示的完全不一样.那么出现 ...

  7. 【struts2】预定义拦截器

    1)预定义拦截器 Struts2有默认的拦截器配置,也就是说,虽然我们没有主动去配置任何关于拦截器的东西,但是Struts2会使用默认引用的拦截器.由于Struts2的默认拦截器声明和引用都在这个St ...

  8. Android 自定义漂亮的圆形进度条

    公司有这样一个需求,实现这个圆弧进度条 所以,现在就将它抽取出来分享 如果需要是圆帽的就将,下面这句代码放开即可 mRingPaint.setStrokeCap(Paint.Cap.ROUND);// ...

  9. 跟我学SharePoint 2013视频培训课程——删除恢复、文档离线工作(11)

    课程简介 第11天,怎样在SharePoint 2013中删除.恢复文档.文档离线工作. 视频 SharePoint 2013 交流群 41032413

  10. Android开发实现计算器的例子

    例子 代码如下 复制代码 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" a ...