# 機能設計書: HTTPリクエスト

## 1. 機能概要

### 1.1 機能名
HTTPリクエスト - HTTPRequestノードによるWeb通信

### 1.2 機能説明
HTTPRequestは、HTTP/HTTPSプロトコルによるWeb通信を簡易的に行うためのノードである。GET、POST、PUT、DELETE等のHTTPメソッドをサポートし、RESTful APIとの連携、ファイルダウンロード、Webサービスとの通信などを実現する。非同期処理に対応し、マルチスレッドでの通信もサポートする。gzip圧縮、リダイレクト追従、TLS/SSL暗号化、プロキシ設定などの機能を備える。

### 1.3 関連画面
- なし（スクリプトベースの機能）

## 2. 機能詳細

### 2.1 主要機能一覧

| 機能ID | 機能名 | 説明 |
|--------|--------|------|
| F38-01 | HTTPリクエスト送信 | GET/POST/PUT/DELETE等の送信 |
| F38-02 | レスポンス受信 | ボディ・ヘッダーの受信 |
| F38-03 | ファイルダウンロード | 大容量ファイルのストリーミングDL |
| F38-04 | TLS/SSL対応 | HTTPS通信のサポート |
| F38-05 | リダイレクト追従 | 301/302リダイレクトの自動追従 |
| F38-06 | gzip圧縮 | gzip/deflate圧縮の自動展開 |
| F38-07 | マルチスレッド | バックグラウンドスレッドでの通信 |
| F38-08 | タイムアウト | リクエストタイムアウト設定 |

### 2.2 クラス構造

```
Node
    └── HTTPRequest
            │
            └── HTTPClient (内部使用)
                    └── StreamPeerGZIP (gzip展開)
```

### 2.3 主要プロパティ

| プロパティ名 | 型 | デフォルト値 | 説明 |
|-------------|-----|-------------|------|
| download_file | String | "" | ダウンロード先ファイルパス |
| download_chunk_size | int | 65536 | チャンクサイズ(バイト) |
| use_threads | bool | false | スレッド使用フラグ |
| accept_gzip | bool | true | gzip受け入れフラグ |
| body_size_limit | int | -1 | ボディサイズ制限(-1で無制限) |
| max_redirects | int | 8 | 最大リダイレクト回数 |
| timeout | double | 0.0 | タイムアウト秒(0で無制限) |

### 2.4 主要メソッド

| メソッド名 | 戻り値 | 説明 |
|-----------|--------|------|
| request(url, custom_headers, method, request_data) | Error | リクエストを送信 |
| request_raw(url, custom_headers, method, request_data_raw) | Error | バイナリデータでリクエスト |
| cancel_request() | void | リクエストをキャンセル |
| get_http_client_status() | HTTPClient.Status | 接続状態を取得 |
| get_downloaded_bytes() | int | ダウンロード済みバイト数 |
| get_body_size() | int | ボディの総サイズ |
| set_tls_options(client_options) | void | TLSオプションを設定 |
| set_http_proxy(host, port) | void | HTTPプロキシを設定 |
| set_https_proxy(host, port) | void | HTTPSプロキシを設定 |

### 2.5 HTTPClient.Method列挙型

| 定数 | 値 | 説明 |
|------|-----|------|
| METHOD_GET | 0 | GETメソッド |
| METHOD_HEAD | 1 | HEADメソッド |
| METHOD_POST | 2 | POSTメソッド |
| METHOD_PUT | 3 | PUTメソッド |
| METHOD_DELETE | 4 | DELETEメソッド |
| METHOD_OPTIONS | 5 | OPTIONSメソッド |
| METHOD_TRACE | 6 | TRACEメソッド |
| METHOD_CONNECT | 7 | CONNECTメソッド |
| METHOD_PATCH | 8 | PATCHメソッド |

### 2.6 Result列挙型

| 定数 | 説明 |
|------|------|
| RESULT_SUCCESS | 成功 |
| RESULT_CHUNKED_BODY_SIZE_MISMATCH | チャンクサイズ不一致 |
| RESULT_CANT_CONNECT | 接続失敗 |
| RESULT_CANT_RESOLVE | 名前解決失敗 |
| RESULT_CONNECTION_ERROR | 接続エラー |
| RESULT_TLS_HANDSHAKE_ERROR | TLSハンドシェイクエラー |
| RESULT_NO_RESPONSE | レスポンスなし |
| RESULT_BODY_SIZE_LIMIT_EXCEEDED | ボディサイズ制限超過 |
| RESULT_BODY_DECOMPRESS_FAILED | 解凍失敗 |
| RESULT_REQUEST_FAILED | リクエスト失敗 |
| RESULT_DOWNLOAD_FILE_CANT_OPEN | ファイルオープン失敗 |
| RESULT_DOWNLOAD_FILE_WRITE_ERROR | ファイル書き込みエラー |
| RESULT_REDIRECT_LIMIT_REACHED | リダイレクト制限到達 |
| RESULT_TIMEOUT | タイムアウト |

