Skip to content

Instantly share code, notes, and snippets.

@masakielastic
Last active June 17, 2025 09:22
Show Gist options
  • Select an option

  • Save masakielastic/51dbb9f0e849dc1f971332bb749cd346 to your computer and use it in GitHub Desktop.

Select an option

Save masakielastic/51dbb9f0e849dc1f971332bb749cd346 to your computer and use it in GitHub Desktop.
H2O で HTTP/1 サーバー

H2O で HTTP/1 サーバー (libuv)

Debian 12 Bookworm で確認しました。次のパッケージが前提です。

sudo apt install libuv1-dev libh2o-dev

H2O のバージョンは 2.2.5 です。

ビルドと実行です。

gcc -o h2o_server server.c -lh2o -luv -lssl -lcrypto -pthread
./h2o_server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <uv.h>
#include <h2o.h>
#include <h2o/http1.h>
static h2o_globalconf_t config;
static h2o_context_t ctx;
static h2o_accept_ctx_t accept_ctx;
static uv_tcp_t listener;
static int should_exit = 0;
// ハンドラー関数:すべてのリクエストに対して "Hello, World!" を返す
static int hello_handler(h2o_handler_t *self, h2o_req_t *req)
{
// GETメソッドのみ受け付ける
if (!h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET"))) {
return -1; // 他のハンドラーに処理を委ねる
}
// レスポンスヘッダーを設定
req->res.status = 200;
req->res.reason = "OK";
h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE,
NULL, H2O_STRLIT("text/plain; charset=utf-8"));
// レスポンスボディを設定
const char *body_str = "Hello, World!\n";
h2o_iovec_t body = h2o_iovec_init(body_str, strlen(body_str));
h2o_send_inline(req, body.base, body.len);
return 0;
}
// 新しい接続を受け入れる際のコールバック
static void on_accept(uv_stream_t *server, int status)
{
if (status < 0) {
fprintf(stderr, "New connection error %s\n", uv_strerror(status));
return;
}
uv_tcp_t *client = malloc(sizeof(uv_tcp_t));
uv_tcp_init(ctx.loop, client);
if (uv_accept(server, (uv_stream_t*)client) == 0) {
h2o_socket_t *sock = h2o_uv_socket_create((uv_stream_t*)client, NULL);
h2o_accept(&accept_ctx, sock);
} else {
uv_close((uv_handle_t*)client, NULL);
free(client);
}
}
// シグナルコールバック
static void signal_cb(uv_signal_t *handle, int signum)
{
should_exit = 1;
uv_stop(ctx.loop);
}
int main(void)
{
struct sockaddr_in addr;
uv_signal_t sig_int, sig_term;
// H2Oの設定を初期化
h2o_config_init(&config);
// ホストを作成(任意のホスト名でアクセス可能)
h2o_hostconf_t *hostconf = h2o_config_register_host(&config,
h2o_iovec_init(H2O_STRLIT("default")),
65535);
// パスハンドラーを登録(すべてのパスに対して)
h2o_pathconf_t *pathconf = h2o_config_register_path(hostconf, "/", 0);
h2o_handler_t *handler = h2o_create_handler(pathconf, sizeof(*handler));
handler->on_req = hello_handler;
// uvループを作成してコンテキストを初期化(ホスト設定後)
uv_loop_t *loop = uv_default_loop();
h2o_context_init(&ctx, loop, &config);
// アクセプトコンテキストを初期化
accept_ctx.ctx = &ctx;
accept_ctx.hosts = config.hosts;
// シグナルハンドラーを設定
uv_signal_init(loop, &sig_int);
uv_signal_init(loop, &sig_term);
uv_signal_start(&sig_int, signal_cb, SIGINT);
uv_signal_start(&sig_term, signal_cb, SIGTERM);
// TCPリスナーを初期化
uv_tcp_init(loop, &listener);
// アドレスを設定
uv_ip4_addr("0.0.0.0", 8080, &addr);
// バインド
int r = uv_tcp_bind(&listener, (const struct sockaddr*)&addr, 0);
if (r) {
fprintf(stderr, "Bind error %s\n", uv_strerror(r));
return 1;
}
// リッスン開始
r = uv_listen((uv_stream_t*)&listener, 128, on_accept);
if (r) {
fprintf(stderr, "Listen error %s\n", uv_strerror(r));
return 1;
}
printf("HTTP server listening on http://localhost:8080/\n");
printf("Press Ctrl+C to stop\n");
// イベントループ実行
uv_run(loop, UV_RUN_DEFAULT);
printf("\nShutting down...\n");
// クリーンアップ
h2o_context_dispose(&ctx);
h2o_config_dispose(&config);
uv_loop_close(loop);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment