ResponseBuilderコンポーネント - ha1t/php-gm-server GitHub Wiki
ResponseBuilderコンポーネント
ResponseBuilderは、Gemini Protocol に準拠したレスポンスを構築するための静的ヘルパークラスです。このコンポーネントは、ファイルハンドラーの処理結果をGeminiプロトコル形式(STATUS SPACE META\r\n BODY)に変換し、クライアントへの送信用レスポンスを生成します。成功レスポンス(ステータス20)、見つからないエラー(ステータス51)、不正リクエストエラー(ステータス59)の3種類のレスポンスメソッドを提供し、サーバー全体のレスポンス処理を一元管理します。
概要
役割と位置付け
ResponseBuilderクラスは、GeminiServerコンポーネントの内部で使用され、StaticFileHandlerコンポーネントの処理結果をGemini Protocol形式のレスポンスに変換する責務を持ちます。プロトコル仕様に準拠したレスポンス文字列を生成することで、異なるクライアント実装間での互換性を確保します。
システム内での位置付け
sequenceDiagram
participant Client as Gemini Client
participant Server as GeminiServer
participant Parser as RequestParser
participant Handler as StaticFileHandler
participant Builder as ResponseBuilder
Client->>Server: TLS Connection + Request
Server->>Parser: parse(buffer)
Parser-->>Server: {host, path}
Server->>Handler: handle(path)
Handler-->>Server: {mime, body} or null
alt File Found
Server->>Builder: success(mime, body)
Builder-->>Server: "20 mime\r\nBody"
else File Not Found
Server->>Builder: notFound()
Builder-->>Server: "51 Not found\r\n"
else Invalid Request
Server->>Builder: badRequest(reason)
Builder-->>Server: "59 reason\r\n"
end
Server->>Client: Response (Gemini Format)
Server->>Client: End Connection
Gemini Protocol レスポンス形式
仕様概要
Gemini Protocol のレスポンスは、以下の構造を持つ単純なテキスト形式です:
STATUS SPACE META\r\n
[BODY]
- STATUS: 1〜3桁の整数(ステータスコード)
- SPACE: 単一のスペース文字(U+0020)
- META: 最大1024バイトの任意テキスト(ステータスに応じた情報)
- CRLF: キャリッジリターンとラインフィード(\r\n)
- BODY: オプショナルなレスポンスボディ(バイナリまたはテキスト)
ステータスコード仕様
ResponseBuilderが生成する3つのステータスコードと対応する処理フロー:
| ステータス | 名称 | 用途 | META | BODY | 出典 |
|---|---|---|---|---|---|
| 20 | Success(成功) | ファイル取得成功 | MIMEタイプ | ファイル内容 | src/ResponseBuilder.php:9-11 |
| 51 | Not found(見つからない) | ファイルが存在しない | "Not found" | 無し | src/ResponseBuilder.php:14-16 |
| 59 | Bad request(不正リクエスト) | リクエスト形式が無効 | カスタム理由文 | 無し | src/ResponseBuilder.php:19-21 |
ResponseBuilder クラス実装
クラス構造
ResponseBuilderクラスは、名前空間 GeminiServer に属する静的メソッドのみで構成されるユーティリティクラスです。インスタンス化は想定されず、すべてのメソッドは static として定義されています。
出典: src/ResponseBuilder.php:7-23
メソッド仕様
success(string $mime, string $body): string
正常に取得したファイルの内容をレスポンスとして構築します。
public static function success(string $mime, string $body): string
{
return "20 {$mime}\r\n{$body}";
}
パラメータ:
$mime(string): レスポンスボディのMIMEタイプ(例:text/gemini、text/plain、image/png)$body(string): ファイルの内容またはレスポンスボディ
戻り値: Gemini形式のレスポンス文字列(ステータス20)
例:
ResponseBuilder::success('text/gemini', 'Hello Gemini!')
// 結果: "20 text/gemini\r\nHello Gemini!"
出典: src/ResponseBuilder.php:9-11
notFound(): string
要求されたリソースが見つからない場合のレスポンスを構築します。
public static function notFound(): string
{
return "51 Not found\r\n";
}
パラメータ: 無し
戻り値: ステータス51のレスポンス文字列(BОDYなし)
例:
ResponseBuilder::notFound()
// 結果: "51 Not found\r\n"
用途: リクエストされたファイルがドキュメントルート内に存在しない場合、または存在してもディレクトリで対応するindex.gmiが無い場合に使用されます。
出典: src/ResponseBuilder.php:14-16
badRequest(string $reason = 'Bad request'): string
クライアントリクエストが不正な形式の場合のレスポンスを構築します。
public static function badRequest(string $reason = 'Bad request'): string
{
return "59 {$reason}\r\n";
}
パラメータ:
$reason(string, optional): エラー理由を説明する文字列。デフォルト値は'Bad request'
戻り値: ステータス59のレスポンス文字列(BОDYなし)
例:
ResponseBuilder::badRequest('Invalid request')
// 結果: "59 Invalid request\r\n"
ResponseBuilder::badRequest('Request too long')
// 結果: "59 Request too long\r\n"
ResponseBuilder::badRequest()
// 結果: "59 Bad request\r\n"
用途: リクエスト形式が無効(スキームが gemini でない、ホスト名が不正、リクエスト長が1024バイト超過)な場合に使用されます。
出典: src/ResponseBuilder.php:19-21
使用コンテキスト
GeminiServerでの使用フロー
GeminiServerクラス内の接続ハンドラーで、3つのシナリオに応じてResponseBuilderメソッドが呼び出されます:
-
ファイル取得成功時:
ResponseBuilder::success()を呼び出し$result = $this->fileHandler->handle($request['path']); $conn->write(ResponseBuilder::success($result['mime'], $result['body'])); -
ファイル見つからない時:
ResponseBuilder::notFound()を呼び出しif ($result === null) { $conn->write(ResponseBuilder::notFound()); } -
不正リクエスト時:
ResponseBuilder::badRequest()を呼び出しif (strlen($buffer) > 1024) { $conn->write(ResponseBuilder::badRequest('Request too long')); }
出典: src/GeminiServer.php:44-82
エラーシナリオの詳細
flowchart TD
A["リクエスト受信"] --> B{"リクエスト形式検証"}
B -->|CRLF未検出 & 長さ ≤ 1024| C["バッファに蓄積"]
B -->|CRLF未検出 & 長さ > 1024| D["badRequest: Request too long"]
B -->|CRLF検出| E["RequestParser::parse"]
C --> E
E -->|パース失敗| F["badRequest: Invalid request"]
E -->|パース成功| G["StaticFileHandler::handle"]
G -->|ファイル取得成功| H["success: mime + body"]
G -->|ファイル不在| I["notFound"]
D --> J["レスポンス送信 & 接続終了"]
F --> J
H --> J
I --> J
テスト仕様
ResponseBuilderクラスのテストは、PHPUnit フレームワークを使用して実装されています。全メソッドのレスポンス形式が Gemini Protocol 仕様に準拠することを検証します。
テストケース
| テスト名 | 検証対象 | アサーション | 出典 |
|---|---|---|---|
| testSuccessResponse | success() | レスポンス形式が 20 {mime}\r\n{body} |
tests/ResponseBuilderTest.php:12-15 |
| testNotFoundResponse | notFound() | レスポンスが 51 Not found\r\n |
tests/ResponseBuilderTest.php:18-21 |
| testBadRequestResponse | badRequest() | レスポンスが 59 {reason}\r\n |
tests/ResponseBuilderTest.php:24-27 |
| testSuccessWithEmptyBody | success() (空ボディ) | 空ボディでも形式維持 | tests/ResponseBuilderTest.php:30-33 |
テスト実行例
vendor/bin/phpunit tests/ResponseBuilderTest.php
出典: tests/ResponseBuilderTest.php
プロトコル準拠性
Gemini Protocol 仕様との合致
ResponseBuilderが生成するレスポンスは、Geminiプロトコル仕様実装に記載されたレスポンス形式仕様に完全に準拠しています:
- 形式:
STATUS SPACE META\r\nBODY構造を厳密に守る - 改行コード: CRLF(\r\n)を正確に使用
- ステータスコード: サーバーが対応する20、51、59のみを生成
- エラーレスポンス: BОDYなしの簡潔なMETA情報を返す
MIMEタイプ対応
StaticFileHandlerが提供するMIMEタイプ一覧(ファイル配信システム詳細参照):
text/gemini(.gmi, .gemini)text/plain(.txt)text/html(.html)text/css(.css)text/javascript(.js)application/json(.json)image/png(.png)image/jpeg(.jpg, .jpeg)image/gif(.gif)image/svg+xml(.svg)application/pdf(.pdf)application/octet-stream(既定)
出典: src/StaticFileHandler.php:11-25
アーキテクチャ的位置付け
コンポーネント間の責任分離
classDiagram
class RequestParser {
+parse(string): ?array
}
class StaticFileHandler {
-docRoot: string
-MIME_MAP: array
+handle(string): ?array
}
class ResponseBuilder {
+success(string, string): string
+notFound(): string
+badRequest(string): string
}
class GeminiServer {
-host: string
-port: int
-fileHandler: StaticFileHandler
-certPath: string
+run(): void
}
GeminiServer --> RequestParser
GeminiServer --> StaticFileHandler
GeminiServer --> ResponseBuilder
各コンポーネントの責責任:
- RequestParser: リクエスト文字列の解析のみ(パース層)
- StaticFileHandler: ファイル解決とMIMEタイプ判定(ビジネスロジック層)
- ResponseBuilder: Gemini形式レスポンス生成(フォーマット層)
- GeminiServer: コンポーネント間の制御フロー管理(オーケストレーション層)
設計原則
ResponseBuilderの設計は以下の原則に従っています:
- 単一責任の原則: レスポンス形式生成に特化
- ステートレス: インスタンス状態を持たない静的メソッド
- イミュータビリティ: 入力パラメータを変更しない
- 簡潔性: 必要最小限の実装で仕様を実現
Related Pages
- Geminiプロトコル仕様実装 — Gemini Protocol 仕様、ステータスコード、MIMEタイプの詳細
- GeminiServerコンポーネント — ResponseBuilder を使用するメインサーバーコンポーネント
- StaticFileHandlerコンポーネント — ファイル取得とMIME判定、success() に渡される mime と body の生成
- RequestParserコンポーネント — リクエスト解析、badRequest() トリガーの判定
- エラーハンドリング戦略 — ステータス51と59の使い分けと発生シナリオ
- レスポンス構築フロー — ファイルハンドラーからレスポンス送信までの完全フロー
- リクエスト処理フロー — クライアント接続からレスポンス送信までの端から端までの流れ
- テスト戦略と実装 — ResponseBuilderTest を含むテストスイート全体
- アーキテクチャ設計 — システム全体のコンポーネント構成と設計パターン