Skip to content

Latest commit

 

History

History
959 lines (627 loc) · 23.4 KB

File metadata and controls

959 lines (627 loc) · 23.4 KB

PRD: 基于 Jupyter Notebook 的 From-Scratch HTTP Server 项目

1. 文档信息

1.1 文档目的

本文档用于定义一个基于 Jupyter Notebook (.ipynb) 交付的 HTTP Server 项目规划。项目目标是在尽量不依赖高级框架和现成服务封装的前提下,使用 Python 基础能力构建一个可运行、可观察、可扩展的最小 HTTP 服务系统,并以 Notebook 作为唯一核心实现载体。

本文档将覆盖:

  • 项目目标与边界
  • 交付形式与技术原则
  • 功能需求与非功能需求
  • Notebook 内部结构设计
  • 核心模块拆解
  • 分阶段实施路径
  • 验收标准
  • 风险与约束
  • 后续扩展规划

1.2 目标读者

  • 项目发起人
  • 项目实现者
  • 后续维护者
  • 需要基于该项目继续扩展功能的开发者

1.3 文档范围

本 PRD 只定义:

  • 一个详细的项目规划书 md
  • 一个后续将被实现为 ipynb 的项目结构与功能规范

本 PRD 不直接包含:

  • 最终代码实现
  • 最终 .ipynb 文件内容
  • 自动化部署方案
  • 生产级运维方案

2. 项目概述

2.1 项目名称

基于 Jupyter Notebook 的 From-Scratch HTTP Server

2.2 项目目标

构建一个完全基于 Jupyter Notebook 展开的 HTTP Server 项目,使其具备以下特征:

  • 使用 Python 低层能力完成 HTTP 服务的核心链路
  • 不依赖 Flask、FastAPI、Django、aiohttp、Tornado 等高级 Web 框架
  • 不直接调用现成的高层 HTTP Server 封装作为核心实现
  • 在 Notebook 中逐步构建 socket 监听、请求解析、响应生成、路由分发和静态文件返回能力
  • 形成一套结构清晰、可运行、可验证的基础服务实现

2.3 项目定位

该项目定位为一个“从基础协议与网络连接出发实现 HTTP 服务”的 Notebook 化项目资产,强调:

  • 原理清晰
  • 代码路径透明
  • 模块边界明确
  • 可以逐步演进
  • 每个部分都能单独运行、观察和验证

2.4 核心价值

项目完成后应提供以下价值:

  • 提供一个从底层搭建 HTTP Server 的完整实现蓝图
  • 让后续扩展具备稳定基础,例如增加路由、文件服务、状态码处理、请求头处理等
  • 为后续继续拆解协议细节、错误分支、性能限制提供明确骨架
  • 通过 Notebook 形式保留完整的实现过程与验证结果

3. 项目背景

3.1 问题背景

绝大多数 Python Web 项目都会直接建立在成熟框架之上,这使得 HTTP 服务的底层结构被大量封装,开发效率很高,但不利于从协议与网络层角度建立清晰的系统认知。对于一个以“从零构建”为核心约束的项目,直接使用高级框架会削弱项目目标。

同时,如果完全使用普通 .py 组织实现,虽然更接近工程化代码形态,但不利于在开发初期逐段观察 socket 行为、报文内容、解析过程和中间状态。Notebook 在这个场景下更适合作为阶段性交付形式,因为它可以:

  • 按单元分段执行
  • 展示中间变量和原始字节内容
  • 便于记录请求样例与测试结果
  • 便于逐步演进

3.2 当前状态

当前项目目录为空,尚未存在任何实现文件、设计文档或 Notebook。适合直接按统一规划建立项目结构,不存在兼容旧代码的问题。

3.3 建设方向

本项目将以 Notebook 为唯一核心交付物展开,优先完成单文件、单进程、基础阻塞式 HTTP Server 的实现,再为后续增强能力预留扩展接口。

4. 项目范围

4.1 本期范围

