🎉

【C 言語】【Zig】llhttp で HTTP/1 メッセージを解析する

2024/04/23に公開

llhttp は Node.js で採用されている HTTP/1 パーサーである。Github からソースコードを入手してビルドしてみよう

git clone https://github.com/nodejs/llhttp.git
cd
npm ci
make

2025年リリース予定の Debian 13 Trixie では libllhttp-dev パッケージが用意されている。

build ディレクトリに libllhttp.alibllhttp.sollhttp.h が生成される。公式リポジトリに記載されているコードを試してみよう

#include "stdio.h"
#include "string.h"
#include "llhttp.h"

int main() {
  llhttp_t parser;
  llhttp_settings_t settings;
  const char* request = "GET / HTTP/1.1\r\n\r\n";
  int request_len = strlen(request);

  llhttp_settings_init(&settings);
  llhttp_init(&parser, HTTP_BOTH, &settings);
  enum llhttp_errno err = llhttp_execute(&parser, request, request_len);

  if (err == HPE_OK) {
    fprintf(stdout, "Successfully parsed!\n");
  } else {
  fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err), parser.reason);
  }

  return 0;
}

ビルドして実行してみよう

zig run test.c -lc -l llhttp -L $HOME/llhttp/build -I $HOME/llhttp/build
Successfully parsed!

on_message_complete のときに実行されるコールバックを追加する。HTTP GET メソッドの文字が表示される。

int handle_on_message_complete(llhttp_t* parser) {
        fprintf(stdout, "Message completed!\n");
        uint8_t method = llhttp_get_method(parser);
        printf("%s\n", llhttp_method_name(method));

        return 0;
}

int main() {
        llhttp_t parser;
        llhttp_settings_t settings;

        llhttp_settings_init(&settings);
        settings.on_message_complete = handle_on_message_complete;

今度は Zig でコードを書いてみよう

const std = @import("std");
const print = std.debug.print;
const llhttp = @cImport({
    @cInclude("llhttp.h");
});

pub fn main() !void {
  var parser = llhttp.llhttp_t {};
  var settings = llhttp.llhttp_settings_t {};

  const request = "GET / HTTP/1.1\r\n\r\n";
  const request_len = request.len;

  llhttp.llhttp_settings_init(&settings);
  llhttp.llhttp_init(&parser, llhttp.HTTP_BOTH, &settings);
  const err = llhttp.llhttp_execute(&parser, request, request_len);

  if (err == llhttp.HPE_OK) {
    print("Successfully parsed!\n", .{});
  } else {
    print("Parse error: {s} {s}\n", .{
      llhttp.llhttp_errno_name(err), parser.reason
    });
  }
}

ビルドと実行は次のとおり

zig run test.zig -l c -l llhttp -L $HOME/llhttp/build -I $HOME/llhttp/build

Discussion