# 機能設計書 76-httpclient

## 概要

本ドキュメントは、Nim標準ライブラリのhttpclientモジュールの機能設計について記載する。本モジュールはWebページやその他のデータを取得するためのシンプルなHTTPクライアントを実装している。

### 本機能の処理概要

httpclientモジュールは、HTTPおよびHTTPS通信を行うための高レベルAPIを提供する。同期版（HttpClient）と非同期版（AsyncHttpClient）の両方をサポートし、GET/POST/PUT/DELETE/PATCH/HEADなどの主要なHTTPメソッドに対応する。プロキシ、SSL/TLS、リダイレクト、マルチパートフォームデータ、プログレス報告などの機能を含む。

**業務上の目的・背景**：Web APIとの連携、ファイルダウンロード、Webスクレイピングなど、現代のアプリケーション開発ではHTTP通信が不可欠である。本モジュールは、これらの用途に対応する使いやすいAPIを提供する。

**機能の利用シーン**：
- Web APIへのリクエスト送信
- ファイルダウンロード
- フォームデータの送信
- Webサービスとの連携
- HTTPSによるセキュア通信

**主要な処理内容**：
1. HTTPクライアントの作成（newHttpClient、newAsyncHttpClient）
2. GETリクエスト（get、getContent）
3. POSTリクエスト（post、postContent）
4. その他HTTPメソッド（put、delete、patch、head）
5. マルチパートフォームデータ送信
6. ファイルダウンロード（downloadFile）
7. プロキシ経由の接続
8. プログレスコールバック

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

**権限による制御**：ファイル書き込み（downloadFile）、ネットワークアクセス権限に依存。

## 関連画面

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

## 機能種別

ネットワーク通信 / HTTPクライアント / API連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| url | Uri/string | Yes | リクエスト先URL | 有効なURL形式、改行文字禁止 |
| httpMethod | HttpMethod | No | HTTPメソッド | デフォルト: HttpGet |
| body | string | No | リクエストボディ | - |
| headers | HttpHeaders | No | HTTPヘッダー | - |
| multipart | MultipartData | No | マルチパートデータ | - |
| userAgent | string | No | User-Agent | デフォルト: Nim-httpclient/バージョン |
| maxRedirects | int | No | 最大リダイレクト数 | デフォルト: 5 |
| timeout | int | No | タイムアウト（ミリ秒） | 同期版のみ、デフォルト: -1 |
| proxy | Proxy | No | プロキシ設定 | HTTP/HTTPS/SOCKS5H対応 |

### 入力データソース

- URL文字列
- リクエストボディ（JSON、フォームデータ等）
- ファイル（マルチパート用）
- 環境変数（http_proxy、https_proxy）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Response | ref object | 同期レスポンスオブジェクト |
| AsyncResponse | ref object | 非同期レスポンスオブジェクト |
| HttpCode | enum | HTTPステータスコード |

### 出力先

- Response.body経由でのボディ取得
- ファイル（downloadFile使用時）

## 処理フロー

### 処理シーケンス

