用 Tornado 实现简单的在线代理

标签: tornado 在线代理 | 发表时间:2011-09-11 14:46 | 作者:(author unknown) Ken
出处:http://simple-is-better.com/

实现代理的方式很多种,流行的web服务器也大都要代理的功能,比如http://www.tornadoweb.cn用的就是nginx的代理功能做的tornadoweb官网的镜像。

最近,我在开发一个移动运用(以下简称APP)的后台程序(Server),该运用需要调用到另一平台产品(Platform)的API。对于这个系统来说,可选的一种实现方式方式是APP同时跟Server&Platform两者交互;另一种则在Server端封装掉Platform的API,APP只和Server交互。显然后一种方式的系统架构会清晰些,APP编程时也就相对简单。那么如何在Server端封装Platform的API呢,我首先考虑到的就是用代理的方式来实现。碰巧最近Tornado邮件群组里有人在讨论using Tornado as a proxy,贴主提到的运用场景跟我这碰到的场景非常的相似,我把原帖的代码做了些整理和简化,源代码如下:

# -*- coding: utf-8 -*-
#
# Copyright(c) 2011 Felinx Lee & http://feilong.me/
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import logging

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.httpclient
from tornado.web import HTTPError, asynchronous
from tornado.httpclient import HTTPRequest
from tornado.options import define, options
try:
    from tornado.curl_httpclient import CurlAsyncHTTPClient as AsyncHTTPClient
except ImportError:
    from tornado.simple_httpclient import SimpleAsyncHTTPClient as AsyncHTTPClient

define("port", default=8888, help="run on the given port", type=int)
define("api_protocol", default="http")
define("api_host", default="feilong.me")
define("api_port", default="80")
define("debug", default=True, type=bool)

class ProxyHandler(tornado.web.RequestHandler):
    @asynchronous
    def get(self):
        # enable API GET request when debugging
        if options.debug:
            return self.post()
        else:
            raise HTTPError(405)

    @asynchronous
    def post(self):
        protocol = options.api_protocol
        host = options.api_host
        port = options.api_port

        # port suffix
        port = "" if port == "80" else ":%s" % port

        uri = self.request.uri
        url = "%s://%s%s%s" % (protocol, host, port, uri)

        # update host to destination host
        headers = dict(self.request.headers)
        headers["Host"] = host

        try:
            AsyncHTTPClient().fetch(
                HTTPRequest(url=url,
                            method="POST",
                            body=self.request.body,
                            headers=headers,
                            follow_redirects=False),
                self._on_proxy)
        except tornado.httpclient.HTTPError, x:
            if hasattr(x, "response") and x.response:
                self._on_proxy(x.response)
            else:
                logging.error("Tornado signalled HTTPError %s", x)

    def _on_proxy(self, response):
        if response.error and not isinstance(response.error,
                                             tornado.httpclient.HTTPError):
            raise HTTPError(500)
        else:
            self.set_status(response.code)
            for header in ("Date", "Cache-Control", "Server", "Content-Type", "Location"):
                v = response.headers.get(header)
                if v:
                    self.set_header(header, v)
            if response.body:
                self.write(response.body)
            self.finish()

def main():
    tornado.options.parse_command_line()
    application = tornado.web.Application([
        (r"/.*", ProxyHandler),
    ])
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":
    main()

运行上面的代码后,访问 http://localhost:8888/ 将会完整显示飞龙博客的首页,即代理访问了http://feilong.me/的内容。

我考虑用程序的方式来做代理而不是直接用Nginx来做代理,其中一点是考虑到用程序可以很容易的控制Platform的哪些API是需要代理的,而哪些是要屏蔽掉的,还有哪些可能是要重写的(比如Server的login可能不能直接代理Platform的login,但却要调用到Platform的login API)。

以上这段代码只是做了简单的页面内容代理,并没有对页面进行进一步的解析处理,比如链接替换等,这些就交个有兴趣的朋友去开发了。基于以上这段代码,将其扩展一下,是完全可以实现一个完整的在线代理程序的。

这段代码我已放到了我的实验项目里,见https://bitbucket.org/felinx/labs,我将会放更多类似于这样的实验性质的小项目到这个repository里来,有兴趣的朋友可以关注一下。

# 来源:飞龙博客


