世界を動かす技術を、日本語で。

HNに表示: 意味の欠如を補うためにアセンブリでウェブサーバーを構築する

概要

  • ymawky はApple Silicon向けの ARM64アセンブリ製Webサーバ
  • syscallのみ で動作、 libc不使用接続ごとにfork
  • MacOS用だが、 Linux移植には修正が必要
  • GET/PUT/DELETE/OPTIONS/HEAD リクエスト対応、 静的ファイル配信
  • 独自エラーページ・ディレクトリリスト・セキュリティ対策 搭載

ymawky: ARM64アセンブリ製Webサーバ概要

  • 完全手書きARM64アセンブリ によるWebサーバ実装
  • syscallのみ利用libc完全不使用 設計
  • MacOS (Apple Silicon/ARM64) 専用サーバ
  • fork-per-connection モデル採用
  • 移植性重視 だが、Linux等では 細かな修正 が必要
  • Xcode Command Line Tools 必須(xcode-select --installで導入)
  • www/ ディレクトリ配下をドキュメントルートに指定
  • err/ ディレクトリで独自エラーページ(例:404.html)をサポート
  • index.html がGET /のデフォルト応答ファイル

起動・動作方法

  • makeコマンドでビルド
  • ./ymawky127.0.0.1:8080 にてサーバ起動
  • ./ymawky [port番号]指定ポート で起動
  • ./ymawky [数字以外の文字]デバッグモード 起動(fork無効・単一リクエストのみ対応)
  • カスタムアドレス未対応 (127.0.0.1固定)

対応HTTP機能・特徴

  • 静的ファイルサーバ として動作
  • GET/PUT/DELETE/OPTIONS/HEAD メソッド対応
  • サーバサイド動的生成・高度なURLパース非対応
  • パーセントエンコーディング(%20等)デコード対応
  • パストラバーサル防止 (..による上位ディレクトリ参照をブロック)
  • www/ 自動付与によるドキュメントルート強制
  • PUTアップロード最大1GiB (設定で拡張可)
  • PUTは一時ファイル→リネームによるアトミック処理
  • Content-Length検証・MIMEタイプ自動判別
  • Range: bytes= ヘッダによる部分配信対応(動画スクラブ可)
  • HTTP/1.0/1.1 パース対応、 Host必須 (1.1時)
  • ディレクトリリスト機能 (index.htmlなければ一覧表示)

セキュリティ・安全対策

  • PATH_MAX(4096バイト)超のパス拒否
  • パストラバーサル(/../..)のリクエスト拒否
  • 16バイト未満のパス拒否
  • www/以下にパスを強制
  • シンボリックリンクパス拒否(O_NOFOLLOW_ANY)
  • PUTは一時ファイル(.ymawky_tmp_)利用で部分書き込み防止
  • 一時ファイルへのGET/PUT拒否
  • 10秒以内にデータ受信なければコネクション切断(スローロリス対策)
  • ヘッダ全体も10秒以内受信必須

サポートHTTPステータスコード

  • 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
  • err/ディレクトリ にカスタムエラーページ(各コード対応HTML)配置可能
  • build_err_pages.sh で一括生成・カスタマイズ対応

MIMEタイプ自動判別

  • 拡張子ごとにMIMEタイプを自動判定
    • Web系: .html, .css, .js, .json, .wasm, .xml, .csv, .map, .mjs
    • 画像: .png, .jpg, .jpeg, .gif, .svg, .ico, .webp, .avif, .bmp, .tiff, .apng
    • フォント: .woff, .woff2, .ttf, .otf
    • 文書: .txt, .pdf, .doc, .docx, .epub, .rtf
    • 動画: .mp4, .webm, .mkv, .avi, .mov
    • 音声: .mp3, .ogg, .wav, .flac, .aac, .m4a, .opus
    • アーカイブ: .zip, .gz, .tar, .7z, .bz2, .rar

設定ファイルとカスタマイズ

  • config.S で各種設定可能
    • DEFAULT_DIR: ドキュメントルート(例:"www/")
    • ERR_DIR: エラーページディレクトリ(例:"err/")
    • DEFAULT_FILE: デフォルトファイル(例:"index.html")
    • RECV_TIMEOUT: 受信タイムアウト秒数(例:10秒)
    • HEADER_REQ_TIMEOUT_SECS: ヘッダ受信タイムアウト(例:10秒)
    • PUT_GRACE_SECS: PUT時の最小猶予(例:5秒)
    • PUT_MIN_BPS: PUT時の最小バイト/秒(例:16KB)
    • MAX_BODY_SIZE: PUT許容最大サイズ(例:1GB)
    • MAX_PROCS: 最大同時プロセス数(例:256)