### 2.7 シグナル

| シグナル名 | 引数 | 説明 |
|-----------|------|------|
| request_completed | result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray | リクエスト完了時 |

## 3. 処理フロー

### 3.1 HTTPリクエスト処理フロー

```
[request() / request_raw() 呼び出し]
      │
      ▼
[_parse_url()] - URL解析
      │
      ├─ スキーム抽出 (http:// / https://)
      ├─ ホスト名抽出
      ├─ ポート番号決定 (デフォルト: HTTP=80, HTTPS=443)
      └─ リクエストパス抽出
      │
      ▼
[use_threads チェック]
      │
      ├─ true → [_thread_func() - 別スレッド]
      │              │
      │              └─ client->set_blocking_mode(true)
      │
      └─ false → [_request() - メインスレッド]
                     │
                     └─ client->set_blocking_mode(false)
      │
      ▼
[client->connect_to_host()]
      │
      ▼
[_update_connection() - ポーリング]
      │
      ├─ STATUS_CONNECTING → 待機
      ├─ STATUS_CONNECTED → リクエスト送信
      ├─ STATUS_REQUESTING → 待機
      └─ STATUS_BODY → ボディ受信
              │
              ├─ [gzip展開処理]
              │       └─ decompressor->put_partial_data()
              │
              ├─ [ファイル書き込み] (download_file設定時)
              │       └─ file->store_buffer()
              │
              └─ [メモリ蓄積] (通常時)
                      └─ body.append_array(chunk)
      │
      ▼
[リダイレクト処理] (301/302)
      │
      ├─ max_redirects以内？
      │       │
      │       ├─ Yes → 新URLで再リクエスト
      │       │            └─ メソッド変更判定 (POST→GET等)
      │       │
      │       └─ No → RESULT_REDIRECT_LIMIT_REACHED
      │
      ▼
[request_completed シグナル発行]
      │
      └─ (result, response_code, headers, body)
```

### 3.2 接続状態遷移

```
[STATUS_DISCONNECTED]
        │
        │ connect_to_host()
        ▼
[STATUS_RESOLVING]
        │
        │ DNS解決完了
        ▼
[STATUS_CONNECTING]
        │
        │ TCP接続完了
        ├─────────────────────┐
        │                     │
        ▼                     ▼
[STATUS_CONNECTED]    [STATUS_TLS_HANDSHAKE]
        │                     │
        │                     │ TLS完了
        │                     │
        │ ←────────────────────
        │
        │ request()
        ▼
[STATUS_REQUESTING]
        │
        │ レスポンス開始
        ▼
[STATUS_BODY]
        │
        │ 完了/切断
        ▼
[STATUS_DISCONNECTED]
```

### 3.3 データフロー図

```
[request() 呼び出し]
        │
        │ URL, headers, method, data
        ▼
┌───────────────────────────────────────┐
│            HTTPRequest                 │
│                                        │
│  ┌────────────────────────────────┐   │
│  │         HTTPClient             │   │
│  │  - connect_to_host()           │   │
│  │  - request()                   │   │
│  │  - read_response_body_chunk()  │   │
│  └────────────────────────────────┘   │
│              │                        │
│              ▼                        │
│  ┌────────────────────────────────┐   │
│  │      StreamPeerGZIP            │   │
│  │  (gzip/deflate展開)            │   │
│  └────────────────────────────────┘   │
│              │                        │
│              ▼                        │
│  ┌────────────────────────────────┐   │
│  │   FileAccess (オプション)       │   │
│  │   (download_file設定時)         │   │
│  └────────────────────────────────┘   │
└───────────────────────────────────────┘
        │
        │ request_completed シグナル
        ▼
[コールバック関数]
  (result, response_code, headers, body)
```

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

### 4.1 推奨読解順序

1. **データ構造の理解**
   - `scene/main/http_request.h` (41-173行目): HTTPRequestクラス定義

2. **エントリーポイント**
   - `http_request.cpp` **_bind_methods()** (634-693行目): GDScriptバインディング
   - `http_request.cpp` **request()** (103-116行目): リクエスト開始

3. **コア処理**
   - `http_request.cpp` **request_raw()** (118-164行目): 実際のリクエスト処理
   - `http_request.cpp` **_update_connection()** (322-506行目): 接続状態管理

### 4.2 重要な処理ポイント

#### URL解析処理（http_request.cpp: 40-70行目）
```cpp
Error HTTPRequest::_parse_url(const String &p_url) {
    use_tls = false;
    String scheme;
    Error err = p_url.parse_url(scheme, url, port, request_string, fragment);

    if (scheme == "https://") {
        use_tls = true;
    }
    if (port == 0) {
        port = use_tls ? 443 : 80;
    }
    // ...
}
```

#### リダイレクト処理（http_request.cpp: 252-304行目）
```cpp
bool HTTPRequest::_handle_response(bool *ret_value) {
    if (response_code == 301 || response_code == 302) {
        if (max_redirects >= 0 && redirections >= max_redirects) {
            _defer_done(RESULT_REDIRECT_LIMIT_REACHED, ...);
            return true;
        }
        // 新URLで再リクエスト
        // POSTなど安全でないメソッドはGETに変更
        if (!_is_method_safe()) {
            method = HTTPClient::METHOD_GET;
        }
    }
}
```

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

```
HTTPRequest::request(url, headers, method, data)
    │
    └── HTTPRequest::request_raw()
            │
            ├── _parse_url(url)
            │       └─ スキーム/ホスト/ポート/パス解析
            │
            ├── [スレッドモード判定]
            │       │
            │       ├── use_threads == true
            │       │       └── thread.start(_thread_func, this)
            │       │               │
            │       │               └── _request() + _update_connection() ループ
            │       │
            │       └── use_threads == false
            │               └── set_process_internal(true)
            │                       │
            │                       └── [_notification(NOTIFICATION_INTERNAL_PROCESS)]
            │                               └── _update_connection()
            │
            └── _request()
                    └── client->connect_to_host(url, port, tls_options)

HTTPRequest::_update_connection()  [ポーリング]
    │
    ├── STATUS_CONNECTED
    │       └── client->request(method, request_string, headers, data)
    │
    ├── STATUS_BODY
    │       │
    │       ├── _handle_response()
    │       │       └── リダイレクト処理
    │       │
    │       ├── client->read_response_body_chunk()
    │       │
    │       ├── [gzip展開]
    │       │       └── decompressor->put_partial_data() + get_data()
    │       │
    │       └── [ファイル書き込み or メモリ蓄積]
    │
    └── 完了時
            └── _defer_done() → _request_done()
                    │
                    ├── cancel_request()
                    │
                    └── emit_signal("request_completed", ...)
```

### 4.4 関連ファイル一覧

| ファイルパス | 種別 | 役割 |
|-------------|------|------|
| scene/main/http_request.h | ヘッダー | HTTPRequestクラス定義 |
| scene/main/http_request.cpp | 実装 | HTTPRequest実装 |
| core/io/http_client.h | ヘッダー | HTTPClient定義 |
| core/io/http_client.cpp | 実装 | 低レベルHTTP処理 |
| core/io/stream_peer_gzip.h | ヘッダー | gzip展開 |
| core/crypto/crypto.h | ヘッダー | TLS/SSL処理 |

## 5. 設計上の考慮事項

### 5.1 非同期処理
- シグナルベースの完了通知
- use_threadsによるバックグラウンド処理
- cancel_request()による中断サポート

### 5.2 セキュリティ
- TLS/SSL暗号化のデフォルトサポート
- TLSOptionsによる証明書検証設定
- プロキシ設定のサポート

### 5.3 リソース管理
- body_size_limitによるメモリ制限
- download_fileによるストリーミング保存
- タイムアウト設定によるリソース解放

## 6. 使用例

### 6.1 基本的なGETリクエスト
```gdscript
@onready var http = $HTTPRequest

func _ready():
    http.request_completed.connect(_on_request_completed)
    http.request("https://api.example.com/data")

func _on_request_completed(result, response_code, headers, body):
    if result != HTTPRequest.RESULT_SUCCESS:
        print("Error: ", result)
        return

    var json = JSON.parse_string(body.get_string_from_utf8())
    print(json)
```

### 6.2 POSTリクエスト
```gdscript
func post_data(data: Dictionary):
    var json = JSON.stringify(data)
    var headers = ["Content-Type: application/json"]
    http.request("https://api.example.com/submit",
                 headers,
                 HTTPClient.METHOD_POST,
                 json)
```

### 6.3 ファイルダウンロード
```gdscript
func download_file(url: String, path: String):
    http.download_file = path
    http.request(url)

func _on_request_completed(result, response_code, headers, body):
    if result == HTTPRequest.RESULT_SUCCESS:
        print("Download complete: ", http.download_file)
```

### 6.4 スレッドを使用した通信
```gdscript
func _ready():
    http.use_threads = true
    http.request_completed.connect(_on_request_completed)
    http.request("https://api.example.com/large-data")
```

### 6.5 タイムアウト設定
```gdscript
func request_with_timeout():
    http.timeout = 10.0  # 10秒でタイムアウト
    http.request("https://api.example.com/slow-endpoint")
```

## 7. 関連機能
- [No.39 WebSocket](./39-WebSocket.md) - 双方向リアルタイム通信
