實現代理的方式很多種,流行的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.httpserverimport tornado.ioloopimport tornado.optionsimport tornado.webimport tornado.httpclientfrom tornado.web import HTTPError, asynchronousfrom tornado.httpclient import HTTPRequestfrom tornado.options import define, optionstry: from tornado.curl_httpclient import CurlAsyncHTTPClient as AsyncHTTPClientexcept 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()
新聞熱點
疑難解答