```
1. HTTPクライアント作成（newHttpClient/newAsyncHttpClient）
   └─ SSL/プロキシ/ヘッダー/タイムアウト設定
2. 接続確立（newConnection）
   ├─ プロキシ経由の場合はプロキシ接続
   ├─ SOCKS5Hプロキシの場合はハンドシェイク
   └─ HTTPS/SSL接続の場合はSSLラッピング
3. リクエスト送信（requestAux）
   ├─ ヘッダー生成（generateHeaders）
   ├─ ボディ送信（body/multipart）
   └─ マルチパートの場合はファイルストリーミング
4. レスポンス解析（parseResponse）
   ├─ ステータス行解析
   ├─ ヘッダー解析
   └─ ボディ解析（parseBody）
5. リダイレクト処理（必要に応じて）
   └─ 301/302/303/307/308に従って再リクエスト
6. クライアントクローズ（close）
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[newHttpClient]
    B --> C[request/get/post等]
    C --> D{接続済み?}
    D -->|No| E[newConnection]
    D -->|Yes| F[ヘッダー生成]
    E --> G{プロキシ?}
    G -->|SOCKS5H| H[socks5hHandshake]
    G -->|HTTP/HTTPS| I[CONNECT要求]
    G -->|なし| J[直接接続]
    H --> K{SSL?}
    I --> K
    J --> K
    K -->|Yes| L[startSsl]
    K -->|No| F
    L --> F
    F --> M[リクエスト送信]
    M --> N{マルチパート?}
    N -->|Yes| O[ファイル送信]
    N -->|No| P[ボディ送信]
    O --> Q[parseResponse]
    P --> Q
    Q --> R{リダイレクト?}
    R -->|Yes| S[新URL取得]
    S --> C
    R -->|No| T[レスポンス返却]
    T --> U[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-76-01 | リダイレクト上限 | デフォルトで最大5回のリダイレクトをフォロー | maxRedirects設定 |
| BR-76-02 | SSL自動有効化 | httpsスキームで自動的にSSL使用 | URLがhttps://の場合 |
| BR-76-03 | 証明書検証 | デフォルトでCVerifyPeerで検証 | SSL使用時 |
| BR-76-04 | Keep-Alive | デフォルトでConnection: Keep-Alive | 明示的に指定なし時 |
| BR-76-05 | リダイレクト時のメソッド変更 | 301/302/303ではGETに変更 | POST/PUT等の場合 |

### 計算ロジック

プログレス計算：
- 1秒ごとにonProgressChangedコールバック呼び出し
- total: Content-Lengthから取得
- progress: 累積受信バイト数
- speed: 直近1秒間の受信バイト数

チャンクサイズ解析：
- 16進数文字列からサイズを解析
- `;`以降の拡張パラメータは無視

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

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ProtocolError | プロトコルエラー | HTTP仕様違反 | サーバー応答を確認 |
| HttpRequestError | リクエストエラー | 4xx/5xxレスポンス | ステータスコードを確認 |
| TimeoutError | タイムアウト | 応答なし | timeout値を調整 |
| ValueError | 値エラー | 無効なURL/パラメータ | 入力値を検証 |
| IOError | 入出力エラー | ファイル操作失敗 | ファイルパスを確認 |

### リトライ仕様

- 自動リトライは実装されていない
- リダイレクトはmaxRedirects回まで自動フォロー
- Keep-Aliveで接続を再利用

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

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

## パフォーマンス要件

- Keep-Aliveによる接続再利用
- チャンク転送エンコーディングのストリーミング処理
- ファイルストリーミング（マルチパート）
- 非同期版による並列リクエスト対応

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

- SSL/TLS暗号化（-d:ssl）
- 証明書検証（CVerifyPeer/CVerifyPeerUseEnvVars/CVerifyNone）
- プロキシ認証（Basic認証）
- リダイレクト時の認証ヘッダー削除（異なるドメインへの場合）
- 悪意あるURIの警告（ドキュメント記載）

## 備考

- 非同期クライアントは1インスタンスで1リクエストのみ同時実行可能
- 複数リクエストの並列実行には複数インスタンスが必要
- User-AgentのデフォルトはNim-httpclient/バージョン
- SOCKS5Hプロキシはプロキシ側でDNS解決

---

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

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

### 推奨読解順序

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | httpclient.nim | `lib/pure/httpclient.nim` | Response型（284-289行目）: 同期レスポンス |
| 1-2 | httpclient.nim | `lib/pure/httpclient.nim` | AsyncResponse型（291-296行目）: 非同期レスポンス |
| 1-3 | httpclient.nim | `lib/pure/httpclient.nim` | HttpClientBase型（617-642行目）: クライアント基底型 |
| 1-4 | httpclient.nim | `lib/pure/httpclient.nim` | Proxy型（348-349行目）: プロキシ設定 |
| 1-5 | httpclient.nim | `lib/pure/httpclient.nim` | MultipartData型（361-362行目）: マルチパートデータ |

**読解のコツ**: HttpClientBaseはSocketTypeをジェネリックパラメータとして持ち、HttpClient（Socket）とAsyncHttpClient（AsyncSocket）で共通のロジックを使用。

#### Step 2: クライアント作成を理解する

HTTPクライアントの生成処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | httpclient.nim | `lib/pure/httpclient.nim` | newHttpClient（647-685行目）: 同期クライアント作成 |
| 2-2 | httpclient.nim | `lib/pure/httpclient.nim` | newAsyncHttpClient（690-728行目）: 非同期クライアント作成 |

**主要処理フロー**:
- **676-685行目**: headers、userAgent、maxRedirects、proxy、timeout、sslContext設定
- **725行目**: bodyStreamをFutureStream[string]で初期化（非同期版）

#### Step 3: 接続処理を理解する

サーバーへの接続処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | httpclient.nim | `lib/pure/httpclient.nim` | newConnection（1020-1092行目）: 接続確立 |
| 3-2 | httpclient.nim | `lib/pure/httpclient.nim` | startSsl（969-976行目）: SSL開始 |
| 3-3 | httpclient.nim | `lib/pure/httpclient.nim` | socks5hHandshake（978-1018行目）: SOCKS5Hハンドシェイク |

**主要処理フロー**:
- **1022-1025行目**: ホスト名/スキーム/ポートが変わった場合のみ再接続
- **1056-1059行目**: net.dial/asyncnet.dialで接続
- **1062-1066行目**: SOCKS5Hプロキシの場合はハンドシェイク後にSSL
- **1068-1088行目**: HTTPプロキシ経由のHTTPSはCONNECT要求

#### Step 4: リクエスト送信を理解する

HTTPリクエストの送信処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | httpclient.nim | `lib/pure/httpclient.nim` | generateHeaders（570-610行目）: ヘッダー生成 |
| 4-2 | httpclient.nim | `lib/pure/httpclient.nim` | requestAux（1148-1208行目）: リクエスト実行 |
| 4-3 | httpclient.nim | `lib/pure/httpclient.nim` | request（1210-1310行目）: リクエスト（リダイレクト処理含む） |

**主要処理フロー**:
- **572-589行目**: HTTPメソッド、パス、HTTP/1.1ヘッダー生成
- **1186行目**: socket.sendでヘッダー送信
- **1264-1310行目**: リダイレクト（301/302/303/307/308）処理

#### Step 5: レスポンス解析を理解する

HTTPレスポンスの解析処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | httpclient.nim | `lib/pure/httpclient.nim` | parseResponse（880-967行目）: レスポンス解析 |
| 5-2 | httpclient.nim | `lib/pure/httpclient.nim` | parseBody（826-878行目）: ボディ解析 |
| 5-3 | httpclient.nim | `lib/pure/httpclient.nim` | parseChunks（789-824行目）: チャンク転送解析 |

**主要処理フロー**:
- **903-918行目**: HTTP/1.0/1.1バージョンとステータスコード解析
- **837-838行目**: Transfer-Encoding: chunkedの場合はparseChunks
- **842-853行目**: Content-Lengthがある場合は指定サイズ受信

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

```
newHttpClient(userAgent, maxRedirects, sslContext, proxy, timeout)
    │
    └─ HttpClient object initialization

