# 機能設計書 77-asynchttpserver

## 概要

本ドキュメントは、Nim標準ライブラリのasynchttpserverモジュールの機能設計について記載する。本モジュールは高性能な非同期HTTPサーバーを実装している。

### 本機能の処理概要

asynchttpserverモジュールは、asyncnetおよびasyncdispatchをベースとした非同期HTTPサーバーを提供する。リクエストの受信、解析、レスポンスの送信を非同期で処理し、多数の同時接続に対応する。

**業務上の目的・背景**：アプリケーションのローカルテスト用のHTTPサーバーとして設計されている。本番環境では、このサーバーの前段にnginxなどのリバースプロキシを配置することが推奨される。

**機能の利用シーン**：
- 開発・テスト環境でのHTTPサーバー
- APIモックサーバー
- 軽量なWebアプリケーション
- リバースプロキシ経由での本番利用

**主要な処理内容**：
1. サーバーの作成と設定（newAsyncHttpServer）
2. ポートのリッスン開始（listen）
3. リクエストの受け入れ（acceptRequest、serve）
4. HTTPリクエストの解析（processRequest）
5. レスポンスの送信（respond）
6. 持続的接続の管理

**関連システム・外部連携**：asyncnet、asyncdispatch、httpcore、uriモジュールと連携する。

**権限による制御**：特権ポート（1024未満）へのバインドには管理者権限が必要。

## 関連画面

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

## 機能種別

ネットワーク通信 / HTTPサーバー / 非同期処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| port | Port | Yes | リッスンポート | 0-65535 |
| address | string | No | バインドアドレス | デフォルト: "" (全インターフェース) |
| domain | Domain | No | アドレスファミリー | デフォルト: AF_INET |
| reuseAddr | bool | No | SO_REUSEADDR設定 | デフォルト: true |
| reusePort | bool | No | SO_REUSEPORT設定 | デフォルト: false |
| maxBody | int | No | 最大Content-Length | デフォルト: 8388608 (8MB) |
| assumedDescriptorsPerRequest | int | No | リクエストあたりのFD数 | デフォルト: 5 |

### 入力データソース

- HTTPリクエスト（クライアントからのネットワーク入力）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Request | object | 受信したHTTPリクエスト |
| HttpCode | enum | HTTPステータスコード |

### 出力先

- HTTPレスポンス（クライアントへのネットワーク出力）

## 処理フロー

### 処理シーケンス

