在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中类型关系和继承关系实例详解
May 25 Python
Python基于checksum计算文件是否相同的方法
Jul 09 Python
Python格式化输出%s和%d
May 07 Python
Flask web开发处理POST请求实现(登录案例)
Jul 26 Python
解决Pyinstaller 打包exe文件 取消dos窗口(黑框框)的问题
Jun 21 Python
Python Pandas分组聚合的实现方法
Jul 02 Python
django日志默认打印request请求信息的方法示例
May 17 Python
Python常用数据分析模块原理解析
Jul 20 Python
Django中和时区相关的安全问题详解
Oct 12 Python
Django-silk性能测试工具安装及使用解析
Nov 28 Python
Pytest中skip和skipif的具体使用方法
Jun 30 Python
Python读写yaml文件
Mar 20 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
memcache命令启动参数中文解释
2014/01/13 PHP
php实例分享之html转为rtf格式
2014/06/02 PHP
php中10个不同等级压缩优化图片操作示例
2016/11/14 PHP
PHP中类与对象功能、用法实例解读
2020/03/27 PHP
使用jscript实现二进制读写脚本代码
2008/06/09 Javascript
jQuery文本框(input textare)事件绑定方法教程
2013/04/24 Javascript
js获取GridView中行数据的两种方法 分享
2013/07/13 Javascript
js查找节点的方法小结
2015/01/13 Javascript
javascript实现复选框超过限制即弹出警告框的方法
2015/02/25 Javascript
JS实现跟随鼠标立体翻转图片的方法
2015/05/04 Javascript
小议JavaScript中Generator和Iterator的使用
2015/07/29 Javascript
Js制作点击输入框时默认文字消失的效果
2015/09/05 Javascript
JavaScript学习笔记之数组随机排序
2016/03/23 Javascript
JavaScript笔记之数据属性和存储器属性
2016/03/31 Javascript
使用jQuery调用XML实现无刷新即时聊天
2016/08/07 Javascript
Angular.js中ng-if、ng-show和ng-hide的区别介绍
2017/01/20 Javascript
JavaScript实现焦点进入文本框内关闭输入法的核心代码
2017/09/20 Javascript
详解Bootstrap 学习(一)入门
2019/04/12 Javascript
JavaScript 作用域实例分析
2019/10/02 Javascript
使用vuex较为优雅的实现一个购物车功能的示例代码
2019/12/09 Javascript
jquery将信息遍历到界面上实例代码
2020/01/21 jQuery
Vue项目中数据的深度监听或对象属性的监听实例
2020/07/17 Javascript
vue-quill-editor的使用及个性化定制操作
2020/08/04 Javascript
python使用clear方法清除字典内全部数据实例
2015/07/11 Python
快速入手Python字符编码
2016/08/03 Python
Python实现合并同一个文件夹下所有txt文件的方法示例
2018/04/26 Python
使用Python做定时任务及时了解互联网动态
2019/05/15 Python
python定间隔取点(np.linspace)的实现
2019/11/27 Python
中东地区为妈妈们提供一切的头号购物目的地:Sprii
2018/05/06 全球购物
自荐信模版
2013/10/24 职场文书
经典大学生求职信范文
2014/01/06 职场文书
2014植树节活动总结
2014/03/11 职场文书
道路施工安全责任书
2014/07/24 职场文书
五年级作文之成长
2019/09/16 职场文书
python3 字符串str和bytes相互转换
2022/03/23 Python
Win11怎么添加用户?Win11添加用户账户的方法
2022/07/15 数码科技