request(client, url, httpMethod, body, headers, multipart)
    │
    ├─ requestAux(client, url, httpMethod, body, headers, multipart)
    │     │
    │     ├─ newConnection(client, url)
    │     │     │
    │     │     ├─ net.dial / asyncnet.dial
    │     │     │
    │     │     ├─ [SOCKS5H] socks5hHandshake(client, url)
    │     │     │
    │     │     └─ [SSL] startSsl(client, hostname)
    │     │           └─ sslContext.wrapConnectedSocket()
    │     │
    │     ├─ generateHeaders(url, httpMethod, headers, proxy)
    │     │
    │     ├─ socket.send(headerString)
    │     │
    │     ├─ [multipart] sendFile(socket, entry)
    │     │
    │     └─ parseResponse(client, getBody)
    │           │
    │           └─ parseBody(client, headers, version)
    │                 │
    │                 ├─ [chunked] parseChunks(client)
    │                 │
    │                 └─ [content-length] recvFull(client, length)
    │
    └─ [redirect] getNewLocation(lastURL, headers)
          └─ requestAux(...)  [再帰]
```

### データフロー図

```
[アプリケーション]              [httpclient]                [サーバー]

newHttpClient() ──────────▶ HttpClient object
         │
         │
request(url) ──────────────▶ newConnection()
                                   │
                                   ├──▶ [プロキシ] socks5hHandshake()
                                   │
                                   └──▶ [SSL] startSsl()
                                              │
                                              ▼
                             generateHeaders() ──────────▶ HTTP Request
                                              │
                             socket.send() ───┘
                                              │
                                              ▼
                             parseResponse() ◀────────── HTTP Response
                                   │
                                   ├──▶ parseBody()
                                   │         │
                                   │         ├──▶ [chunked] parseChunks()
                                   │         │
                                   │         └──▶ [content-length] recvFull()
                                   │
                                   ▼
Response ◀─────────────────────────┘
         │
         ▼
response.body() ──────────▶ bodyStream.readAll()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| httpclient.nim | `lib/pure/httpclient.nim` | ソース | HTTPクライアント実装 |
| httpcore.nim | `lib/pure/httpcore.nim` | ソース | HTTP共通定義（HttpMethod、HttpHeaders等） |
| net.nim | `lib/pure/net.nim` | ソース | 同期ソケット/SSL |
| asyncnet.nim | `lib/pure/asyncnet.nim` | ソース | 非同期ソケット |
| asyncdispatch.nim | `lib/pure/asyncdispatch.nim` | ソース | 非同期ディスパッチャ |
| uri.nim | `lib/pure/uri.nim` | ソース | URI解析 |
| mimetypes.nim | `lib/pure/mimetypes.nim` | ソース | MIMEタイプ判定 |
