# 機能設計書 158-Unix\connect_async

## 概要

本ドキュメントは、Hack Standard Library (HSL) における `Unix\connect_async` 関数の機能設計を記載する。この関数は Unix ドメインソケットへの非同期接続機能を提供する。

### 本機能の処理概要

`Unix\connect_async` は、指定されたファイルシステムパスに存在する Unix ドメインソケットに対して非同期で接続を確立する関数である。Unix ドメインソケットは同一ホスト上のプロセス間通信（IPC）に使用され、TCP よりもオーバーヘッドが小さく高速な通信が可能である。接続成功後は `CloseableSocket` インターフェースを実装したソケットハンドルを返す。

**業務上の目的・背景**：Unix ドメインソケットは、同一マシン上のプロセス間通信において TCP よりも効率的な通信手段を提供する。データベースサーバー（MySQL、PostgreSQL など）やアプリケーションサーバー（PHP-FPM、uWSGI など）との通信で広く使用される。非同期接続により、接続待機中に他の処理を実行でき、アプリケーションのレスポンス性を向上させることができる。

**機能の利用シーン**：
- ローカルデータベースサーバーへの接続（MySQL の `/var/run/mysqld/mysqld.sock` など）
- PHP-FPM や uWSGI などのアプリケーションサーバーとの通信
- Docker コンテナ内のプロセス間通信（`/var/run/docker.sock` など）
- ローカルサービス間の高速通信
- セキュリティが重要なローカル IPC（ネットワーク経由でない通信）

**主要な処理内容**：
1. ソケットの作成（PF_UNIX ドメイン）
2. sockaddr_un 構造体の構築（パス指定）
3. 非同期接続の実行
4. 接続成功時にソケットハンドルを返却

**関連システム・外部連携**：オペレーティングシステムの Unix ドメインソケット機能を使用。ファイルシステム上のソケットファイルに接続する。

**権限による制御**：Unix ドメインソケットへのアクセスはファイルシステムのパーミッションに従う。ソケットファイルへの読み書き権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 該当なし | - | 本プロジェクトはUIを持たないライブラリのため画面なし |

## 機能種別

データ連携（プロセス間通信）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| $path | string | Yes | Unix ソケットのファイルパス | - |
| $opts | ConnectOptions | No | 接続オプション。デフォルトは空の shape | - |

### ConnectOptions の構造

| フィールド名 | 型 | 説明 |
|-------------|-----|------|
| timeout_ns | ?int | 接続タイムアウト（ナノ秒単位）。未指定でタイムアウトなし |

### 入力データソース

ファイルシステム（Unix ドメインソケットファイル）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | Awaitable<CloseableSocket> | 接続された Unix ソケット。読み書き可能なハンドル |

### CloseableSocket インターフェース

| メソッド | 戻り値 | 説明 |
|---------|--------|------|
| getLocalAddress() | ?string | ローカル側のパスを取得（クライアント側は通常 null） |
| getPeerAddress() | ?string | リモート側のパス（接続先のソケットパス）を取得 |
| readAllowPartialSuccessAsync() | Awaitable<string> | データを非同期で読み取り |
| writeAllowPartialSuccessAsync(string) | Awaitable<int> | データを非同期で書き込み |
| close() | void | ソケットを閉じる |

### 出力先

関数の戻り値として呼び出し元に返却（Awaitable）

## 処理フロー

### 処理シーケンス