MacOS特有の実装注意点

  • MacOS専用システムコール (x16+svc #0x80利用、Linuxはx8+svc #0)
  • エラー報告形式や構造体レイアウトの違い
  • fork()の戻り値仕様差
  • SO_NOSIGPIPE, O_NOFOLLOW_ANY, renameatx_np()等MacOS固有
  • Mach-O用アセンブリ記法(adr xN, foo@PAGE等)
  • sigaction構造体とシグナルトランポリンの挙動差
  • Linux移植時は各所のアセンブリ/システムコール修正が必要

"Safety"と注意事項

  • 個人プロジェクト としての実装のため 未知の脆弱性 リスクあり
  • 可能な限り 安全対策 を施しているが 本番利用前に十分な検証推奨

参考リンク・詳細情報


追記: ymawkyは Apple Silicon/MacOS専用、静的ファイルWebサーバアセンブリ愛好家や低レイヤ学習者向け プロジェクト。 GET/PUT/DELETE/HEAD/OPTIONS対応、Rangeヘッダ・エラーページ・ディレクトリリスト・簡易DoS対策 を搭載。 詳細は公式サイト参照

Hackerたちの意見

あの偽のオライリーの本の表紙、マジで最高だね。

あの本が、そもそもこれを作るきっかけになったんだ、ハハ。サブタイトルから名付けた略語も生まれたしね。

フォーレイリー!

誰かが手作業でこんなことをやってくれるって知って、なんか温かい気持ちになったよ。俺だけじゃないんだね!

ありがとう!このアイデアにずっと夢中だったんだけど、やっと始めることにしたんだ。それから数週間ずっとハマってたよ。もし似たようなプロジェクトがあったら、ぜひ見せてほしいな。俺だけじゃないって思うと嬉しい!プログラマーのほとんどは、数週間か数ヶ月かけてアセンブリを学ぶことで、CPUやコンパイル言語の仕組みを理解するのに大いに役立つと思うよ。

意味のない比較とはいえ、これとフル機能のウェブサーバーのパフォーマンス(秒あたりの最大リクエスト数?)を比べてみたいな。

正直、ベンチマークは取ってないけど、ymawkyはほとんどのフル機能のウェブサーバーよりかなり遅いと思うよ。ymawkyは接続ごとにフォークを使うから、nginxやApacheが使ってるものより根本的に遅いんだ。nginxはイベント駆動のIO(kqueue/epoll)を使ってて、各リクエストでプロセスをフォークするオーバーヘッドなしで数千の同時接続を処理できる。Apacheはスレッドプールを使って、リクエストごとに生成する必要なく複数の接続を処理する。どのウェブサーバーと直接比較しても、「接続ごとのフォーク vs イベントループ/スレッドプール」を測ることになるから、アセンブリとは関係ないんだよね。Cで書かれた似たようなフォークごとのサーバーとの比較では、スループットはほぼ同じになると思う。というのも、このモデルのボトルネックは実際のコードじゃなくてfork()自体だから。リクエスト/secよりもバイナリサイズや起動時間の方が重要かも。でも、実際にベンチマーク取るのは楽しそうだね。

自分のClaudeにnginxとベンチマークを取らせるべきかな?それとも君のClaudeに頼む?

HACKERを名乗る人を見ると、なんか妙にワクワクするんだよね。でも同時に、スキルやクラフトマンシップが業界で評価されていた頃を思い出しちゃった。そろそろキャリアプログラマーから卒業する時期なのかもね。

なんて冷たいコメントなんだ。今や誰でもLLMにコードを書かせられるから、プロジェクトに価値をもたらせるのは、LLMの出力を改善できる人だけだよ。つまり、論理や言語を深く理解している人たちね。そういう人たちだけが、好奇心から物事の仕組みを学ぶために時間と労力をかけるんだから。どんな代替案があっても、その代替案には未来がないよ。

これは呪われてて素晴らしいね。特にステータスコード418が好きだな。いつか実際に遭遇したら、君のことを思い出すよ!

10年前だったら、こんなものを作れるエリートに頭を下げてたと思う。今は「LLMがこれを書くのにどれくらい時間がかかるだろう?」って考えるだけだ。人間の芸術形態の死を悲しんでるよ。

悲しいよりもワクワクするよ。アセンブリ言語が必要なアイデアがあるなら、今ならできるじゃん!実際には不可能だったことを考えるよりも、ポジティブに見ていこう。今は自分の手でコードを書く能力に制限されずに、コンピュータをプログラムできるのが信じられないほどワクワクするよ。

人間のアートフォームはまだまだ健在だね。この投稿がその証拠だよ。確かにLLM(大規模言語モデル)が書くこともできるし、うまくいくかもしれない。でも、これは意味のないものになってしまう。これとは違ってね。

言いたいことはわかるけど、「手作り」のコードへの新たな深い渇望がちょっと行き過ぎてる気がする。ソフトウェアエンジニアは、ソフトウェアが登場して以来、できるだけショートカットを使ってきたよね。もう手作業で飛行機を作らないことを悲しむの?(つまり、「コーディングの技術」の死)。ソフトウェアを大工のように考えるのはやめようよ。魔法のような物理的なスキルがあって、それが「守るべき技術」だっていう考え方はさ。少なくとも君のコメントは現実に根ざしてたけど、僕が話す多くの人(コーダーじゃない人たち)は、良いソフトウェアエンジニアはすべての行と単語を考え抜いて書いてると思ってるみたいで、AIはただコードをスパムしてるから、どちらが優れているかっていう話になる。彼らはそれが微妙に賢い意見だと思っていて、ソフトウェア開発を内面的に理解してるとか何とか言ってるけど、基本的な前提はまだ正しい(純粋なAI生成コードはゴミ)けど、それはほとんどが設計が悪いからで、まだまだ貧弱なアーキテクトなんだよね。スラップに対抗する必要はあるけど、どうしてコードを書くことを神聖な活動のように持ち上げる必要があるの?良いコーダーの仕事のほとんどは、物理的な現実とはあまり関係なく、頭の中でやってることなんだ。

答えは「全く時間がかからない」だよ。今年の初めに、すごく難しいアセンブラでどれだけうまくいくか試すためにGemini Ultraを使ったんだ。8086アセンブラでCGAで動くフラットシェーディングの3Dエンジンを一気に書いてって頼んだら、数分で一発でできたよ。 https://imgur.com/a/Dy5rUku

10年前、こんなものを作れるエリートには頭を下げていたと思う。だけど、これはエリートなスキルっていうより、ジャグリングみたいなもんだよね。最初の数秒はすごいと思うけど、すぐに新鮮さがなくなって、結局は維持が大変なプロジェクトになっちゃう。どのプラットフォームでも動かせないし、全然メリットもない。実用的な使い道がないギミックみたいなもんだよ。これは、アマチュアのギタリストがYouTubeにシャレた演奏動画をアップしてるのと同じようなもんだね。

WebAssemblyでソフトウェアレンダラーを書こうとしてるんだけど、何か理由があってこの雰囲気のコードの世界に逆らいたくて、また挑戦したい気持ちがあるんだ。完成するかどうかは分からないし、クレイジーだし、役に立つわけでもないけど、すごく楽しいよ。OPの達成におめでとうと言いたい。

進捗をぜひ投稿して!めっちゃクールだね!

3Dソフトウェアレンダラーのこと?僕はティーンエイジャーの頃からそれに取り組んでて、プロのキャリアの初めもx86とCでやってたんだ。LLMが純粋な8088アセンブラでCGA用のものを書くのを見たくて、いいデモを一発で出してくれたよ(エリート船のベクトルをプロンプトに入れたんだ): https://imgur.com/a/Dy5rUku

うーん。最初のアセンブリプロジェクトの一つは、x86アセンブリで100%書いたCGIスクリプトだったな。フルウェブサーバーは確かにもっと印象的!でも初心者には、まずApacheのCGIやmod_cgiを調べることをおすすめするよ、笑。

実際にアセンブリで大きなものを書き始めると、特にマクロアセンブラだと、冗長になるけど、基本的には高級プログラミングとそんなに違わないことに気づくよ。手続きやマクロを使って抽象化を構築するコツをつかめば、うまくいくはず。アセンブリを効果的に読むのは、書くよりもずっと難しいことが多いんだ。

うん、これを通じて僕もそう気づいたよ。もっと明確にする必要があるけど、特定の関数の動作は基本的にはそんなに違わないんだ。「strlen」は、CでもRustでもアセンブリでも、他のどんな言語でも、NULLバイトを探して文字列を反復するからね。CPUが何をどうするか、正確にどの順番でやるかを示してるから、他の言語よりもむしろわかりやすく感じるかもしれない。

よくやったね!RISC-Vの似たような小さなプロジェクトに取り組んでるところだよ。これは素晴らしい。

それめっちゃクールだね!どこかでシェアしてるなら、ぜひ見てみたいな。