コンテンツにスキップ

静的ファイルを配信する

wired_staticfile_resolve は、リクエストパスをドキュメントルート下のファイルパスに変換し、パストラバーサル(.. を使ってドキュメントルートの外へ抜け出す攻撃)を拒否します。 wired_mimetype_for_path は、その拡張子から Content-Type を選びます。 このページのサーバーは、この例のディレクトリに置いた 2 つのファイルを配信し、それ以外のパスには 404 を返します。

コード

main.c
#define WIRED_MAIN
#include "wired.h"
/* The whole point of this page: wired_staticfile_resolve rejects path
* traversal and picks an index file for a directory request, then
* wired_mimetype_for_path infers the Content-Type from the resolved
* file's extension. */
/* Serve one round of the resolved file's bytes at x->offset, 404 if it
* does not resolve or open/read. */
static int on_request(void* ctx, wired_http_exchange* x) {
char resolved[256], reqpath[128];
ssz fd, total, got;
(void)ctx;
wired_span_to_cstr(reqpath, sizeof reqpath, wired_h3req_path(x->req));
if (!wired_staticfile_resolve(
".", reqpath, "index.html", resolved, sizeof resolved))
return wired_http_reply_text(x, 404, "not found");
fd = wired_fio_open(resolved);
if (fd < 0) return wired_http_reply_text(x, 404, "not found");
total = wired_fio_size(resolved);
got =
wired_fio_pread(fd, wired_mspan_of(x->body->p, x->body->cap), x->offset);
wired_fio_close(fd);
if (total < 0 || got < 0) return wired_http_reply_text(x, 404, "not found");
if (x->offset == 0) {
x->content_type = wired_mimetype_for_path(resolved);
x->total_size = (u64)total;
}
x->body->len = (usz)got;
if (x->offset + (u64)got < (u64)total) x->more = 1;
return 1;
}
int wired_main(int argc, char** argv) {
/* Fixed demo identity (self-signed certificate; never for production). */
static wired_srvboot_demo_keys keys;
wired_srvboot_id id;
wired_srvboot_demo(&id, &keys, 0x50, "guide-h3");
u16 port = (u16)wired_cliargs_int(argc, argv, "--port", 4433);
wired_srvrun_handler h = {.http = on_request};
wired_srvrun_obs obs = {0};
return wired_server_run(port, &id, h, obs) ? 0 : 1;
}

実行結果

Terminal window
$ ./h3-static-files --port 4433
conn closed by peer (CONNECTION_CLOSE)
Terminal window
$ probe h3get 127.0.0.1:4433 /hello.txt /page.html /nope.txt
GET /hello.txt status=200 content-type=text/plain body="hello static file"
GET /page.html status=200 content-type=text/html body="<h1>hi</h1>"
GET /nope.txt status=404 content-type=text/plain body="not found"
  • wired_staticfile_resolve(".", reqpath, "index.html", resolved, cap) は、 リクエストパスを現在のディレクトリ下に解決し、.. を含むセグメントを拒否します。
  • wired_mimetype_for_path(resolved) は、解決済みパスの拡張子を読みます (.txt は text/plain、.html は text/html)。 この呼び出しは最初のラウンド(ハンドラーが呼ばれる 1 回分の処理)で一度だけ行い、wired_h3reqdrive 自身の content-type フィールドと同じ扱いにします。
  • wired_fio_open / wired_fio_size / wired_fio_pread / wired_fio_close は、 x->body->cap 単位でファイルをチャンク読みします。 word_list の例が、1 ラウンドより大きいドキュメントルートで使っているのと同じ読み方です。
  • ファイルが見つからない場合、または wired_staticfile_resolve が拒否した場合は、 x->status(wired_http_handler、パスによる振り分け ページで直した仕組みと同じ)で本物の 404 を返します。