```
1. タイムアウト値の取得
   └─ オプションから timeout_ns を取得（デフォルト: 0）
2. ソケット作成
   └─ OS\socket(PF_UNIX, SOCK_STREAM, 0)
3. sockaddr_un 構造体の構築
   └─ OS\sockaddr_un($path)
4. 非同期接続の実行
   └─ _Network\socket_connect_async($sock, $sa, $timeout_ns)
5. ソケットハンドルの返却
   └─ _Unix\CloseableSocket($sock)
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[タイムアウト値取得]
    B --> C[PF_UNIX ソケット作成]
    C --> D[sockaddr_un 構築]
    D --> E[非同期接続試行]
    E --> F{接続成功?}
    F -->|Yes| G[CloseableSocket 返却]
    F -->|No| H[例外をスロー]
    G --> I[終了]
    H --> J[異常終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-158-01 | パス制約 | Unix ソケットパスは OS の制限（通常 108 バイト）を超えてはならない | 常時 |
| BR-158-02 | ノンブロッキング | 接続処理は非同期で実行され、呼び出し元をブロックしない | 常時 |
| BR-158-03 | ファイル権限 | ソケットファイルへのアクセス権限が必要 | 常時 |
| BR-158-04 | ローカル通信 | Unix ドメインソケットは同一ホスト上の通信にのみ使用可能 | 常時 |

### 計算ロジック

Unix ドメインソケットは TCP と異なり、ホスト名解決や IP プロトコルバージョンの考慮が不要なため、処理が単純化されている。

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

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ENOENT | OS\ErrnoException | ソケットファイルが存在しない | パスを確認、サーバーが起動しているか確認 |
| ECONNREFUSED | OS\ErrnoException | 接続が拒否された | サーバーがリッスンしているか確認 |
| EACCES | OS\ErrnoException | アクセス権限がない | ファイルパーミッションを確認 |
| ETIMEDOUT | OS\ErrnoException | 接続がタイムアウト | タイムアウト値を調整 |
| ECONNRESET | OS\ErrnoException | 接続がリセットされた | リトライを検討 |

### リトライ仕様

本関数自体にはリトライ機構はない。アプリケーションレベルでのリトライは呼び出し側で実装する。

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

本機能はトランザクションを使用しない。接続はアトミックな操作として扱われる。

## パフォーマンス要件

- 接続確立のレイテンシ：マイクロ秒オーダー（TCP より高速）
- 同一ホスト上の通信であるため、ネットワークスタックのオーバーヘッドが少ない
- 非同期実行により、接続待機中に他の処理が可能

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

- Unix ドメインソケットは同一ホスト上の通信に限定されるため、ネットワーク経由の攻撃を受けない
- アクセス制御はファイルシステムのパーミッションで行われる
- ソケットファイルのパーミッションを適切に設定することでアクセスを制限可能
- ネットワーク経由での接続が必要な場合は `TCP\connect_async` を使用すること

## 備考

- 非推奨の `connect_nd_async` 関数が存在するが、`connect_async` を使用すること
- 戻り値の `CloseableSocket` は使用後に必ず `close()` を呼び出すこと
- TCP 接続が必要な場合は `TCP\connect_async` を使用すること
- Unix ドメインソケットのパスは通常 `/var/run/` や `/tmp/` 以下に配置される
- クライアント側の `getLocalAddress()` は通常 null を返す（匿名ソケット）

---

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

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

### 推奨読解順序

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

接続オプションとソケット関連の型定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ConnectOptions.php | `src/unix/ConnectOptions.php` | ConnectOptions 型エイリアスの定義 |
| 1-2 | Socket.php | `src/unix/Socket.php` | Unix ソケットインターフェース定義。TAddress の型は ?string |
| 1-3 | CloseableSocket.php | `src/unix/CloseableSocket.php` | 閉じることができるソケットのインターフェース |

**読解のコツ**: TCP と異なり、Unix ソケットのアドレスは文字列（パス）または null で表現される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | connect.php | `src/unix/connect.php` | connect_async 関数の実装（17-29行目） |

**主要処理フロー**:
1. **17-20行目**: 関数宣言。$path と $opts を引数として取る
2. **21行目**: オプションから timeout_ns を取得（デフォルト: 0）
3. **22行目**: PF_UNIX ドメインでソケット作成
4. **23-27行目**: socket_connect_async で非同期接続
5. **28行目**: CloseableSocket を返却

```hack
async function connect_async(
  string $path,
  ConnectOptions $opts = shape(),
): Awaitable<CloseableSocket> {
  $timeout_ns = $opts['timeout_ns'] ?? 0;
  $sock = OS\socket(OS\SocketDomain::PF_UNIX, OS\SocketType::SOCK_STREAM, 0);
  await _Network\socket_connect_async(
    $sock,
    new OS\sockaddr_un($path),
    $timeout_ns,
  );
  return new _Unix\CloseableSocket($sock);
}
```

**読解のコツ**: TCP 版と異なり、ホスト名解決や IP バージョンのフォールバックがないため、実装が非常にシンプルである。

#### Step 3: 内部実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | socket_connect_async.php | `src/network/_Private/socket_connect_async.php` | 非同期接続の低レベル実装（TCP と共通） |
| 3-2 | CloseableSocket.php | `src/unix/_Private/CloseableSocket.php` | Unix ソケットの具象クラス |

**CloseableSocket.php の主要処理**:
- **22-24行目**: コンストラクタで FileDescriptor を受け取る
- **26-28行目**: getLocalAddress は getsockname から sockaddr_un のパスを取得
- **30-32行目**: getPeerAddress は getpeername から sockaddr_un のパスを取得

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

```
Unix\connect_async($path, $opts)
    │
    ├─ $opts['timeout_ns'] ?? 0
    │
    ├─ OS\socket(PF_UNIX, SOCK_STREAM, 0)
    │
    ├─ OS\sockaddr_un($path)
    │
    ├─ _Network\socket_connect_async($sock, $sa, $timeout)
    │      ├─ OS\fcntl (O_NONBLOCK 設定)
    │      ├─ OS\connect
    │      └─ _OS\poll_async (接続完了待機)
    │
    └─ _Unix\CloseableSocket($sock)
```

### データフロー図

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

$path (string) ──────▶┐
                      ├──▶ socket(PF_UNIX) ──▶ ソケットFD
$opts (shape) ───────▶┘              │
                                     ▼
                              sockaddr_un($path)
                                     │
                                     ▼
                              socket_connect_async()
                                     │
                                     ▼
                              CloseableSocket ──▶ CloseableSocket
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| connect.php | `src/unix/connect.php` | ソース | connect_async 関数の実装 |
| ConnectOptions.php | `src/unix/ConnectOptions.php` | ソース | 接続オプションの型定義 |
| Socket.php | `src/unix/Socket.php` | ソース | Unix ソケットインターフェース |
| CloseableSocket.php | `src/unix/CloseableSocket.php` | ソース | 閉じることができるソケットインターフェース |
| CloseableSocket.php | `src/unix/_Private/CloseableSocket.php` | ソース | Unix ソケットの具象実装 |
| socket_connect_async.php | `src/network/_Private/socket_connect_async.php` | ソース | 非同期接続の低レベル実装（TCP と共通） |
| Socket.php | `src/network/Socket.php` | ソース | 汎用ソケットインターフェース |
| CloseableSocket.php | `src/network/CloseableSocket.php` | ソース | 汎用閉じることができるソケットインターフェース |
| HSLUnixSocketTest.php | `tests/unix/HSLUnixSocketTest.php` | テスト | Unix ソケット接続のユニットテスト |