本期规划的实现范围包括:

  • ipynb 中建立 HTTP Server 的完整实现过程
  • 基于 socket 构建 TCP 监听服务
  • 接收客户端连接并读取原始请求数据
  • 解析 HTTP Request Line
  • 解析基础请求头
  • 提取方法、路径、协议版本
  • 构建基础路由分发机制
  • 返回标准 HTTP Response
  • 支持文本响应
  • 支持简单 HTML 响应
  • 支持读取本地静态文件并返回内容
  • 支持基础错误响应,如 404 Not Found400 Bad Request
  • 支持最基本的日志输出
  • 提供 Notebook 内的运行说明、验证方式和结果示例

4.2 本期不包含

以下内容明确不纳入本期实现范围:

  • 不引入 Flask / FastAPI / Django / aiohttp / Tornado
  • 不使用 gunicorn / uvicorn / waitress 作为核心运行器
  • 不实现多进程或多线程并发模型
  • 不实现异步 IO 架构
  • 不实现 HTTPS / TLS
  • 不实现复杂模板引擎
  • 不实现数据库集成
  • 不实现 Session / Cookie 管理体系
  • 不实现生产级静态资源缓存机制
  • 不实现反向代理或负载均衡
  • 不实现完整 RFC 级别 HTTP 协议覆盖
  • 不追求生产环境性能指标

5. 核心原则

5.1 From-Scratch 原则

“from scratch” 在本项目中的具体含义如下:

  • 从 TCP socket 建立服务入口
  • 自行处理连接、读取、解析和响应流程
  • 自行拼接 HTTP 响应报文
  • 自行处理最基础的路由逻辑
  • 自行读取文件并生成合适的响应头与响应体
  • 优先使用 Python 标准库

5.2 禁用或避免的实现方式

为保证项目目标清晰,以下方式应避免作为核心实现:

  • 使用现成框架注册路由
  • 使用 http.server 直接完成整个服务的核心功能
  • 使用高级封装隐藏请求解析过程
  • 使用自动响应对象抽象掉原始 HTTP 报文结构

说明:

允许参考标准库中某些低层模块辅助实现,例如:

  • socket
  • pathlib
  • os
  • mimetypes
  • datetime
  • urllib.parse

但这些模块只能作为基础工具,不能替代核心 HTTP 服务逻辑本身。

5.3 Notebook-first 原则

项目的主实现必须放在 .ipynb 中,且 Notebook 不应只是演示代码片段,而应承担以下角色:

  • 主实现容器
  • 实验过程记录
  • 结果验证载体
  • 文档化执行流程

5.4 可观察性原则

每个关键步骤都应具备可观察性,包括但不限于:

  • 原始请求字节
  • 解码后的请求文本
  • 解析出的 method/path/version
  • 路由匹配结果
  • 返回的状态码
  • 返回头部内容
  • 文件读取路径
  • 异常和错误分支

6. 交付形式

6.1 主要交付物

本项目后续的核心交付物应为:

  • 一个主 Notebook 文件,例如:http_server_from_scratch.ipynb

6.2 辅助交付物

可选的辅助内容包括:

  • 示例静态资源目录,例如 static/
  • 示例 HTML 文件
  • 示例文本文件
  • README 或运行说明

但就本期规划而言,必须优先定义并完成的是 Notebook 本体设计。

6.3 Notebook 的角色定义

Notebook 不是简单说明文档,而是“可执行的项目主体”。因此其内容需要同时满足:

  • 可以从头按顺序执行
  • 每段执行结果有意义
  • 关键变量可输出
  • 结构分层明确
  • 最终能够运行出一个本地 HTTP 服务

7. 用户场景

7.1 场景一:启动本地服务

用户希望通过运行 Notebook 中的若干代码单元,在本地启动一个最小 HTTP Server,并在浏览器中访问指定端口获得页面响应。

7.2 场景二:观察请求解析过程

用户希望看到浏览器发来的原始请求内容,并能在 Notebook 中明确知道请求行、请求头和路径参数是如何被解析出来的。

7.3 场景三:添加新路由

用户希望在 Notebook 的路由映射结构中新增一个路径,例如 /about,并快速让服务返回新的文本或 HTML 内容。

7.4 场景四:返回本地静态文件

用户希望通过 /static/... 形式请求本地文件,并由服务读取文件后返回内容,同时包含基本的 Content-Type

7.5 场景五:验证错误分支

用户希望访问不存在的路径时能够收到 404 响应,请求格式异常时收到 400 响应,从而验证服务具备最基本的鲁棒性。

