# 機能設計書 77-HTTPダウンロード (serve-d:http)

## 概要

本ドキュメントは、serve-dのサブパッケージ `serve-d:http` の設計を記述する。プログレス表示付きのHTTPファイルダウンロード機能を提供するサブパッケージである。

### 本機能の処理概要

**業務上の目的・背景**：serve-dがDCDのプリビルドバイナリをダウンロードする際に使用されるHTTPダウンロード機能を提供する。プラットフォーム固有の実装（Windows: WinINet API、その他: requestsライブラリ）によりHTTPファイルダウンロードを実行し、ダウンロード進捗をコールバックで通知する。

**機能の利用シーン**：DCD更新処理（served/updateDCD）でDCDバイナリをGitHubからダウンロードする際に使用される。ダウンロード中の進捗はcoded/logInstall通知を通じてクライアントに送信される。

**主要な処理内容**：
1. HTTPダウンロード実行（downloadFileManual関数）：URLからファイルをダウンロードしローカルファイルに保存
2. プログレスコールバック：1秒ごとにダウンロード進捗をコールバックで通知
3. ファイバー連携：ダウンロード中にFiber.yieldを呼び出し、他の処理をブロックしない
4. 人間可読サイズ変換（humanSize関数）：バイト数をkiB/MiB等の単位に変換

**関連システム・外部連携**：Windows環境ではWinINet API（InternetOpenA、InternetOpenUrlA、InternetReadFile）、その他の環境ではrequestsライブラリを使用する。

**権限による制御**：なし

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 14 | インストールログ通知 | 結果表示画面 | ダウンロード進捗がcoded/logInstall通知で送信される |

## 機能種別

データ連携 / ファイルダウンロード

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| url | string | Yes | ダウンロード元のURL | 有効なHTTP/HTTPS URL |
| title | string | Yes | ダウンロードの表示タイトル | - |
| into | string | Yes | ダウンロード先のローカルファイルパス | 書き込み可能なパス |
| onLog | void delegate(string) | Yes | 進捗通知コールバック | - |

### 入力データソース

- DCD更新処理からの呼び出し（dcd_update.dモジュール）
- ダウンロード元URL（GitHubリリースページ等）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ローカルファイル | File | ダウンロードされたバイナリファイル |
| 進捗メッセージ | string | "{title} {received} / {total} ({percent} %)" 形式の進捗文字列 |

### 出力先

- ローカルファイルシステム（into引数で指定されたパス）
- onLogコールバックへの進捗通知

## 処理フロー

### 処理シーケンス

```
1. ファイルオープン
   └─ intoパスに書き込みモードでファイルを開く
2. HTTP接続確立
   └─ プラットフォーム固有のHTTPクライアントでURLに接続
3. ダウンロードループ
   └─ データチャンクを受信しファイルに書き込む
4. 進捗通知（1秒ごと）
   └─ onLogコールバックに進捗メッセージを送信
5. Fiber.yield
   └─ 他のファイバーに制御を渡す
6. ダウンロード完了
   └─ ファイルクローズ
```

### フローチャート

