Show HN: 用汇编语言构建 Web 服务器,为我的生命赋予(些许缺乏的)意义
摘要
ymawky 是一个专为 macOS 编写的 ARM64 汇编 Web 服务器,其特点是不依赖 libc 仅使用系统调用,并具备基本的 HTTP 功能。
查看缓存全文
缓存时间: 2026/05/10 03:39
imtomt/ymawky 来源:https://github.com/imtomt/ymawky
ymawky – ARM 汇编编写的 Web 服务器
这是 ymawky(发音:yuh maw kee),一个完全用 ARM64 汇编语言编写的 Web 服务器。ymawky 是一个仅使用系统调用、不依赖 libc、每连接一次 fork 的 Web 服务器,由纯手工编写。虽然它主要为 macOS 开发,但我已尽量使其保持高可移植性——但是,你很可能仍然需要进行一些 (希望是微小的) 重大调整才能在 Linux/其他 Unix 系统上运行它。详见实现笔记。
构建
需要 Xcode 命令行工具。通过 xcode-select --install 安装。
ymawky 仅能在 Apple Silicon (arm64) 上运行。运行 make 进行构建。
确保在 ymawky 可执行文件旁边有一个 www/ 目录。这是文档根目录,ymawky 将在此处查找文件。带有空文件名的 GET 请求 (GET /) 将搜索 www/index.html,因此你可能需要确保存在一个 index.html 文件。
当客户端请求出错时(例如 404),ymawky 会尝试提供静态错误页面。它在 err/(code).html 中搜索这些页面,因此请确保 err/ 目录存在于 ymawky 和 www/ 旁边。
见配置以修改默认文件和文档根目录。
运行
- 运行
./ymawky以在127.0.0.1:8080上启动 Web 服务器。 - 运行
./ymawky [port]以在127.0.0.1:[port]上启动 Web 服务器。 - 运行
./ymawky [literally-any-character-other-than-0-9](任何非数字字符)以在127.0.0.1:8080上以调试模式启动 Web 服务器。调试模式禁用了 fork,使 ymawky 仅处理一个请求。(我不得不这样做,因为lldb不允许我调试子进程,唉。)
不幸的是,虽然支持自定义端口,但不支持自定义地址。目前,ymawky 只能在 127.0.0.1 上运行。这纯粹是因为我尚未实现该功能——但如果你愿意将其视为一种安全特性,那也可以说是有意为之。
要查看 ymawky 的效果,使用 ./ymawky [port] 启动 ymawky。然后打开你选择的网页浏览器(或使用 curl),访问 127.0.0.1:8080/ 或 127.0.0.1:8080/pretty/index.html。沐浴在汇编的温暖中吧。
它能做什么?
ymawky 是一个静态文件 Web 服务器。它不支持生成动态内容的服务端代码,也不支持更高级的 URL 解析,例如 /search?query=term。但这并不意味着它功能缺失。
- 支持的 HTTP 方法:
- GET
- PUT
- DELETE
- OPTIONS
- HEAD
- 基本防护,抵御类似 slowloris 的拒绝服务攻击
- 解码
%十六进制编码,例如,文件名中%20解码为空格,%61解码为a - 智能路径遍历检测与防止。阻止
..遍历路径,但在多个点作为文件名一部分时不禁止:GET /../../../etc/passwd->403 ForbiddenGET /ohwell...txt->200 OKGET /../src/ymawky.S->403 ForbiddenGET /hehe..txt->200 OK
- 自动在请求的文件前添加
www/。GET /index.html将检索www/index.html - 空的
GET /请求默认为GET www/index.html PUT请求支持高达 1GiB 的上传,尽管这可以配置以支持更大的文件PUT是原子的,因为它是先写入临时文件然后重命名,允许并发的PUT请求而不会留下部分写入的文件PUT请求中Content-Length:的解析和验证- MIME 类型检测,在响应头中提供相应的
Content-Type和 MIME 类型 - 接受 GET 请求中的
Range: bytes=范围,支持完整范围bytes=X-N、后缀范围bytes=-N和开放范围bytes=X-。视频拖拽播放支持良好 - 基本 HTTP 版本解析。请求需要指定
HTTP/1.1或HTTP/1.0,如果请求HTTP/1.1,则头部必须存在Host:字段。目前,ymawky 不使用 Host,但根据 RFC 9112 第 3.2 节,必须发送该头 - 为错误代码(如 404 或 500)提供自定义 HTML 页面。查看
err/目录中的示例 - 如果请求的资源是目录,则列出目录中的所有文件和子目录。注意,这排除了
www/(或你的文档根目录):如果没有给出文件,GET /将始终搜索index.html。
“安全性”
这是一个作为趣味项目完全用 ARM64 汇编手工编写的 Web 服务器。它可能有很多我不知道的漏洞。然而,我已尽力使其更加安全。以下是 ymawky 采取的一些安全措施。
- 拒绝路径长度 >= PATH_MAX (4096 字节)
- 拒绝任何包含路径遍历的路径 –
/../.. - 拒绝任何在 16 字节内不包含路径的请求
- 限制在
www/内。任何请求的路径都会在其前添加www/ - 拒绝任何包含符号链接的路径,使用
O_NOFOLLOW_ANY PUT写入临时文件www/.ymawky_tmp_。成功接收整个文件后,该临时文件将被重命名为请求的文件名。这防止了部分或损坏的PUT请求覆盖现有文件。- 拒绝任何以
www/.ymawky_tmp_开头的路径。这防止有人通过GET获取临时文件,并防止有人发送PUT /.ymawky_tmp_4533之类的请求。 - 必须在 10 秒内接收数据。如果更慢,连接将关闭。如果在总共 10 秒内未收到完整头部,连接将关闭。这是为了防止类似 slowloris 的攻击。
HTTP 状态码
ymawky 目前支持并可以回复以下状态码:
200 OK201 Created204 No Content206 Partial Content400 Bad Request403 Forbidden404 Not Found408 Request Timeout409 Conflict411 Length Required413 Content Too Large414 URI Too Long416 Range Not Satisfiable418 I'm a teapot431 Request Header Fields Too Large500 Internal Server Error501 Not Implemented503 Service Unavailable505 HTTP Version Not Supported507 Insufficient Storage
自定义 HTML 页面将与错误代码 (400+) 一起提供服务。这些 HTML 文件位于 err/(code).html。你可以使用 build_err_pages.sh 为每个代码创建一个页面,并根据需要更改文本。编辑 build_err_pages.sh 的源代码以修改每页文本,并修改 err/template.html 以修改基本模板。
在 err/template.html 中:
{{CODE}}- HTTP 代码:例如,404{{TITLE}}- 标题文本:例如,“Not Found”{{MSG}}- 自定义消息:例如,“the rats ate this page”
MIME 类型
MIME 类型通过分析文件扩展名来检测。识别以下 MIME 类型。
Web 相关文件:
.html->text/html; charset=utf-8.htm->text/html; charset=utf-8.css->text/css; charset=utf-8.csv->text/csv; charset=utf-8.xml->text/xml; charset=utf-8.js->text/javascript; charset=utf-8.json->application/json.wasm->application/wasm.mjs->text/javascript; charset=utf-8.map->application/json
图片文件:
.png->image/png.jpg->image/jpeg.jpeg->image/jpeg.gif->image/gif.svg->image/svg+xml.ico->image/x-icon.webp->image/webp.avif->image/avif.bmp->image/bmp.tiff->image/tiff.apng->image/apng
字体文件:
.woff->font/woff.woff2->font/woff2.ttf->font/ttf.otf->font/otf
文档文件:
.txt->text/plain; charset=utf-8.pdf->application/pdf.doc->application/msword.docx->application/vnd.openxmlformats-officedocument.wordprocessingml.document.epub->application/epub+zip.rtf->application/rtf
视频文件:
.mp4->video/mp4.webm->video/webm.mkv->video/x-matroska.avi->video/x-msvideo.mov->video/quicktime
音频文件:
.mp3->audio/mpeg.ogg->audio/ogg.wav->audio/wav.flac->audio/flac.aac->audio/aac.m4a->audio/mp4.opus->audio/opus
归档文件:
.zip->application/zip.gz->application/gzip.tar->application/x-tar.7z->application/x-7z-compressed.bz2->application/x-bzip2.rar->application/vnd.rar
配置
你可以使用 config.S 文件配置 ymawky。选项如下所述。
#define DEFAULT_DIR "www/"– 这是文档根目录。将其更改为相对于 ymawky 的 HTML 文件所在位置,或使用绝对路径:#define DEFAULT_DIR "www/"#define DEFAULT_DIR "/Library/WebServer/Documents"#define DEFAULT_DIR "./"
#define DEFAULT_ERR_DIR "err/"– 这是 ymawky 将搜索自定义错误 HTML 页面的目录,例如err/404.html或err/500.html#define DEFAULT_FILE "index.html"– 这是 ymawky 在收到空GET / HTTP/1.1请求时将提供的默认文件.equ RECV_TIMEOUT, 10– ymawky 在关闭连接前等待接收数据的秒数。如果两次read()之间超过RECV_TIMEOUT秒,ymawky 将使用408 Request Timed Out关闭连接.equ HEADER_REQ_TIMEOUT_SECS, 10– ymawky 等待接收完整头部的最大秒数。如果接收头部所需时间超过此值,ymawky 将使用408 Request Timed Out关闭连接.equ PUT_GRACE_SECS, 5– ymawky 根据Content-Length动态计算每个 PUT 的最大时间。最大时间定义为PUT_GRACE_SECS + Content-Length / PUT_MIN_BPS。如果计算出的文件上传时间 <1 秒,这是允许的最小宽限期.equ PUT_MIN_BPS, 1024 * 16– 最小每秒字节数。想要更严格则调高,想要更宽松则调低。由于这使用了.equ指令,支持算术运算,1024 * 16将在汇编时计算为16384或 16KB.equ MAX_BODY_SIZE, 1024 * 1024 * 1024– PUT 允许的 Content-Length 最大字节数。默认值为 1GB (102410241024 = 1073741824 字节)。Content-Length 大于此值的文件将被拒绝,并返回413 Content Too Large.equ MAX_PROCS, 256– ymawky 允许运行的最大并发进程数。由于 ymawky 是一个每连接一次 fork 的服务器,你需要确保 ymawky 不会耗尽你的 PID 空间。ymawky 将回复503 Service Unavailable
实现笔记
ymawky 是为 macOS 编写的(抱歉…)。代码中有几个(确切地说,远不止几个)特定于 macOS 的部分,不具备可移植性。
- macOS 上的系统调用使用
x16作为编号,并使用svc #0x80进行调用。Linux 使用x8和svc #0。 - 错误报告不同。macOS 在出错时设置进位标志,并将
errno放在x0中。Linux 在x0中返回负值,例如-ENOENT。所有的b.cs都需要替换为cmp x0, #0/b.lt ...,并且你需要对x0取反以获得 errno。 fork()的工作方式不同,macOS 在子进程中在x1中放入 1,而 Linux 在x0中放入0。SO_NOSIGPIPE在 Linux 上不存在。O_NOFOLLOW_ANY也是特定于 macOS 的。renameatx_np()也是特定于 macOS 的。Linux 有renameat2(),标志值不同。- 结构体布局和偏移量将不同。
stat64结构体、itimerval结构体和sockaddr_in结构体都需要重新考虑。 adr xN, foo@PAGE/add xN, xN, foo@PAGEOFF是 Mach-O 重定位操作符。Linux ELF 使用不同的语法,如:pg_hi21:和:lo12:。adr_l、ldr_l和str_l宏需要重写或替换。- 我个人最喜欢的部分 :3 信号处理在 Linux 和 macOS 上工作方式不同。macOS 的
sigaction结构体包含一个sa_tramp字段,内核在跳转到你的处理器之前会跳转到该字段。ymawky 直接利用sa_tramp作为处理器本身,完全跳过 libc 跳板和sigreturn。由于处理器仅发送 408 并退出,不需要返回,这很好且在没有 libc 的情况下运行完美。sigaction调用需要为 POSIX 系统重写。
特别感谢:
- Bob Johnson
- Bob Johnson 的心理医生
相似文章
用 aarch64 汇编构建 Web 服务器,给我的生活(缺乏)意义
本文介绍了 'ymawky',一个完全用 aarch64 汇编为 macOS 编写的最小 HTTP Web 服务器,使用原始系统调用而无需 libc 包装器,以探索底层系统机制。
用 x86_64 汇编写成的 Linux 桌面
一位开发者借助 Claude Code,用纯 x86_64 汇编重建了完整的 Linux 桌面栈——从 shell、终端、窗口管理器到各种工具,实现微秒级启动,并延长数小时续航。
Show HN: Nibble
Nibble 是一种类 C 的系统编程语言,用 3000 行 C 代码实现,无需外部依赖或堆分配即可生成 LLVM IR。它支持 defer、递归、多种类型、结构体、指针,并包含图形演示。
Yojam:一款 macOS 默认浏览器垫片,通过规则引擎路由 URL
Yojam 是一款开源的 macOS 默认浏览器垫片,它通过一个可自定义的规则引擎路由 URL,允许用户根据域名、来源或正则表达式模式,自动将链接定向到特定的浏览器、配置文件或应用。
Plexus P/20 模拟器
一款全新的开源 WebAssembly 模拟器,重现 1980 年代 Plexus P/20 Unix 服务器,让用户可在浏览器中运行 SystemV Unix。