使用Tornado实现http代理

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有两个回调函数。我的理解是第一个回调在关闭的时候调用,第二个回调在不停读出数据的时候调用。

写出来用的效果还行:

时间: 2024-12-11 01:13:12

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

Nginx反向代理的配置

Chapter: Nginx基本操作释疑 1. Nginx的端口修改问题 2. Nginx 301重定向的配置 3. Windows下配置Nginx使之支持PHP 4. Linux下配置Nginx使之支持PHP 5. 以源码编译的方式安装PHP与php-fpm 6. Nginx多站点配置的一次实践 7. Nginx反向代理的配置 Nginx 作为 web 服务器一个重要的功能就是反向代理.其实我们在前面的一篇文章<Nginx多站点配置的一次实践>里,用的就是 Nginx 的反向代理,这里简单再

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

最近有打算研读nginx源代码,看到网上介绍nginx可以作为一个反向代理服务器完成负载均衡.所以搜罗了一些关于反向代理服务器的内容,整理综合. 一  概述 反向代理(Reverse Proxy)方式是指以代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器:并将从服务器上得到的结果返回给Internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器. 通常的代理服务器,只用于代理内部网络对Internet的连接请求,客户机必须指定代理服务器,并将本来要直接

centos7/nginx/tornado下发生“connect() to 127.0.0.1:8080 failed (13: Permission denied) while connecting to upstream”

1. 问题 在centos7下配置tornado的反向代理,结果日志一直记录错误如本文标题. 2. SELinux 安全子系统造成 3. 解决 1 临时方法 – 设置系统参数 使用命令setenforce 0 附: setenforce 1 设置SELinux 成为enforcing模式 setenforce 0 设置SELinux 成为permissive模式 2 永久方法 – 需要重启 vi /etc/selinux/config 设置SELINUX=disabled ,重启服务器.

nginx的反向代理和配置

最近有打算研读nginx源代码,看到网上介绍nginx可以作为一个反向代理服务器完成负载均衡.所以搜罗了一些关于反向代理服务器的内容,整理综合. 一  概述 反向代理(Reverse Proxy)方式是指以代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器:并将从服务器上得到的结果返回给Internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器. 通常的代理服务器,只用于代理内部网络对Internet的连接请求,客户机必须指定代理服务器,并将本来要直接

深入理解Tornado——一个异步web服务器

本人的第一次翻译,转载请注明出处:http://www.cnblogs.com/yiwenshengmei/archive/2011/06/08/understanding_tornado.html原文地址:http://golubenco.org/?p=16 这篇文章的目的在于对Tornado这个异步服务器软件的底层进行一番探索.我采用自底向上的方式进行介绍,从轮巡开始,向上一直到应用层,指出我认为有趣的部分.所以,如果你有打算要阅读Tornado这个web框架的源码,又或者是你对一个异步we

异步非阻塞IO的Python Web框架--Tornado

Tornado的全称是Torado Web Server,从名字上就可知它可用作Web服务器,但同时它也是一个Python Web的开发框架.最初是在FriendFeed公司的网站上使用,FaceBook收购之后便进行了开源. 作为Web框架,是一个轻量级的Web框架,类似于另一个Python web 框架Web.py,其拥有异步非阻塞IO的处理方式. 作为Web服务器,Tornado有较为出色的抗负载能力,官方用nginx反向代理的方式部署Tornado和其它Python web应用框架进行对

《Introduction to Tornado》中文翻译计划——第五章:异步Web服务

http://www.pythoner.com/294.html 本文为<Introduction to Tornado>中文翻译,将在https://github.com/alioth310/itt2zh上面持续更新,本文内容可能不是最新状态,请在GitHub上获得最新版本. 本文也可在http://demo.pythoner.com/itt2zh上进行格式化的预览. 第五章:异步Web服务 到目前为止,我们已经看到了许多使Tornado成为一个Web应用强有力框架的功能.它的简单性.易用性

ubuntu下python+tornado+supervisor+nginx部署

由于之前在医院采集的数据都是拍照得到的处方图片,而需要用到的是处方的文本形式.因此这两个星期写了个小程序把服务器的图片显示给用户(到时候雇一些人),让用户根据图片录入文字信息. 之前都是用java写web,想到自己最近学机器学习要用python,所以用python来写一下,此外,因为想用点新东西,也介于程序比较小,所以考虑用mongodb来存储(虽然确实没有必要). 基本架构是这样:(后台语言)python +(web框架和web服务器)tornado + (数据库)mongodb  +(进程管

tornado django flask 跨域解决办法(cors)

XMLHttpRequest cannot load http://www.baidu.com. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://10.16.16.25:9988' is therefore not allowed access. tornado 这个就是典型的cors,允许后端允许跨域的方法.第二种方法,反向代理还在实践中 #!/usr/bi