```mermaid
flowchart TD
    A[ファイルオープン] --> B[HTTP接続確立]
    B --> C{データチャンク受信}
    C -->|受信あり| D[ファイルに書き込み]
    D --> E{1秒経過?}
    E -->|Yes| F[進捗通知 + Fiber.yield]
    E -->|No| C
    F --> C
    C -->|受信完了| G[ダウンロード完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-77-01 | プラットフォーム固有実装 | WindowsはWinINet API、その他はrequestsライブラリを使用 | コンパイル時にversion(Windows)で分岐 |
| BR-77-02 | 進捗通知間隔 | 1秒ごとにonLogコールバックで進捗を通知 | ダウンロード中 |
| BR-77-03 | ファイバー連携 | 進捗通知後にFiber.yieldで他の処理に制御を渡す | 進捗通知時 |
| BR-77-04 | ストリーミングダウンロード | データをストリーミングで受信し逐次ファイルに書き込む | ダウンロード中 |

### 計算ロジック

humanSize関数：バイト数を人間可読形式に変換する。
- 1024未満: "{bytes} B"
- 1024以上: `log2(bytes) / 8`で適切な単位プレフィックス（k, M, G, T, P, E）を選択し、"{value:.1f} {prefix}iB"形式で表示

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

本機能ではデータベース操作は行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Exception | Windows: InternetReadFileが失敗した場合 | "Failed to read from internet file"例外を発生 |
| - | enforce失敗 | Windows: InternetOpenAまたはInternetOpenUrlAが失敗した場合 | 対応するエラーメッセージ付き例外 |

### リトライ仕様

リトライは行わない。呼び出し元（DCD更新処理）でエラーハンドリングを行う。

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

不要（ファイルダウンロードはアトミックではない。中断時は不完全なファイルが残る）。

## パフォーマンス要件

- Windows: 50KB バッファでチャンク受信
- 非同期処理: Fiber.yieldにより他のサーバー処理をブロックしない
- ストリーミング: メモリ使用量を最小化

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

HTTPSを使用してダウンロードを行う。Windows環境ではWinINetのプリコンフィグプロキシ設定が使用される（INTERNET_OPEN_TYPE_PRECONFIG）。User-Agent文字列は"serve-d_release-downloader/Pure-D/serve-d"。

## 備考

- InteractiveDownload構造体はurl、title、output（保存先パス）を束ねた便利構造体
- Content-Lengthが取得できない場合（maxLen == 0）、進捗は"??? "と表示される
- Windows版はpragma(lib, "wininet")でWinINetライブラリを自動リンク

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | http.d | `http/source/served/http.d` | InteractiveDownload構造体（25-31行目）: url, title, outputの3フィールド |

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | http.d | `http/source/served/http.d` | downloadFileManual関数（32-110行目）: メイン処理 |

**主要処理フロー**:
1. **34行目**: `File(into, "wb")`でダウンロード先ファイルを書き込みモードで開く
2. **36-37行目**: StopWatchで進捗通知のタイミングを計測
3. **39-89行目**: Windows版: WinINet APIでHTTPダウンロード
   - **47行目**: `InternetOpenA`でインターネットハンドルオープン
   - **52行目**: `InternetOpenUrlA`でURLオープン
   - **62-63行目**: `HttpQueryInfoA`でContent-Lengthを取得
   - **69行目**: `InternetReadFile`でデータチャンクを読み込み
4. **91-109行目**: 非Windows版: requestsライブラリでストリーミングダウンロード
   - **93行目**: `req.useStreaming = true`でストリーミングモード
   - **97行目**: `res.receiveAsRange`でチャンクを受信

#### Step 3: ユーティリティ関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | http.d | `http/source/served/http.d` | humanSize関数（112-120行目）: バイト数の人間可読変換 |

**主要処理フロー**:
- **116-117行目**: 1024未満はバイト表示
- **118行目**: log2(bytes)/8で単位指数を算出
- **119行目**: kMGTPEプレフィックスから適切な単位を選択

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

```
DCD更新処理 (dcd_update.d)
    │
    └─ downloadFileManual (http.d: 32行目)
           │
           ├─ [Windows] WinINet API
           │      ├─ InternetOpenA (47行目)
           │      ├─ InternetOpenUrlA (52行目)
           │      ├─ HttpQueryInfoA (62行目)
           │      ├─ InternetReadFile (69行目)
           │      └─ InternetCloseHandle (scope exit)
           │
           ├─ [非Windows] requests ライブラリ
           │      ├─ Request.get (95行目)
           │      └─ Response.receiveAsRange (97行目)
           │
           ├─ File.rawWrite (ファイル書き込み)
           ├─ onLog (進捗通知)
           │      └─ humanSize (112行目)
           └─ Fiber.yield (制御譲渡)
```

### データフロー図

```
[入力]                    [処理]                       [出力]

URL ──────────────▶ downloadFileManual ──────▶ ローカルファイル
                     (HTTP GET + ストリーム)

                     ┌─ 1秒ごと ─────────────▶ onLogコールバック
                     │  (進捗メッセージ)          (進捗表示)
                     │
                     └─ Fiber.yield ──────────▶ サーバー応答性維持
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| http.d | `http/source/served/http.d` | ソース | HTTPダウンロード機能の全実装 |
| dcd_update.d | `source/served/commands/dcd_update.d` | ソース | DCD更新処理（本機能の呼び出し元） |
