ストップウォッチは待機を止めても、HTTP リクエストは続行している可能性があります。プロキシ経由の試行に予定された予算がある場合は、操作そのものにキャンセルを渡します。

既存の プロキシ設定済みの HttpClient を持つ現代的な .NET アプリケーションの場合:

using var budget = new CancellationTokenSource(TimeSpan.FromSeconds(5));
using var response = await client.GetAsync(
    targetUrl,
    HttpCompletionOption.ResponseHeadersRead,
    budget.Token);
response.EnsureSuccessStatusCode();
string body = await response.Content.ReadAsStringAsync(budget.Token);

同じ予算は準備の前に始まり、本文の読み取り中も有効なままです。ResponseHeadersRead はヘッダーの後に制御を返すため、HttpClient 自身の完了タイムアウトだけでは後続の本文読み取り予算を定義しません。

キャンセルは HTTP ステータスや転送エラーとは区別してください。OperationCanceledException は指定したトークンからも、別に構成されたタイムアウトからも発生し得ます。すべての失敗を死んだプロキシとして報告するのではなく、該当するキャンセル元を確認します。

このプロキシ経路を所有するクライアントを再利用します。試行のキャンセルは、以降のリクエストごとに接続プール全体を作り直すことを必ずしも意味しません。

この例は有限の応答本文をバッファーします。ストリーミング ダウンロードでは、トークンをストリーム コピー ループに通し、すべての終了経路で応答を解放する必要があります。

キャンセルは操作ポリシーを表すものであり、基盤となるすべてのプラットフォーム操作に対する正確な実時間の約束ではありません。Task.IsCompleted を巡る無限のポーリング ループで置き換えることは避けてください。

関連記事