```
1. サーバー作成（newAsyncHttpServer）
   └─ reuseAddr、reusePort、maxBody設定
2. リッスン開始（listen）
   ├─ newAsyncSocketでソケット作成
   ├─ setSockOptでオプション設定
   ├─ bindAddrでアドレスバインド
   └─ listenで待機開始
3. リクエスト受け入れ（acceptRequest/serve）
   ├─ shouldAcceptRequestでFD制限確認
   └─ acceptAddrでクライアント接続受け入れ
4. リクエスト処理（processRequest）
   ├─ リクエスト行解析（メソッド、URL、プロトコル）
   ├─ ヘッダー解析
   ├─ ボディ読み取り（Content-Length/Chunked）
   └─ コールバック呼び出し
5. レスポンス送信（respond）
   └─ HTTP/1.1ステータス + ヘッダー + ボディ
6. 接続管理
   ├─ Keep-Alive: 接続維持
   └─ Close: 接続終了
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[newAsyncHttpServer]
    B --> C[listen]
    C --> D{shouldAcceptRequest?}
    D -->|Yes| E[acceptRequest]
    D -->|No| F[sleepAsync/poll]
    F --> D
    E --> G[processClient]
    G --> H[processRequest]
    H --> I[リクエスト行解析]
    I --> J[ヘッダー解析]
    J --> K{ボディあり?}
    K -->|Content-Length| L[recv contentLength]
    K -->|Chunked| M[チャンク読み取り]
    K -->|No| N[コールバック呼び出し]
    L --> N
    M --> N
    N --> O[respond]
    O --> P{Keep-Alive?}
    P -->|Yes| H
    P -->|No| Q[close]
    Q --> D
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-77-01 | 最大行長 | リクエスト行/ヘッダー行は8KB以下 | 常時（maxLine=8*1024） |
| BR-77-02 | ヘッダー数上限 | headerLimit以下 | 常時 |
| BR-77-03 | 最大ボディサイズ | maxBody以下（デフォルト8MB） | Content-Length解析時 |
| BR-77-04 | 持続的接続 | HTTP/1.1デフォルトでKeep-Alive | Connection: close以外 |
| BR-77-05 | FD制限 | maxFDs超過時はリクエスト拒否 | shouldAcceptRequest使用時 |

### 計算ロジック

チャンク転送エンコーディング解析：
- サイズ行を16進数として解析
- 指定バイト数を読み取り
- `\r\n`セパレーターをスキップ
- サイズ0で終了

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

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| Http400 | Bad Request | 無効なリクエスト形式 | クライアントに400返却 |
| Http411 | Length Required | POST時Content-Lengthなし | クライアントに411返却 |
| Http413 | Payload Too Large | 行長/ボディサイズ超過 | クライアントに413返却 |
| Http417 | Expectation Failed | 未対応Expectヘッダー | クライアントに417返却 |
| ValueError | 値エラー | プロトコル解析失敗 | 例外発生 |

### リトライ仕様

- processRequestは不正リクエスト後に再試行可能（return true）
- 接続切断時は再試行しない（return false）

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

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

## パフォーマンス要件

- 非同期I/Oによる高並行性
- FD制限による過負荷防止
- FutureVarによるメモリ再利用
- 持続的接続による接続オーバーヘッド削減

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

- 本番環境ではリバースプロキシ経由での使用を推奨
- headerLimitによるヘッダー数制限（DoS対策）
- maxBodyによるボディサイズ制限（DoS対策）
- maxLineによる行長制限（DoS対策）
- FD制限によるリソース枯渇防止

## 備考

- 開発・テスト用途として設計されている
- 本番環境ではnginx等のリバースプロキシを推奨
- HTTP/1.0とHTTP/1.1両方をサポート
- Expect: 100-continueをサポート
- Upgrade接続（WebSocket等）に対応可能

---

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

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

### 推奨読解順序

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

まず、主要なデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | asynchttpserver.nim | `lib/pure/asynchttpserver.nim` | Request型（61-68行目）: リクエスト情報 |
| 1-2 | asynchttpserver.nim | `lib/pure/asynchttpserver.nim` | AsyncHttpServer型（70-75行目）: サーバー設定 |
| 1-3 | asynchttpserver.nim | `lib/pure/asynchttpserver.nim` | maxLine定数（53行目）: 最大行長 |

**読解のコツ**: Request型はclient（AsyncSocket）、reqMethod、headers、protocol、url、hostname、bodyを持つ。

#### Step 2: サーバー作成を理解する

サーバーの初期化処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | asynchttpserver.nim | `lib/pure/asynchttpserver.nim` | newAsyncHttpServer（90-93行目）: サーバー作成 |
| 2-2 | asynchttpserver.nim | `lib/pure/asynchttpserver.nim` | listen（382-395行目）: リッスン開始 |

**主要処理フロー**:
- **93行目**: reuseAddr、reusePort、maxBodyを設定
- **388行目**: newAsyncSocketでソケット作成
- **389-393行目**: setSockOptでオプション設定
- **394-395行目**: bindAddrとlistenで待機開始

#### Step 3: リクエスト処理を理解する

HTTPリクエストの解析処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | asynchttpserver.nim | `lib/pure/asynchttpserver.nim` | processRequest（173-357行目）: リクエスト処理 |
| 3-2 | asynchttpserver.nim | `lib/pure/asynchttpserver.nim` | parseProtocol（148-157行目）: プロトコル解析 |
| 3-3 | asynchttpserver.nim | `lib/pure/asynchttpserver.nim` | hasChunkedEncoding（162-171行目）: Chunked判定 |

**主要処理フロー**:
- **199-214行目**: 空行スキップとリクエスト行読み取り
- **216-249行目**: メソッド、URL、プロトコル解析
- **251-270行目**: ヘッダー解析（headerLimit確認）
- **272-278行目**: Expect: 100-continue処理
- **282-334行目**: ボディ読み取り（Content-Length/Chunked）
- **337行目**: コールバック呼び出し
- **346-357行目**: 持続的接続判定

#### Step 4: レスポンス送信を理解する

HTTPレスポンスの送信処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | asynchttpserver.nim | `lib/pure/asynchttpserver.nim` | respond（105-137行目）: レスポンス送信 |
| 4-2 | asynchttpserver.nim | `lib/pure/asynchttpserver.nim` | respondError（139-146行目）: エラーレスポンス |
| 4-3 | asynchttpserver.nim | `lib/pure/asynchttpserver.nim` | sendHeaders（99-103行目）: ヘッダー送信 |

**主要処理フロー**:
- **123行目**: HTTP/1.1ステータス行生成
- **125-133行目**: ヘッダー追加（Content-Length自動設定）
- **136-137行目**: ボディ追加して送信

#### Step 5: 接続管理を理解する

接続の受け入れと管理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | asynchttpserver.nim | `lib/pure/asynchttpserver.nim` | shouldAcceptRequest（397-403行目）: FD制限確認 |
| 5-2 | asynchttpserver.nim | `lib/pure/asynchttpserver.nim` | acceptRequest（405-410行目）: リクエスト受け入れ |
| 5-3 | asynchttpserver.nim | `lib/pure/asynchttpserver.nim` | serve（412-436行目）: サーバーループ |
| 5-4 | asynchttpserver.nim | `lib/pure/asynchttpserver.nim` | processClient（359-374行目）: クライアント処理 |

**主要処理フロー**:
- **402-403行目**: activeDescriptors + assumedDescriptorsPerRequest < maxFDs確認
- **409行目**: acceptAddrでクライアント接続受け入れ
- **410行目**: asyncCheckでprocessClient非同期実行
- **368-374行目**: processRequestを繰り返し呼び出し

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

```
newAsyncHttpServer(reuseAddr, reusePort, maxBody)
    │
    └─ AsyncHttpServer object initialization

