# 機能設計書 79-cgi

## 概要

本ドキュメントは、Nim標準ライブラリのcgiモジュールの機能設計について記載する。本モジュールはCGI（Common Gateway Interface）アプリケーション開発のためのヘルパー関数を実装している。

### 本機能の処理概要

cgiモジュールは、CGI環境変数の読み取り、フォームデータの解析、クッキーの操作、HTMLコンテンツの出力などのCGIアプリケーション開発に必要な機能を提供する。GETとPOSTの両方のリクエストメソッドをサポートし、XMLエスケープ機能も含む。

**業務上の目的・背景**：従来のWebサーバー（Apache、nginxなど）でCGI経由でNimアプリケーションを実行する場合に、環境変数からリクエスト情報を取得し、標準出力にレスポンスを返す必要がある。本モジュールはこれらの処理を簡素化する。

**機能の利用シーン**：
- CGI形式のWebアプリケーション開発
- フォームデータの処理
- クッキーの設定・取得
- CGI環境変数の取得
- テスト用データの設定

**主要な処理内容**：
1. CGIデータの読み取り（readData、decodeData）
2. 環境変数の取得（getQueryString、getRequestMethod等）
3. クッキー操作（setCookie、getCookie、existsCookie）
4. コンテンツ出力（writeContentType）
5. XMLエスケープ（xmlEncode）
6. テストデータ設定（setTestData）

**関連システム・外部連携**：Webサーバー（Apache、nginx等）、uri、cookies、strtabsモジュールと連携する。

**権限による制御**：環境変数と標準入出力へのアクセス権限に依存。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本モジュールはGUI/画面を持たないライブラリ機能 |

## 機能種別

Web開発 / CGIサポート / フォーム処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| allowedMethods | set[RequestMethod] | No | 許可するメソッド | デフォルト: {methodNone, methodPost, methodGet} |
| data | string | No | デコード対象データ | - |
| validKeys | varargs[string] | Yes | 有効なキー一覧 | validateData用 |
| name | string | Yes | クッキー名 | - |
| value | string | Yes | クッキー値 | - |

### 入力データソース

- 環境変数（REQUEST_METHOD、QUERY_STRING、CONTENT_LENGTH等）
- 標準入力（POSTデータ）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| StringTableRef | ref StringTable | キーバリューペアのテーブル |
| string | string | 環境変数の値 |

### 出力先

- 標準出力（HTTPレスポンス）

## 処理フロー

### 処理シーケンス

```
1. リクエストメソッド判定（getEncodedData）
   ├─ POST: CONTENT_LENGTHバイトをstdinから読み取り
   └─ GET: QUERY_STRINGを取得
2. データデコード（decodeData）
   └─ uri.decodeQueryでkey=value形式をパース
3. データ読み取り（readData）
   └─ StringTableに格納
4. データ検証（validateData）
   └─ 未知のキーがあればCgiError
5. レスポンス出力
   ├─ writeContentType: Content-type: text/html
   └─ setCookie: Set-Cookie: name=value
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{REQUEST_METHOD?}
    B -->|POST| C[CONTENT_LENGTH取得]
    C --> D[stdin読み取り]
    B -->|GET| E[QUERY_STRING取得]
    B -->|その他| F{methodNone許可?}
    F -->|Yes| G[空文字列]
    F -->|No| H[CgiError]
    D --> I[decodeData]
    E --> I
    G --> I
    I --> J[readData]
    J --> K[StringTable返却]
    K --> L[validateData]
    L --> M{全キー有効?}
    M -->|Yes| N[処理続行]
    M -->|No| O[CgiError]
    N --> P[writeContentType]
    P --> Q[コンテンツ出力]
    Q --> R[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-79-01 | POST読み取り | CONTENT_LENGTHバイトをstdinから読み取り | REQUEST_METHOD=POST |
| BR-79-02 | GET読み取り | QUERY_STRINGから取得 | REQUEST_METHOD=GET |
| BR-79-03 | メソッド検証 | allowedMethodsに含まれないメソッドは拒否 | 常時 |
| BR-79-04 | キー検証 | validKeysに含まれないキーはエラー | validateData使用時 |

### 計算ロジック

XMLエスケープ（xmlEncode）:
- `"` -> `&quot;`
- `<` -> `&lt;`
- `>` -> `&gt;`
- `&` -> `&amp;`
- その他 -> そのまま

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | 本モジュールはデータベース操作を行わない |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| CgiError | CGIエラー | 未許可メソッド | allowedMethodsを確認 |
| CgiError | CGIエラー | stdin読み取り失敗 | CONTENT_LENGTHを確認 |
| CgiError | CGIエラー | 未知のキー | validKeysを確認 |

### リトライ仕様

- エラー時のリトライは実装されていない

## トランザクション仕様

本モジュールはCGI通信を行うものであり、データベーストランザクション管理は行わない。

## パフォーマンス要件

- newStringOfCapによるメモリ事前確保（xmlEncode）
- スレッドローカルなクッキーキャッシュ（gcookies）

## セキュリティ考慮事項

- xmlEncodeでXSS対策
- validateDataで入力検証
- クッキーはスレッドローカルにキャッシュ

## 備考

