0x00 http代理

http代理的用处非常多,市面上也有公开的代理,可是有时候为了工作须要,比方分析应用层流量、做数据訪问控制、甚至做监控等等。Tornado提供了一些非常方便的环境和API,我们能够基于Tornado轻松实现一个http代理。

0x01 实现原理

http代理主要做client和web服务器之间的转发。这是大家都熟悉的场景,但仅仅限于http协议的情形。对于https的情况。这时候代理仅仅作为TCP中继进行信息中转,须要单独处理。

0x02 Tornado实现

基于Tornado能够实现一个异步的http代理,性能优越,实现也简单,主要使用的类是AsyncHTTPClient,IOStream。

阅读过Tornado源代码的同学可能对这两个类并不陌生。

这里还是简单说下,AsyncHTTPClient顾名思义,是用来做异步HTTPclient请求的。而IOStream是对socket的一层封装。

AsyncHTTPClient就是用来处理普通的http请求的。RequestHandler获取client请求之后,proxy须要解析client的请求并使用这个类来请求服务器,拿到response,然后写给client。打完收工。

对于proxy作为TCP中继的时候,事实上全然能够使用原生的socket两头儿读写数据,只是太麻烦了。Tornado提供了一个IOStream类,这个类能够看做是socket的包装类,用起来比socket简单很多。而且socket是异步非堵塞的。

Talk is cheap, show me the code,不多说,看代码好了,这里因为一些原因,我仅仅能贴出关键部分的代码,希望阅读此文的同学能够自己写一个出来用,事实上也不难。

处理http请求

    @tornado.web.asynchronous
def get(self):
# 获取请求体
body = self.request.body
if not body:
body = None
try:
# 代理发送请求
render_request(
self.request.uri,
callback=self.on_response,
method=self.request.method,
body=body,
headers=self.request.headers,
follow_redirects=False,
allow_nonstandard_methods=True)
except tornado.httpclient.HTTPError as httperror:
if hasattr(httperror, 'response') and httperror.response:
self.on_response(httperror.response)
else:
self.set_status(500)
self.write('Internal server error:\n' + str(httperror))
self.finish()

没啥好说的。接到client请求。直接去请求服务器即可了。异步回调函数是on_response,这个函数里就处理proxy和client的交互即可了。self.write(response.body)你懂的。

这里有个坑。就是写headers的时候。把response的headers照搬设置一遍是会出错的,造成訪问失败。这里我的处理方法是仅仅写RequestHandler中self._headers存在的头即可。

TCP中继实现

对于443端口或者浏览器的connect请求。代理仅仅能从TCP层入手。转发整个HTTP报文。这里使用的是http协议中的connect方法,在RequestHandler中实现这种方法即可了。

这里要注意。Tornado默认是不支持http的connect方法的,所以要改动SUPPORTED_METHODS參数才行:



这里在RequestHandler中加入一个SUPPORTED_METHODS替换父类的即可:

SUPPORTED_METHODS.append('CONNECT')

顺便说下connect方法,这种方法被调用的时候,代理不用关系http层请求的详细内容,而是直接从TCP层转发这个报文给服务器。

收到时,也是相同的转发给client。

CONNECT www.web-tinker.com:80 HTTP/1.1
Host: www.web-tinker.com:80
Proxy-Connection: Keep-Alive
Proxy-Authorization: Basic *
Content-Length: 0

详细实现的代码例如以下:

    @tornado.web.asynchronous
def connect(self):
'''
对于HTTPS连接。代理应当作为TCP中继
'''
def req_close(data):
if conn_stream.closed():
return
else:
conn_stream.write(data) def write_to_server(data):
conn_stream.write(data) def proxy_close(data):
if req_stream.closed():
return
else:
req_stream.close(data) def write_to_client(data):
req_stream.write(data) def on_connect():
'''
创建TCP中继的回调
'''
req_stream.read_until_close(req_close, write_to_server)
conn_stream.read_until_close(proxy_close, write_to_client)
req_stream.write(b'HTTP/1.0 200 Connection established\r\n\r\n') print 'Starting Conntect to %s' % self.request.uri
# 获取request的socket
req_stream = self.request.connection.stream # 找到主机端口。一般为443
host, port = (None, 443)
netloc = self.request.uri.split(':')
if len(netloc) == 2:
host, port = netloc
elif len(netloc) == 1:
host = netloc[0] # 创建iostream
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
conn_stream = tornado.iostream.IOStream(s)
conn_stream.connect((host, port), on_connect)

我解释下这两句:

req_stream.read_until_close(req_close, write_to_server)
conn_stream.read_until_close(proxy_close, write_to_client)

短短两行代码,加上4个回调函数,就完毕了数据的中转。

首先,req_stream是proxy和client之间的socket,能够通过HTTPRequest获取到相应的iostream,proxy和server之间的socket就要自己创建了,这里是conn_stream。

read_until_close方法是iostream中提供的,作用是一直读数据,直到socket关闭了。

第一行的作用就是从client和proxy之间的socket中读数据。读出来之后,写入到proxy和server之间的socket中。由proxy转发。

第二行的作用就是将服务器数据写到clientsocket中了,和上面一样。没啥好说的。写入的功能就在四个回调函数中。

有人奇怪为啥read_until_close有两个回调函数。我的理解是第一个回调在关闭的时候调用,第二个回调在不停读出数据的时候调用。