listen(server, port, address, domain)
    │
    ├─ maxDescriptors() → server.maxFDs
    │
    ├─ newAsyncSocket(domain)
    │
    ├─ setSockOpt(OptReuseAddr/OptReusePort)
    │
    ├─ bindAddr(port, address)
    │
    └─ socket.listen()

serve(server, port, callback) / acceptRequest(server, callback)
    │
    ├─ shouldAcceptRequest(server)
    │     └─ activeDescriptors() + assumed < maxFDs
    │
    └─ acceptAddr() → (address, client)
          │
          └─ processClient(server, client, address, callback)
                │
                └─ processRequest(server, req, client, address, lineFut, callback)
                      │
                      ├─ recvLineInto() [リクエスト行]
                      │
                      ├─ parseProtocol()
                      │
                      ├─ recvLineInto() [ヘッダー行]
                      │
                      ├─ parseHeader()
                      │
                      ├─ [Content-Length] recv(contentLength)
                      │
                      ├─ [Chunked] parseHexInt → recv(chunk)
                      │
                      ├─ callback(request)
                      │
                      └─ [Keep-Alive判定]
```

### データフロー図

```
[クライアント]                [asynchttpserver]                [コールバック]

TCP接続要求 ──────────────▶ acceptAddr()
                                   │
                                   ▼
                           processClient()
                                   │
                                   ▼
GET /path HTTP/1.1 ────────▶ processRequest()
Header: value                      │
                                   ├──▶ リクエスト行解析
                                   │
                                   ├──▶ ヘッダー解析
                                   │
[POST時] body ─────────────────────┤
                                   │
                                   ├──▶ Request object作成
                                   │
                                   └──▶ callback(request) ──────────▶ 処理
                                                                       │
HTTP/1.1 200 OK ◀──────────────── respond(code, content) ◀────────────┘
Content-Length: N
<body>
                                   │
                           [Keep-Alive?]
                                   │
                       ┌───────────┴───────────┐
                       │                       │
                  [接続維持]              [接続終了]
                       │                       │
                       ▼                       ▼
              processRequest()            close()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| asynchttpserver.nim | `lib/pure/asynchttpserver.nim` | ソース | 非同期HTTPサーバー実装 |
| asyncnet.nim | `lib/pure/asyncnet.nim` | ソース | 非同期ソケット |
| asyncdispatch.nim | `lib/pure/asyncdispatch.nim` | ソース | 非同期ディスパッチャ |
| httpcore.nim | `lib/pure/httpcore.nim` | ソース | HTTP共通定義 |
| uri.nim | `lib/pure/uri.nim` | ソース | URI解析 |
| nativesockets.nim | `lib/pure/nativesockets.nim` | ソース | getLocalAddr |
