跳转至

以下内容基本上是 AI 生成的,我还没校对,可能质量不高

Cached Proxy

Repo   ·   Source

轻量级 HTTP 缓存代理服务器。用 Python 实现,支持响应内容修改和递归防循环。

Warning

这是一个周末 hack,代码简洁但功能实用。适合本地站点镜像或缓存。

功能

  • 代理 HTTP 请求,缓存响应
  • 修改响应内容,如替换域名
  • 支持递归代理和循环检测

实现

用 Python 写,依赖 pydantichttpx。启动时设置 baseurl 和缓存时间,代理请求时检查缓存,必要时转发并缓存。

cached-proxy 把代理、缓存与响应改写做成了耦合良好的小工具:主程序用 httpx.AsyncClient(http2=True) 发起请求、把响应写入 diskcache 并通过 x-diskcache-hits/misses/age 等 header 报告缓存元数据,decorate_body / decorate_headers 则负责把返回体或 Location 头内的 base URL 做可配置替换,方便做镜像或内嵌资源替换。递归抓取逻辑由 recursion.py 实现,使用 visited_urls 避免循环爬取。配置使用 pydantic-settings 驱动(env.py 定义了 baseurl、min_age、replace 等字段),整体设计适合轻量站点镜像或开发时的缓存代理场景。

代理核心逻辑

main.py 中的缓存和响应处理:

# main.py
async def fetch(url: str):
    cache_key = url
    hit = cache.get(cache_key)

    hits, misses = cache.stats()
    common_headers = {"x-diskcache-hits": str(hits), "x-diskcache-misses": str(misses)}

    if not hit or env.min_age and (age := time() - hit["timestamp"]) > env.min_age:
        res = await client.get(url)

        res_body = res.read()
        cache.set(
            cache_key,
            {
                "body": compress(res_body, 1, 9, Filter.NOFILTER, Codec.LZ4),
                "headers": dict(res.headers),
                "status": res.status_code,
                "timestamp": time(),
            },
        )
        return make_response(res_body, res_status, res_headers)

    return make_response(decompress(hit["body"]), hit["status"], hit["headers"])

使用 blosc2 的 LZ4 压缩算法实现高效存储。

递归爬虫

recursion.py 中的链接提取和并发控制:

# recursion.py
from bs4 import BeautifulSoup

client = AsyncClient(http2=True, base_url=base_url, timeout=60)
sem = Semaphore(500)
visited_urls = set()

async def crawl(url: str, visited_urls: set):
    if url in visited_urls:
        return

    visited_urls.add(url)
    links = await get_links(url)

    tasks = []
    for i in links:
        if i and is_same_origin(i) and not i.startswith("#"):
            tasks.append(create_task(crawl(i, visited_urls)))

    await gather(*tasks)

使用 BeautifulSoup 解析 HTML,提取所有资源链接。

我的巧思

  • 响应内容重写:用正则替换域名,实现自包含镜像。
  • 递归保护:跟踪 visited_urls,避免无限循环。
  • 轻量无依赖:除了 pydantic,几乎纯标准库。

深入洞见

这个项目是一个极简的 ASGI 反向代理,专注于缓存和重写单个上游网站。通过 diskcache 的 Blosc2 压缩实现高效存储,httpx 支持 HTTP/2。响应体重写使用简单字符串替换,适合静态站点,但对动态内容可能失效。预缓存爬虫使用 BeautifulSoup 递归抓取资源,限制并发和深度。

缓存统计头(x-diskcache-hits/misses/age)提供可观测性,但无驱逐策略可能导致磁盘增长。