- uri.encodeUrl/decodeUrlを再エクスポート
- デバッグ用にsetTestDataでGETリクエストをシミュレート可能
- スタックトレースを標準出力に表示するsetStackTraceStdout

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、主要な型と例外を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | cgi.nim | `lib/pure/cgi.nim` | CgiError例外（58行目）: CGIエラー |
| 1-2 | cgi.nim | `lib/pure/cgi.nim` | RequestMethod列挙型（59-62行目）: リクエストメソッド |

**読解のコツ**: RequestMethodはmethodNone、methodPost、methodGetの3値。

#### Step 2: データ読み取りを理解する

CGIデータの読み取り処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | cgi.nim | `lib/pure/cgi.nim` | getEncodedData（68-87行目）: 生データ取得 |
| 2-2 | cgi.nim | `lib/pure/cgi.nim` | decodeData（89-102行目）: データデコード |
| 2-3 | cgi.nim | `lib/pure/cgi.nim` | readData（104-116行目）: StringTableに格納 |

**主要処理フロー**:
- **69-78行目**: POSTはCONTENT_LENGTHバイトをstdinから読み取り
- **79-82行目**: GETはQUERY_STRINGを取得
- **92行目**: uri.decodeQueryでキーバリューペアをパース

#### Step 3: 環境変数取得を理解する

CGI環境変数の取得関数を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | cgi.nim | `lib/pure/cgi.nim` | getContentLength（125-127行目） |
| 3-2 | cgi.nim | `lib/pure/cgi.nim` | getRequestMethod（209-211行目） |
| 3-3 | cgi.nim | `lib/pure/cgi.nim` | getQueryString（185-187行目） |
| 3-4 | cgi.nim | `lib/pure/cgi.nim` | getHttpCookie（161-163行目） |

**読解のコツ**: すべての関数はgetEnv()のラッパー。環境変数名と関数名の対応を確認。

#### Step 4: 出力関数を理解する

HTTPレスポンスの出力処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | cgi.nim | `lib/pure/cgi.nim` | writeContentType（271-277行目）: Content-type出力 |
| 4-2 | cgi.nim | `lib/pure/cgi.nim` | setCookie（305-307行目）: Set-Cookie出力 |
| 4-3 | cgi.nim | `lib/pure/cgi.nim` | writeErrorMessage（289-299行目）: エラー出力 |

**主要処理フロー**:
- **277行目**: `Content-type: text/html\n\n`を出力
- **307行目**: `Set-Cookie: name=value\n`を出力

#### Step 5: クッキー取得を理解する

クッキーの取得とキャッシュを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | cgi.nim | `lib/pure/cgi.nim` | gcookies（310行目）: スレッドローカルキャッシュ |
| 5-2 | cgi.nim | `lib/pure/cgi.nim` | getCookie（312-315行目）: クッキー取得 |
| 5-3 | cgi.nim | `lib/pure/cgi.nim` | existsCookie（317-320行目）: クッキー存在確認 |

**主要処理フロー**:
- **314行目**: gcookies == nilの場合、parseCookies(getHttpCookie())でキャッシュ作成
- **315行目**: getOrDefaultで取得（存在しなければ空文字列）

### プログラム呼び出し階層図

```
readData(allowedMethods)
    │
    └─ getEncodedData(allowedMethods)
          │
          ├─ [POST] getEnv("CONTENT_LENGTH")
          │     │
          │     └─ readBuffer(stdin, buffer, L)
          │
          ├─ [GET] getEnv("QUERY_STRING")
          │
          └─ [その他] cgiError() or ""
                │
                └─ decodeData(data)
                      │
                      └─ uri.decodeQuery(data)
                            │
                            └─ StringTable[key] = value

getCookie(name)
    │
    └─ [gcookies == nil] parseCookies(getHttpCookie())
          │
          └─ getEnv("HTTP_COOKIE")
                │
                └─ cookies.parseCookies()
                      │
                      └─ gcookies[name].getOrDefault("")

xmlEncode(s)
    │
    └─ addXmlChar(dest, c)
          │
          ├─ [&] "&amp;"
          ├─ [<] "&lt;"
          ├─ [>] "&gt;"
          ├─ ["] "&quot;"
          └─ [その他] c
```

### データフロー図

```
[Webサーバー]           [環境変数/stdin]         [cgiモジュール]         [stdout]

REQUEST_METHOD ────────────────────────▶ getRequestMethod()
                                                  │
QUERY_STRING ──────────────────────────▶ getEncodedData() ─────┐
                                                  │              │
CONTENT_LENGTH ───────────────────────▶ readBuffer(stdin)      │
stdin ◀─────────────────────────────────────────┘              │
                                                               │
                                              decodeData() ◀───┘
                                                  │
                                              readData()
                                                  │
                                                  ▼
                                             StringTable
                                                  │
                                              処理ロジック
                                                  │
                                                  ▼
                                         writeContentType() ──────▶ Content-type: text/html
                                                  │
                                         write(stdout) ───────────▶ HTMLコンテンツ
                                                  │
                                         setCookie() ─────────────▶ Set-Cookie: ...
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| cgi.nim | `lib/pure/cgi.nim` | ソース | CGIヘルパー関数 |
| uri.nim | `lib/pure/uri.nim` | ソース | URL/クエリエンコード |
| cookies.nim | `lib/pure/cookies.nim` | ソース | クッキーパース |
| strtabs.nim | `lib/pure/strtabs.nim` | ソース | StringTable |
| os.nim | `lib/pure/os.nim` | ソース | getEnv/putEnv |
