7.5 KiB
Feature Specification: HTTP 服务与单仓代理
Feature Branch: 002-fiber-single-proxy
Created: 2025-11-13
Status: Draft
Input: User description: "HTTP 服务与单仓代理 - 使用 Fiber 搭建 HTTP 服务,支持基于 Host 的路由到单一 Hub。 - 实现文件缓存模块(读写、TTL 检查),完成命中/回源流程。 - 提供 Docker Hub/NPM 任一仓库的最小可用代理,并通过集成测试验证。"
宪法对齐(v1.0.0):
- 保持“轻量、匿名、CLI 多仓代理”定位:不得引入 Web UI、账号体系或与代理无关的范围。
- 方案必须基于 Go 1.25+ 单二进制,依赖仅限 Fiber、Viper、Logrus/Lumberjack 及必要标准库。
- 所有行为由单一
config.toml控制;若需新配置项,需在规范中说明字段、默认值与迁移策略。- 设计需维护缓存优先 + 流式传输路径,并描述命中/回源/失败时的日志与观测需求。
- 验收必须包含配置解析、缓存读写、Host Header 绑定等测试与中文注释交付约束。
User Scenarios & Testing (mandatory)
User Story 1 - Host 路由下的单仓访问 (Priority: P1)
企业内开发者希望通过 docker.hub.local 或 npm.hub.local 这样的 Host 头访问本地代理,系统需要根据 Host 定位唯一 Hub,并把请求透明转发至上游。
Why this priority: 没有稳定的 HTTP 入口和 Host 路由,就无法承载任何代理能力,是 Phase 1 的核心目标。
Independent Test: 启动 any-hub,准备含单一 Hub 的配置,使用 curl -H "Host: docker.hub.local" http://127.0.0.1:5000/v2/_catalog,验证请求进入正确的 Handler 并记录结构化日志。
Acceptance Scenarios:
- Given 配置声明 Hub
docker监听端口 5000,When 客户端携带Host: docker.hub.local访问,Then Fiber 将请求路由到 docker Hub,并构造正确的上游 URL。 - Given 未声明的 Host,When 客户端请求,Then 立即返回 404 并在日志中标记
host_unmapped。
User Story 2 - 磁盘缓存与回源流程 (Priority: P1)
CI/CD 任务需要重复下载相同镜像或包,期望 any-hub 能在本地缓存结果:命中时直接返回,过期或未命中时回源并刷新缓存,同时保持流式传输。
Why this priority: 缓存是代理节省带宽与加速的唯一方式;缺失会让 Phase 1 成为普通转发层。
Independent Test: 使用集成测试模拟上游服务器,第一次请求写入缓存,第二次命中缓存并快速返回;设置 TTL 过期后触发 revalidate。
Acceptance Scenarios:
- Given 缓存文件存在且未过期,When 再次请求相同路径,Then 直接从磁盘流式返回,并记录
cache_hit=true。 - Given 缓存过期,When 新请求到达,Then 先向上游发送带条件的请求;若上游 304,则回退本地缓存,若 200 则边回源边写磁盘与客户端。
- Given 回源失败或磁盘写入错误,Then 系统返回合理的 5xx 并记录
cache_hit=false与错误原因。
User Story 3 - 最小 Docker/NPM 代理样例 (Priority: P2)
平台运维需要一份可运行的示例,让团队快速验证 Docker 或 NPM 仓库能通过 any-hub 获取常见资源,并在 CI 中运行端到端测试确保回源逻辑可靠。
Why this priority: 实际仓库样例可以验证配置、日志、缓存整体流程,也为后续多仓扩展提供可复制模板。
Independent Test: 提供 configs/docker.sample.toml 或 configs/npm.sample.toml,在集成测试中启动临时上游(可模拟 docker registry 或 npm registry),通过 HTTP 调用完成一次拉取并校验缓存目录生成。
Acceptance Scenarios:
- Given 示例配置启用 Docker Hub,When 运行 quickstart 脚本,Then 可以从真实或模拟上游下载一个 manifest 并写入缓存目录。
- Given 示例配置选择 NPM,When 执行
npm view foo指向代理,Then CLI 能收到正确响应,日志显示命中/回源信息。
Edge Cases
- 配置监听端口但 Host 头缺失或大小写异常:必须直接返回 404,并在日志中记录
host_unmapped字段,禁止回退默认 Hub。 - 大文件下载中途中断:需要保证写入临时文件并在失败时清理,避免污染缓存。
- TTL 设为 0(永远回源)或非常大:需要解释行为并防止整数溢出。
- 上游返回 4xx/5xx:缓存不得写入,同时应透传状态码。
Requirements (mandatory)
Functional Requirements
- FR-001: 系统必须提供基于 Fiber 的 HTTP Server,监听配置声明的端口,并按照
Host→Hub的映射路由所有请求。 - FR-002: 对于匹配的 Hub,请求路径、查询、方法、Headers 必须重新组装为上游 URL,并保持匿名代理(不注入用户标识)。
- FR-003: 缓存模块必须在磁盘上以
StoragePath/<Hub>/<path>结构保存内容,并为每个条目维护元数据(TTL、ETag/Last-Modified、写入时间)。 - FR-004: 命中缓存时必须流式读取磁盘并返回;未命中或过期时需边回源边写入磁盘,并在完成前向客户端持续输出,避免全量加载内存。
- FR-005: 系统必须支持条件请求:若缓存存在 ETag/Last-Modified,回源时附带
If-None-Match/If-Modified-Since,收到 304 时回退缓存。 - FR-006: 任一请求都要记录结构化日志字段(hub、domain、cache_hit、upstream_status、elapsed_ms),并在错误时附带原因。
- FR-007: 提供至少一个 Docker 或 NPM 的示例配置与 quickstart 说明,包含端到端集成测试脚本,证明可从代理获取真实或模拟数据。
- FR-008: 所有配置项(端口、Host、TTL、缓存目录)必须在
config.toml中声明,CLI 不引入新的隐式参数。
Key Entities (include if feature involves data)
- HubRoute: 映射 Host/端口到具体上游信息,字段包括
Name,Domain,Port,Upstream,Proxy,CacheTTL。 - CacheEntry: 表示磁盘缓存文件与
.meta元数据(ETag、Last-Modified、StoredAt、Size、Checksum)。 - ProxyRequest: 记录一次代理请求生命周期(原始 URL、Host、缓存命中状态、上游响应码、耗时)。
- SampleConfig: 示例配置集合,用于定义 Docker/NPM Hub 所需的字段和默认值。
Success Criteria (mandatory)
Measurable Outcomes
- SC-001: 针对同一资源的第二次请求延迟较首次下降 ≥70%,且日志显示
cache_hit=true。 - SC-002: Host 路由能在 100% 测试用例中将请求映射到正确 Hub,未配置的 Host 返回 404,误路由率为 0。
- SC-003: 在示例配置下,端到端集成测试成功率达到 100%,并能在 2 分钟内完成一次 docker 或 npm 包的完整拉取。
- SC-004: 缓存目录在异常情况下不产生损坏文件,测试覆盖包括中断写入、上游错误、TTL 过期等至少 5 个边界场景。
Assumptions
- Phase 1 仅需支持单一 Hub 路由;多 Hub 并行将在后续阶段扩展。
- 上游仓库需支持 HTTP/HTTPS GET/HEAD,暂不支持 WebSocket、Chunked 上传等复杂协议。
- 示例代理默认指向公共 Docker Hub;若网络受限,可在 quickstart 中改为模拟上游。
- 磁盘缓存容量和清理策略仍沿用全局配置,不在本次迭代扩展淘汰算法。
Clarifications
Session 2025-11-13
- Q: Host 头缺失或未匹配时应如何处理? → A: 一律返回 404 并记录
host_unmapped,不允许回退默认 Hub。