8. 功能需求

8.1 功能总览

本项目应至少包含以下功能模块:

  1. 服务配置模块
  2. Socket 监听模块
  3. 连接接收模块
  4. 请求读取模块
  5. 请求解析模块
  6. 路由分发模块
  7. 响应构建模块
  8. 静态文件处理模块
  9. 错误处理模块
  10. 日志输出模块
  11. Notebook 运行控制模块

以下章节将逐一拆解。

8.2 服务配置模块

8.2.1 目标

提供服务启动所需的基础参数配置。

8.2.2 需要支持的配置项

  • HOST
  • PORT
  • BUFFER_SIZE
  • ENCODING
  • STATIC_DIR
  • SERVER_NAME
  • DEFAULT_CONTENT_TYPE

8.2.3 功能要求

  • 配置值应集中出现在 Notebook 靠前位置
  • 配置单元执行后可直接被后续所有单元复用
  • 允许通过修改变量快速更换端口和静态目录

8.3 Socket 监听模块

8.3.1 目标

基于 socket 创建 TCP 服务端,完成:

  • 创建 socket
  • 绑定地址
  • 进入监听状态

8.3.2 功能要求

  • 使用 AF_INETSOCK_STREAM
  • 正确调用 bind()
  • 正确调用 listen()
  • 启动后打印监听地址
  • 在 Notebook 中清晰展示服务进入监听状态

8.3.3 约束

  • 本期采用阻塞式实现
  • 默认一次处理一个连接
  • 不处理高并发优化

8.4 连接接收模块

8.4.1 目标

接收客户端连接,获取:

  • 客户端 socket
  • 客户端地址信息

8.4.2 功能要求

  • 调用 accept() 接收连接
  • 输出客户端 IP 和端口
  • 对每次连接提供基本日志

8.4.3 风险点

  • Notebook 环境中长时间阻塞会影响交互体验
  • 需要在 PRD 中预留停止服务和中断单元的说明

8.5 请求读取模块

8.5.1 目标

从客户端连接中读取原始请求数据。

8.5.2 功能要求

  • 使用 recv() 读取字节流
  • 将原始字节保留为中间变量
  • 提供字节到文本的解码流程
  • 对空请求或异常读取情况有基础处理

8.5.3 输出内容

应至少输出以下中间结果:

  • 原始字节串
  • 解码后的完整请求文本
  • 请求文本按行拆分结果

8.6 请求解析模块

8.6.1 目标

将原始 HTTP 请求解析为结构化数据。

8.6.2 解析内容

至少包括:

  • HTTP 方法,例如 GET
  • 请求路径,例如 /
  • 查询字符串,例如 ?name=test
  • 协议版本,例如 HTTP/1.1
  • 请求头键值对

8.6.3 功能要求

  • 正确拆分请求首行
  • 能识别非法首行格式
  • 请求头解析为字典结构
  • 查询参数至少预留解析入口

8.6.4 建议数据结构

建议在 Notebook 中将解析结果组织为类似结构:

request = {
    "method": "GET",
    "path": "/",
    "query_string": "",
    "http_version": "HTTP/1.1",
    "headers": {},
    "raw_text": "...",
}

8.6.5 错误处理要求

当出现以下情况时,应生成 400 Bad Request

  • 请求首行为空
  • 首行字段数量不足
  • 请求方法缺失
  • 协议版本缺失

8.7 路由分发模块

8.7.1 目标

根据请求路径将请求转发到不同处理逻辑。

8.7.2 路由范围

本期至少支持:

  • /
  • /hello
  • /about
  • /static/...

8.7.3 功能要求

  • 使用清晰可读的映射或条件分发方式
  • 对未命中的路径返回 404
  • 将静态资源路径与普通页面路径区分处理

8.7.4 设计原则

  • 路由逻辑应保持简单直观
  • 避免过早抽象为复杂框架风格
  • 处理函数与响应构建逻辑尽量分离

8.8 响应构建模块

8.8.1 目标

手动构建标准 HTTP 响应报文。

8.8.2 必须支持的响应部分

  • Status Line
  • Response Headers
  • 空行分隔
  • Response Body

8.8.3 功能要求

  • 能返回 200 OK
  • 能返回 404 Not Found
  • 能返回 400 Bad Request
  • 至少支持 Content-Type
  • 至少支持 Content-Length
  • 可选支持 Connection: close

8.8.4 实现要求

  • 响应头必须按 HTTP 文本格式拼接
  • 响应体为字节时,应正确计算长度
  • 字符串响应需统一编码

8.8.5 输出结果

应明确区分:

  • 响应头文本
  • 响应体字节
  • 最终发送给客户端的完整字节流

8.9 静态文件处理模块

8.9.1 目标

允许通过 URL 路径访问本地静态文件。

8.9.2 最小支持范围

  • 文本文件
  • HTML 文件
  • CSS 文件
  • JavaScript 文件
  • 常见图片文件类型可作为扩展项

8.9.3 功能要求

  • /static/... 中提取相对文件路径
  • 拼接本地静态目录
  • 判断文件是否存在
  • 正确读取文件内容
  • 根据扩展名推断 Content-Type

8.9.4 安全要求

必须考虑基础路径安全,避免明显的目录穿越问题,例如:

  • ../
  • 绝对路径逃逸
  • 非法路径拼接

8.9.5 错误处理要求

  • 文件不存在返回 404
  • 文件读取失败返回 500 或本期约定错误响应

8.10 错误处理模块

8.10.1 目标

覆盖最关键的失败路径,保证服务在最小范围内可控。

8.10.2 需要处理的错误类型

  • 非法请求格式
  • 路径未命中
  • 文件不存在
  • 文件读取失败
  • 编码异常
  • socket 通信异常

8.10.3 功能要求

  • 错误必须有明确状态码
  • 错误必须有可读响应体
  • Notebook 中必须有日志输出,便于定位问题

8.11 日志输出模块

8.11.1 目标

提供最基本的运行日志,便于观察服务行为。

8.11.2 至少输出的信息

  • 服务启动地址
  • 收到连接的客户端地址
  • 请求方法
  • 请求路径
  • 返回状态码
  • 错误原因

8.11.3 日志原则

  • 优先简洁清晰
  • 输出必须足够支持调试
  • 不要求接入完整 logging 系统,可先使用基础打印

8.12 Notebook 运行控制模块

8.12.1 目标

保证 Notebook 内的服务启动、停止、重复运行具备基本可控性。

8.12.2 功能要求

  • 明确哪个单元负责启动服务
  • 明确服务启动后将持续阻塞
  • 提供停止方式说明
  • 说明重复运行前可能需要先中断旧实例

8.12.3 风险说明

Notebook 与常规脚本不同,服务运行单元可能长期占用执行状态,因此必须在文档中明确操作顺序和中断方式。

9. 非功能需求

9.1 可读性

Notebook 中的代码必须具备较高可读性:

  • 变量名清晰
  • 单元边界明确
  • 每一段实现有明确职责
  • 必要时有简短注释

9.2 可执行性

从头顺序执行 Notebook 时,应能够在合理环境下成功启动服务并完成基础访问验证。

9.3 可理解性

关键实现步骤应尽量展开,而不是过度封装为黑盒函数。即使后续会抽函数,也应保留足够可见的处理链路。

9.4 可扩展性

虽然项目本期只实现基础能力,但结构上应能支持后续增加:

  • 更多路由
  • 更多状态码
  • 更完整的请求头解析
  • POST 请求处理
  • 更丰富的静态文件能力

9.5 稳定性

本期不要求生产级稳定性,但至少应满足:

  • 常规 GET 请求可正常处理
  • 非法请求不导致整个 Notebook 逻辑失控
  • 不存在明显的路径拼接安全漏洞

10. 技术方案原则

10.1 技术栈

推荐技术边界如下:

  • 语言:Python
  • 交付:Jupyter Notebook (.ipynb)
  • 核心网络:socket
  • 路径处理:pathlibos.path
  • MIME 推断:mimetypes
  • 查询参数辅助解析:urllib.parse

10.2 明确不使用

  • Flask
  • FastAPI
  • Django
  • aiohttp
  • Tornado
  • Bottle
  • Sanic
  • uvicorn
  • gunicorn

10.3 关于标准库 http.server

本项目原则上不应以 http.server 为核心方案,因为这会明显削弱“from scratch”的目标。若后续作为对照参考,可以单独说明,但不能作为主实现路径。

11. Notebook 结构设计

11.1 整体结构目标

Notebook 应按“先定义,再验证,最后运行”的逻辑组织,而不是随意堆叠代码单元。

11.2 建议章节结构

建议最终 Notebook 至少包含以下章节:

  1. 项目说明
  2. 依赖与约束说明
  3. 全局配置
  4. HTTP 基础格式说明
  5. 创建 socket 服务
  6. 接收并打印原始请求
  7. 解析请求行与请求头
  8. 构建响应函数
  9. 构建路由函数
  10. 增加静态文件处理
  11. 整合主循环
  12. 本地验证说明
  13. 已知限制

11.3 每章作用说明

11.3.1 项目说明

说明项目目标、运行方式、技术原则和边界。

11.3.2 依赖与约束说明

说明仅使用 Python 标准库,以及不使用高级框架的约束。

11.3.3 全局配置

定义端口、编码、缓冲区大小、静态目录等常量。

11.3.4 HTTP 基础格式说明

说明请求报文和响应报文的基础结构,帮助理解后续解析逻辑。

11.3.5 创建 socket 服务

完成 socket 创建、绑定、监听,并打印服务地址。

11.3.6 接收并打印原始请求

建立 accept 和 recv 的最小闭环,先看到请求,再谈解析。

11.3.7 解析请求行与请求头

将原始文本处理成结构化对象。

11.3.8 构建响应函数

实现统一的响应报文拼接函数。

11.3.9 构建路由函数

根据路径返回不同内容。

11.3.10 增加静态文件处理

处理本地文件访问和 Content-Type 推断。

11.3.11 整合主循环

把前面所有函数串起来,形成可持续运行的服务。

11.3.12 本地验证说明

说明如何在浏览器中访问、如何测试 404、如何验证静态文件。

11.3.13 已知限制

列出单线程、阻塞式、协议支持不完整等约束。

12. 核心模块详细设计要求

12.1 请求对象设计

Notebook 中建议形成一个统一的请求对象,用于在各处理阶段之间传递上下文。

建议字段包括:

  • method
  • raw_path
  • path
  • query_string
  • query_params
  • http_version
  • headers
  • raw_bytes
  • raw_text
  • client_address

12.2 响应对象设计

虽然不要求实现复杂类结构,但建议统一响应数据格式,至少在函数间保持如下信息一致:

  • status_code
  • reason_phrase
  • headers
  • body

12.3 路由处理函数设计

建议每个路由处理逻辑尽量保持单一职责,例如:

  • 首页处理函数只返回首页内容
  • hello 路由只负责返回简单文本
  • 静态文件处理函数只负责文件定位与读取

12.4 主循环设计

主循环至少应包含:

  1. 接收连接
  2. 读取请求
  3. 解析请求
  4. 分发路由
  5. 构建响应
  6. 发送响应
  7. 关闭连接
  8. 输出日志

12.5 连接关闭策略

本期建议采用简单策略:

  • 每次请求处理完成后关闭连接
  • 不优先支持 keep-alive

这样可以显著降低实现复杂度,保持核心逻辑可控。

13. 页面与返回内容设计

13.1 首页 /

首页应返回一个简单 HTML 页面,用于证明:

  • 服务已成功启动
  • 浏览器可以正常访问
  • HTML 响应构建有效

页面内容不要求复杂,但应包含:

  • 页面标题
  • 服务运行提示
  • 若干可访问路径说明

13.2 /hello

返回纯文本响应,用于验证:

  • 文本类型响应
  • Content-Type: text/plain
  • 基础路由分发

13.3 /about

返回另一段 HTML 或文本内容,用于验证多路由支持。

13.4 /static/...

返回本地文件,用于验证:

  • 文件路径映射
  • 文件读取
  • MIME 类型识别
  • 二进制或文本内容发送

13.5 错误页

建议对 404400 返回简洁但明确的内容,例如:

  • 状态码
  • 错误名称
  • 简短说明

14. 验收标准

14.1 文档级验收标准

PRD 本身应满足:

  • 结构完整
  • 范围明确
  • 交付形式明确
  • 技术原则明确
  • 实施路径明确
  • 验收标准可执行

14.2 Notebook 级验收标准

