在Python的Tornado框架中实现简单的在线代理的教程


Posted in Python onMay 02, 2015

实现代理的方式很多种,流行的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里来,有兴趣的朋友可以关注一下。

转载请注明出处:http://feilong.me/2011/09/tornado-as-a-proxy

Python 相关文章推荐
python使用WMI检测windows系统信息、硬盘信息、网卡信息的方法
May 15 Python
Django中模型Model添加JSON类型字段的方法
Jun 17 Python
浅谈Python基础之I/O模型
May 11 Python
python装饰器实例大详解
Oct 25 Python
Python continue继续循环用法总结
Jun 10 Python
解决pip install的时候报错timed out的问题
Jun 12 Python
python使用Turtle库绘制动态钟表
Nov 19 Python
Python字符串通过'+'和join函数拼接新字符串的性能测试比较
Mar 05 Python
Python reversed函数及使用方法解析
Mar 17 Python
Python求解排列中的逆序数个数实例
May 03 Python
tensorflow模型转ncnn的操作方式
May 25 Python
使用python创建股票的时间序列可视化分析
Mar 03 Python
探究Python的Tornado框架对子域名和泛域名的支持
May 02 #Python
Python编程中运用闭包时所需要注意的一些地方
May 02 #Python
按日期打印Python的Tornado框架中的日志的方法
May 02 #Python
详细解读Python的web.py框架下的application.py模块
May 02 #Python
使用Python的web.py框架实现类似Django的ORM查询的教程
May 02 #Python
在ironpython中利用装饰器执行SQL操作的例子
May 02 #Python
用Python编写简单的定时器的方法
May 02 #Python
You might like
php mssql 时间格式问题
2009/01/13 PHP
《PHP编程最快明白》第二讲 数字、浮点、布尔型、字符串和数组
2010/11/01 PHP
PHP读取RSS(Feed)简单实例
2014/06/12 PHP
简介WordPress中用于获取首页和站点链接的PHP函数
2015/12/17 PHP
基于jquery实现的类似百度搜索的输入框自动完成功能
2011/08/23 Javascript
自定义右键属性覆盖浏览器默认右键行为实现代码
2013/02/02 Javascript
node.js中的url.parse方法使用说明
2014/12/10 Javascript
通过BootStrap实现轮播图的实际应用
2016/09/26 Javascript
jQuery实现按比例缩放图片的方法
2017/04/29 jQuery
微信小程序实现左右联动的实战记录
2018/07/05 Javascript
微信小程序页面间值传递的两种方法
2018/11/26 Javascript
深度解读vue-resize的具体用法
2020/07/08 Javascript
[04:36]DOTA2国际邀请赛 ti3精彩集锦
2013/08/19 DOTA
[50:27]OG vs LGD 2018国际邀请赛淘汰赛BO3 第一场 8.26
2018/08/30 DOTA
收集的几个Python小技巧分享
2014/11/22 Python
整理Python中的赋值运算符
2015/05/13 Python
Python Selenium Cookie 绕过验证码实现登录示例代码
2018/04/10 Python
Python基于dom操作xml数据的方法示例
2018/05/12 Python
python实现点对点聊天程序
2018/07/28 Python
python并发编程 Process对象的其他属性方法join方法详解
2019/08/20 Python
Python 如何创建一个简单的REST接口
2020/07/30 Python
五分钟学会怎么用Pygame做一个简单的贪吃蛇
2021/01/06 Python
python读取图片颜色值并生成excel像素画的方法实例
2021/02/19 Python
HTML5验证以及日期显示的实现详解
2013/07/05 HTML / CSS
某/etc/fstab文件中的某行如下: /dev/had5 /mnt/dosdata msdos defaults,usrquota 1 2 请解释其含义
2013/09/18 面试题
儿科护士实习自我鉴定
2013/10/17 职场文书
结婚典礼证婚词
2014/01/11 职场文书
创先争优制度
2014/01/21 职场文书
教师党员岗位承诺书
2014/05/29 职场文书
党员群众路线对照检查材料思想汇报
2014/09/17 职场文书
2014年商场工作总结
2014/11/22 职场文书
写自招自荐信的绝招!
2019/04/19 职场文书
大学生创业计划书
2019/06/24 职场文书
小学三年级作文之写景
2019/11/05 职场文书
深入浅出讲解Java8函数式编程
2022/01/18 Java/Android
Oracle 11g数据库使用expdp每周进行数据备份并上传到备份服务器
2022/06/28 Oracle