Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

mini-bandit HTTP2 handler(deps on ThousandIsland)

LT/ElixirImp_20250618/handler.livemd

mini-bandit HTTP2 handler(deps on ThousandIsland)

Mix.install([
  {:thousand_island, "~> 1.0"}
])

HTTP2 module

defmodule Http2LikeServer do
  use ThousandIsland.Handler

  @impl ThousandIsland.Handler
  def handle_connection(_socket, state) do
    {:continue, state}
  end

  @impl ThousandIsland.Handler
  def handle_data(data, socket, state) do
    data
    |> deserialize()
    |> case do
      {:ok, {:headers, payload}} ->
        IO.puts("Received headers: #{payload}")
        # レスポンスデータを作成
        response_payload = "Hello from HTTP2-like server!"
        response_frame = serialize(:data, response_payload)
        ThousandIsland.Socket.send(socket, response_frame)
        {:continue, state}

      {:ok, {:data, payload}} ->
        IO.puts("Received data: #{payload}")
        response_payload = "ACK: Received DATA (#{byte_size(payload)} bytes)"
        response_frame = serialize(:data, response_payload)
        ThousandIsland.Socket.send(socket, response_frame)

        {:continue, state}

      :error ->
        IO.puts("Failed to parse frame.")
        {:close, state}
    end
  end

  # フレームの解析
  defp deserialize(<<_length::24, type::8, _flags::8, _stream_id::32, payload::binary>>) do
    case type do
      0x01 -> {:ok, {:headers, payload}}
      0x00 -> {:ok, {:data, payload}}
      _ -> :error
    end
  end

  defp deserialize(_), do: :error

  # フレームの構築
  defp serialize(:headers, payload), do: serialize(0x01, payload)
  defp serialize(:data, payload), do: serialize(0x00, payload)

  defp serialize(type, payload) do
    length = byte_size(payload)
    <>
  end
end

Run

name = :my_thousand_server
pid = Process.whereis(name)
unless is_nil(pid), do: Supervisor.stop(pid)
{:ok, new_pid} = ThousandIsland.start_link(port: 1234, handler_module: Http2LikeServer)
Process.register(new_pid, name)

Test

テスト用サーバーを使います
handler_test