后续实现 Notebook 时,应满足以下最低验收条件:

  1. Notebook 能从头顺序执行
  2. 能成功启动本地服务
  3. 访问 / 返回 200
  4. 访问 /hello 返回 200
  5. 访问不存在路径返回 404
  6. 至少一个 /static/... 资源可访问
  7. 能在 Notebook 输出中看到基本请求日志
  8. 不依赖任何高级 Web 框架

14.3 实现质量验收标准

  • 代码单元职责明确
  • 没有把全部逻辑混在一个超长单元中
  • 请求解析与响应构建是可单独理解的
  • 错误分支至少覆盖 400404

15. 分阶段实施计划

15.1 第一阶段:建立最小服务闭环

目标:

  • 创建 socket
  • 启动监听
  • 接收一次请求
  • 返回固定文本响应

完成标志:

  • 浏览器访问后能收到基础响应

15.2 第二阶段:加入请求解析

目标:

  • 解析请求行
  • 解析请求头
  • 输出 method、path、version

完成标志:

  • 根据不同路径识别请求目标

15.3 第三阶段:加入路由机制

目标:

  • 支持 //hello/about
  • 路由未命中返回 404

完成标志:

  • 访问不同路径得到不同响应

15.4 第四阶段:加入静态文件功能

目标:

  • 支持 /static/...
  • 返回本地文件内容
  • 根据扩展名设置 Content-Type

完成标志:

  • 浏览器可直接访问一个本地 HTML 或 CSS 文件

15.5 第五阶段:完善错误处理与说明

目标:

  • 增加 400404 等错误响应
  • 输出基础日志
  • 完成 Notebook 的运行说明与限制说明

完成标志:

  • Notebook 成为一个完整、可重复执行的项目成品

16. 风险与约束

16.1 Notebook 长时间阻塞风险

服务主循环在 Notebook 中启动后会持续运行,可能影响交互体验。需要在实现中明确说明中断方式。

16.2 浏览器请求复杂性

现代浏览器发送的请求通常包含较多请求头,甚至可能发起额外资源请求。项目实现需要聚焦最关键字段,避免一开始试图完整覆盖所有浏览器行为。

16.3 路径安全风险

静态文件服务如果路径拼接不严谨,容易出现目录穿越问题。因此静态文件处理必须在设计上优先考虑路径边界。

16.4 协议完整性限制

本项目不会在本期实现完整 HTTP 协议支持,因此:

  • 只保证最基础的 GET 场景
  • 不承诺处理所有客户端边缘行为
  • 不承诺兼容复杂长连接语义

16.5 性能限制

阻塞式、单连接处理模式天然不适合高并发场景,因此本项目只追求实现清晰与正确性,不追求吞吐表现。

17. 成功标准

项目被视为成功的标准包括:

  • 有一份足够详细、可直接执行后续开发的规划书
  • 后续实现者无需再补总体架构即可开始构建 Notebook
  • 项目目标、边界、实现顺序和验收方式全部明确
  • “from scratch” 的约束在文档中得到清晰落实
  • Notebook 作为唯一核心交付形态得到完整定义

18. 后续扩展方向

在本期目标完成后,可以考虑的扩展包括:

  • 支持 POST
  • 支持请求体读取
  • 支持表单解析
  • 支持 JSON 响应
  • 支持更规范的响应头体系
  • 支持更完善的 MIME 类型处理
  • 支持目录索引页
  • 支持简单模板渲染
  • 支持多连接处理模型
  • 支持更细粒度日志系统

19. 最终结论

本项目应被定义为一个基于 Jupyter Notebook 的、以 Python 标准库和底层 socket 能力为核心的 HTTP Server 构建项目。其重点不在于快速得到一个现成 Web 应用,而在于完整、透明、可观察地搭建出一个最小但结构完整的 HTTP 服务系统。

在实现过程中,必须始终坚持以下四点:

  • .ipynb 作为核心交付物
  • socket 和基础标准库作为主要技术手段
  • 不引入高级 Web 框架作为核心实现
  • 优先保证过程清晰、结构明确和可验证性

基于以上原则,本文档已给出完整的范围定义、模块拆解、结构设计、实施路径与验收标准,可直接作为后续 Notebook 实现的正式规划依据。