写出来用的效果还行:

使用Tornado实现http代理的更多相关文章

  1. tornado 采用 epoll 代理构建高并发网络模型

    1 阻塞和非阻塞  对于阻塞和非阻塞,网上有一个很形象的比喻,就是说好比你在等快递,阻塞模式就是快递如果不到,你就不能做其他事情.非阻塞模式就是在这段时间里面,你可以做其他事情,比如上网.打游戏.睡觉 ...

  2. Nginx反向代理的配置

    Chapter: Nginx基本操作释疑 1. Nginx的端口修改问题 2. Nginx 301重定向的配置 3. Windows下配置Nginx使之支持PHP 4. Linux下配置Nginx使之 ...

  3. nginx反向代理原理和配置讲解

    最近有打算研读nginx源代码,看到网上介绍nginx可以作为一个反向代理服务器完成负载均衡.所以搜罗了一些关于反向代理服务器的内容,整理综合. 一  概述 反向代理(Reverse Proxy)方式 ...

  4. nginx的反向代理和配置

    最近有打算研读nginx源代码,看到网上介绍nginx可以作为一个反向代理服务器完成负载均衡.所以搜罗了一些关于反向代理服务器的内容,整理综合. 一  概述 反向代理(Reverse Proxy)方式 ...

  5. Nginx 的方向代理及配置

    最近有打算研读nginx源代码,看到网上介绍nginx可以作为一个反向代理服务器完成负载均衡.所以搜罗了一些关于反向代理服务器的内容,整理综合. 一  概述 反向代理(Reverse Proxy)方式 ...

  6. 在centos7.6上部署前后端分离项目Nginx反向代理vue.js2.6+Tornado5.1.1,使用supervisor统一管理服务

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_102 这一次使用vue.js+tornado的组合来部署前后端分离的web项目,vue.js不用说了,前端当红炸子鸡,泛用性非常广 ...

  7. 转载:使用Tornado+Redis维护ADSL拨号服务器代理池

    我们尝试维护过一个免费的代理池,但是代理池效果用过就知道了,毕竟里面有大量免费代理,虽然这些代理是可用的,但是既然我们能刷到这个免费代理,别人也能呀,所以就导致这个代理同时被很多人使用来抓取网站,所以 ...

  8. 基于 Tornado 实现的 Web 站点反向代理

    因为一个奇怪的需求,使用 Python 和 Tornado 框架实现了一个 Web 站点的反向代理.实现的功能是这样: 假设这个反向代理网站的地址是 http://www.example.com 访问 ...

  9. tornado zbar 二维码识别 ,配合nginx 反向代理,supervisord 监控

    tornado zbar 二维码识别 ,配合nginx 反向代理,supervisord 监控 1.zbar识别二维码程序,python2.6.6 #!/usr/bin/env python # co ...

随机推荐

  1. 忘记dba用户密码,利用SQLPlus重置dba密码

    打开SQL Plus 输入用户名: sys as sysdba 输入口令:可直接回车 连接到: Oracle Database 11g Enterprise Edition Release 11.2. ...

  2. COMMIT - 提交当前事务

    SYNOPSIS COMMIT [ WORK | TRANSACTION ] DESCRIPTION 描述 COMMIT 提交当前事务. 所有事务的更改都将为其他事务可见,而且保证当崩溃发生时的可持续 ...

  3. call, apply, bind 区别

    #call, apply, bind 区别及模拟实现call apply bind 三者都可以用来改变this的指向,但是在用法上略有不同  首先说一下call和apply的区别 call和apply ...

  4. NLog小记

    NLog安装: Install-Package NLog NLog配置: <?xml version="1.0" encoding="utf-8" ?&g ...

  5. POJ中和质数相关的三个例题(POJ 2262、POJ 2739、POJ 3006)

    质数(prime number)又称素数,有无限个.一个大于1的自然数,除了1和它本身外,不能被其他自然数整除,换句话说就是该数除了1和它本身以外不再有其他的因数:否则称为合数.      最小的质数 ...

  6. 离线缓存 application cache

    1. 什么是离线缓存: 离线缓存可以将站点的一些文件缓存到本地,它是浏览器自己的一种机制,将需要的文件缓存下来,以便后期即使没有连接网络,被缓存的页面也可以展示. 例子:比如我们在手机或电脑上访问一个 ...

  7. Android中notifyDataSetInvalidated()和notifyDataSetChanged()有什么区别

     看下源码中对于这两个方法   public void notifyDataSetChanged () 该方法内部实现了在每个观察者上面调用onChanged事件.每当发现数据集有改变的情况,或者读取 ...

  8. 如何HTML标签和JS中设置CSS3 var变量

    一.HTML标签中设置CSS变量 如下: <div style="--color: #cd0000;"> <img src="mm.jpg" ...

  9. 浅谈FFT(快速博立叶变换)&学习笔记

    0XFF---FFT是啥? FFT是一种DFT的高效算法,称为快速傅立叶变换(fast Fourier transform),它根据离散傅氏变换的奇.偶.虚.实等 特性,对离散傅立叶变换的算法进行改进 ...

  10. Layui表格之动态添加数据(表格多选解决方案)

    前言: Layui已经给出了多选记录的解决方案,是在每条数据的前面加上CheckBox,每次选择都有监听.效果是这样: 实现监听的代码如下,这是一种解决选择多条数据的方案: table.on('edi ...