Show HN: 用汇编语言构建 Web 服务器,为我的生命赋予(些许缺乏的)意义

Hacker News Top 工具

摘要

ymawky 是一个专为 macOS 编写的 ARM64 汇编 Web 服务器,其特点是不依赖 libc 仅使用系统调用,并具备基本的 HTTP 功能。

这是 ymawky,一个完全使用 ARM64 汇编语言为 macOS 编写的静态文件 Web 服务器。它支持 GET、PUT、DELETE、HEAD 和 OPTIONS 请求,并支持 Range: bytes=X-Y 头信息(从而支持视频流式的快速传输/跳过)。它可以解码百分号编码的 URL,严格实施文档根目录限制,针对任何 HTTP 错误响应提供自定义错误页面,支持目录列表,并包含针对类似 Slowloris 攻击的(部分)缓解措施。<p>我还撰写了一篇更详细的介绍文章,详见:<a href="https://imtomt.github.io/ymawky/" rel="nofollow">https://imtomt.github.io/ymawky/</a>
查看原文 导出为 Word 导出为 PDF
查看缓存全文

缓存时间: 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/ 目录存在于 ymawkywww/ 旁边。

配置以修改默认文件和文档根目录。

运行

  • 运行 ./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 Forbidden
    • GET /ohwell...txt -> 200 OK
    • GET /../src/ymawky.S -> 403 Forbidden
    • GET /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.1HTTP/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 OK
  • 201 Created
  • 204 No Content
  • 206 Partial Content
  • 400 Bad Request
  • 403 Forbidden
  • 404 Not Found
  • 408 Request Timeout
  • 409 Conflict
  • 411 Length Required
  • 413 Content Too Large
  • 414 URI Too Long
  • 416 Range Not Satisfiable
  • 418 I'm a teapot
  • 431 Request Header Fields Too Large
  • 500 Internal Server Error
  • 501 Not Implemented
  • 503 Service Unavailable
  • 505 HTTP Version Not Supported
  • 507 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.htmlerr/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 使用 x8svc #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_lldr_lstr_l 宏需要重写或替换。
  • 我个人最喜欢的部分 :3 信号处理在 Linux 和 macOS 上工作方式不同。macOS 的 sigaction 结构体包含一个 sa_tramp 字段,内核在跳转到你的处理器之前会跳转到该字段。ymawky 直接利用 sa_tramp 作为处理器本身,完全跳过 libc 跳板和 sigreturn。由于处理器仅发送 408 并退出,不需要返回,这很好且在没有 libc 的情况下运行完美。sigaction 调用需要为 POSIX 系统重写。

特别感谢:

  • Bob Johnson
  • Bob Johnson 的心理医生

相似文章

用 x86_64 汇编写成的 Linux 桌面

Lobsters Hottest

一位开发者借助 Claude Code,用纯 x86_64 汇编重建了完整的 Linux 桌面栈——从 shell、终端、窗口管理器到各种工具,实现微秒级启动,并延长数小时续航。

Show HN: Nibble

Hacker News Top

Nibble 是一种类 C 的系统编程语言,用 3000 行 C 代码实现,无需外部依赖或堆分配即可生成 LLVM IR。它支持 defer、递归、多种类型、结构体、指针,并包含图形演示。

Plexus P/20 模拟器

Hacker News Top

一款全新的开源 WebAssembly 模拟器,重现 1980 年代 Plexus P/20 Unix 服务器,让用户可在浏览器中运行 SystemV Unix。