在微博上关注: 新浪, 腾讯   投稿

最新招聘

更多>>

相关 [tornado 在线代理] 推荐:

用 Tornado 实现简单的在线代理

- Ken - python.cn(jobs, news)
实现代理的方式很多种,流行的web服务器也大都要代理的功能,比如http://www.tornadoweb.cn用的就是nginx的代理功能做的tornadoweb官网的镜像. 最近,我在开发一个移动运用(以下简称APP)的后台程序(Server),该运用需要调用到另一平台产品(Platform)的API.

#Tornado# 文档翻译中文版

- sasiky - python.cn(jobs, news)
内容索引 Table of Contents. 4.1   请求处理和请求参数. 4.2   RequestHandler中的主要方法. 4.4   Cookies和安全Cookies. 4.6   跨站伪造请求的防范. 4.7   静态文件和主动式文件缓存. 4.10   非阻塞式的异步请求. 7   WSGI 和 Google AppEngine.

Tornado 的 IOStream 简介与应用

- Ken - python.cn(jobs, news)
Tornado的核心源码是由ioloop.py和iostream.py这2个文件组成的. 前者提供了一个循环,用于处理I/O事件;后者则封装了一个非阻塞的socket. 有了这2者后,就能搭建起TCP server和HTTP server,实现异步HTTP客户端,这便是Tornado的主要内容了. 之前在研究socket时已差不多弄懂了ioloop的逻辑,于是本文就接着研究iostream了.

Torrent Tornado:浏览器内 BT 下载

- - LinuxTOY
Torrent Tornado 是一款完全使用 JavaScript 实现的附加组件,可以为 Firefox 浏览器增加 BT 下载功能. 体积小巧(不到 100K),完全使用 JavaScript 实现,跨平台且无本地二进制依赖. 支持和磁力链接及种子文件关联. 注意 当前 1.0 版本仅支持下载,不支持上传.

基于tornado的异步TCPServer以及TCPClient

- - ITeye博客
关于tornado,我这里就不详细讲了,有兴趣的同学可以通过以下两篇博客了解一下:. 我们一般用tornado来编写web程序,但实际上,tornado底层的代码非常优秀,也可以用这些代码来编写TCP应用. tornado最突出的特点就是“异步”,所以,我这里编写了一个异步的TCPServer和一个异步的TCPClient来帮助大家理解,下面直接看代码:.

用Tornado打造WebSocket与Ajax Long-Polling自适应聊天室

- satan - keakon的涂鸦馆
这几天忙着研究Tornado,想着总得学以致用吧,于是就决定做个聊天室玩玩. 实际上在Tornado的源码里就有chat和websocket这2个聊天室的demo,分别采用Ajax Long-Polling和WebSocket技术构建. 而我要实现的则很简单:将这2种技术融合在一起. 就技术而言,WebSocket的通信开销很少,没有连接数的限制,但由于本身比较新,支持它的浏览器并不多(目前仅有Chrome 6+、Safari 5.0.1+、Firefox 4+和Opera 11+,且Firefox和Opera还因安全原因默认禁用了).

Flask, Tornado, GEvent, 以及它们的结合的性能比较

- Ken - python.cn(jobs, news)
英文: http://blog.wensheng.com/2011/10/performance-of-flask-tornado-gevent-and.html. 我在选一个python的互联网框架, 本来已经定下来用Tornado了.  但我还听到很多人推荐Flask的简单性和灵活性, 还有gevent的高性能, 所以决定也试试它们以及它们和Tornado的结合.

Browser和Server持续同步的几种方式(jQuery+tornado演示)

- mrluanma - 残阳似血的博客
在B/S模型的Web应用中,客户端常常需要保持和服务器的持续更新. 这种对及时性要求比较高的应用比如:股票价格的查询,实时的商品价格,自动更新的twitter timeline以及基于浏览器的聊天系统(如GTalk)等等. 由于近些年AJAX技术的兴起,也出现了多种实现方式. 本文将对这几种方式进行说明,并用jQuery+tornado进行演示,需要说明的是,如果对tornado不了解也没有任何问题,由于tornado的代码非常清晰且易懂,选择tornado是因为其是一个非阻塞的(Non-blocking IO)异步框架(本文使用2.0版本).