V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
sujin190
V2EX  ›  Python

造个小轮子,通过 proxy 方式自动在网页中静态资源后面加上时间戳,强制刷新静态资源

  •  
  •   sujin190 ·
    snower · 2015-01-14 15:30:45 +08:00 · 2793 次点击
    这是一个创建于 3694 天前的主题,其中的信息可能已经有所发展或是发生改变。
    # -*- coding: utf-8 -*-
    # 14/12/16
    # create by: snower
    import sys
    import time
    import re
    import logging
    import traceback
    from tornado.ioloop import IOLoop
    from tornado.httpserver import HTTPServer
    from tornado.web import Application, RequestHandler
    from tornado.httpclient import HTTPError
    from tornado import gen
    from tornado.httpclient import AsyncHTTPClient, HTTPRequest
    application = None
    server = None
    ioloop = None
    PORT = sys.argv[1]
    PROXY_PASS = sys.argv[2]
    def request(method):
    @gen.coroutine
    def _(self, *args, **kwargs):
    try:
    yield method(self, *args, **kwargs)
    except Exception as e:
    self.set_status(200)
    tb = traceback.format_exc()
    tb = "".join(["<p>%s</p>\n" % _tb for _tb in tb.split("\n")])
    self.finish(u'''
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no" />
    <div>proxy error: %s</div>\n<div>%s</div>'''
    % (e, tb))
    return _
    class ProxyRequestHandler(RequestHandler):
    rs = (
    re.compile(r'[\'"](.+?\/.+?\.html.*?)[\'"]'),
    re.compile(r'[\'"](.+?\/.+?\.js.*?)[\'"]'),
    re.compile(r'[\'"](.+?\/.+?\.css.*?)[\'"]'),
    re.compile(r'[\'"](.+?\/.+?\.(png|jpeg|jpg|gif|webp).*?)[\'"]'),
    )
    def check_etag_header(self):
    return False
    def set_etag_header(self):
    return
    @request
    @gen.coroutine
    def head(self, url):
    response = yield self.fetch(url, 'head')
    yield self.write_response(response)
    @request
    @gen.coroutine
    def get(self, url):
    response = yield self.fetch(url, 'get')
    if not response.error:
    body = self.rtpath(response.body)
    else:
    body = None
    yield self.write_response(response, body)
    @request
    @gen.coroutine
    def post(self, url):
    response = yield self.fetch(url, 'post')
    yield self.write_response(response)
    @request
    @gen.coroutine
    def delete(self, url):
    response = yield self.fetch(url, 'delete')
    yield self.write_response(response)
    @request
    @gen.coroutine
    def patch(self, url):
    response = yield self.fetch(url, 'patch')
    yield self.write_response(response)
    @request
    @gen.coroutine
    def put(self, url):
    response = yield self.fetch(url, 'put')
    yield self.write_response(response)
    @request
    @gen.coroutine
    def options(self, url):
    response = yield self.fetch(url, 'options')
    yield self.write_response(response)
    @gen.coroutine
    def fetch(self, url, method):
    client = AsyncHTTPClient()
    request = HTTPRequest(PROXY_PASS + self.request.uri, method.upper(), self.request.headers, self.request.body or None, connect_timeout=5, request_timeout=5, follow_redirects=False, max_redirects=0)
    try:
    response = yield client.fetch(request)
    except HTTPError as e:
    response = e.response
    raise gen.Return(response)
    @gen.coroutine
    def write_response(self, response, body=None):
    self.set_status(response.code, response.reason)
    for key, value in response.headers.iteritems():
    if key != 'Content-Length':
    self.set_header(key, value)
    self.finish(response.body or None if body is None else body)
    def rtpath(self, body):
    for r in self.rs:
    urls = re.findall(r, body)
    for url in urls:
    if isinstance(url, tuple):
    url = url[0]
    rurl = url+"&ts="+str(int(time.time())) if "?" in url else url+"?ts="+str(int(time.time()))
    body = re.sub(url, rurl, body)
    return body
    urls = (
    (r'^(.*)$',ProxyRequestHandler),
    )
    def start():
    global application, server, ioloop
    application = Application(urls, debug=False, autoreload=False)
    server = HTTPServer(application, xheaders=True)
    server.bind(PORT)
    server.start()
    ioloop = IOLoop.instance()
    ioloop.start()
    def stop():
    global application, server, ioloop
    def _():
    application.stop()
    server.stop()
    def stop_loop():
    now = time.time()
    if ioloop._callbacks or ioloop._timeouts:
    ioloop.add_timeout(now + 0.5, stop_loop)
    else:
    ioloop.stop()
    stop_loop()
    ioloop.add_callback_from_signal(_)
    if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO, format="%(asctime)s %(process)d %(levelname)s %(message)s")
    start()
    view raw rproxy.py hosted with ❤ by GitHub
    2 条回复    2015-01-15 09:41:42 +08:00
    jones
        1
    jones  
       2015-01-15 08:06:16 +08:00 via Android
    并发量上来性能堪忧啊,所有的响应内容都去扫一遍html响应内容查找一遍静态资源后缀,python的正则和字符串运算现在性能真的牛叉到可以忽略并发任性使用了?静态资源版本号还是前端构建工具发布的时候添加上比较好
    sujin190
        2
    sujin190  
    OP
       2015-01-15 09:41:42 +08:00   ❤️ 1
    @jones 这个只是用于调试的时候了,比如微信调试的时候就比较坑,没办法发刷缓存,每次改了都不生效,又没办法在pc上调试,真的到了线上,当然还是使用构建工具了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   982 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 20:35 · PVG 04:35 · LAX 12:35 · JFK 15:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.