Таймаут Invoke-WebRequest и WebClient в PowerShell
У System.Net.WebClient нет свойства Timeout. Варианта три: параметр -TimeoutSec у Invoke-WebRequest и Invoke-RestMethod, свойство Timeout у HttpClient или свой наследник WebClient.
# Веб-командлеты: секунды, 0 (по умолчанию) = без таймаута
Invoke-WebRequest -Uri 'https://example.com/' -UseBasicParsing -TimeoutSec 30
Invoke-RestMethod -Uri 'https://example.com/' -TimeoutSec 30
# HttpClient: Timeout меняют только до первого запроса
Add-Type -AssemblyName System.Net.Http
$http = [System.Net.Http.HttpClient]::new()
$http.Timeout = [TimeSpan]::FromSeconds(30)
$html = $http.GetStringAsync('https://example.com/').GetAwaiter().GetResult()
Таймаут WebClient: наследник через Add-Type
Переопределяем GetWebRequest и ставим таймаут каждому запросу:
Add-Type -IgnoreWarnings -TypeDefinition @"
using System;
using System.Net;
public class TimeoutWebClient : WebClient
{
public int TimeoutMs = 30000;
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
if (request != null)
{
request.Timeout = TimeoutMs;
HttpWebRequest http = request as HttpWebRequest;
if (http != null) { http.ReadWriteTimeout = TimeoutMs; }
}
return request;
}
}
"@
$wc = New-Object TimeoutWebClient
$wc.TimeoutMs = 10000 # миллисекунды
$wc.DownloadString('https://example.com/')
Почему код такой:
Timeout— сколько ждать ответа (по умолчанию 100 секунд),ReadWriteTimeout— сколько ждать каждой порции тела (по умолчанию 5 минут). В моём тесте сервер отдал заголовки и замолчал на 8 секунд:ReadWriteTimeoutна 3 секунды сработал,Timeout— нет.- В PowerShell 7 нужен ключ
-IgnoreWarnings.WebClientустарел в .NET 6 (SYSLIB0014), аAdd-Typeсчитает предупреждения ошибками. Без ключа получитеCannot add type. Compilation errors occurred. - Код на C# обходится без нового синтаксиса, чтобы тот же блок собрался и в Windows PowerShell 5.1 со старым компилятором CodeDom. Тип в сессии не переопределить: поменяли класс — переименуйте его или перезапустите PowerShell.
Invoke-WebRequest -TimeoutSec: что он ограничивает
-TimeoutSec действует, только пока не пришли заголовки ответа: сюда входят DNS, подключение и время, пока сервер думает. На скачивание тела он не влияет. С -TimeoutSec 3 ответ, который сразу отдал заголовки и потом шёл 8 секунд, скачался без ошибок.
В PowerShell 7.4 параметр переименовали в -ConnectionTimeoutSeconds (-TimeoutSec остался псевдонимом) и добавили -OperationTimeoutSeconds — максимальную паузу между порциями данных. Когда качаете файлы, ставьте оба:
Invoke-WebRequest -Uri $url -OutFile $file -ConnectionTimeoutSeconds 15 -OperationTimeoutSeconds 60
Общее время не ограничивает ни один из них. Жёсткий лимит даёт HttpClient.Timeout: он действует на весь вызов GetStringAsync вместе с телом.
Ещё две детали:
-MaximumRetryCountне повторяет запрос после таймаута: с-TimeoutSec 2 -MaximumRetryCount 2вызов упал через 2 секунды.- В Windows PowerShell 5.1 есть только
-TimeoutSec, и медленный DNS-запрос может оттянуть таймаут до 15 секунд и дольше (документация Microsoft). В PowerShell 7 такого нет. Добавляйте в 5.1-UseBasicParsing: после обновления безопасности от декабря 2025 годаInvoke-WebRequestбез него останавливается и спрашивает подтверждение.
«The operation has timed out» и DownloadDataInternal
Так выглядит таймаут в Windows PowerShell 5.1 или .NET Framework:
Exception calling "DownloadString" with "1" argument(s): "The operation has timed out"
System.Net.WebException: The operation has timed out
at System.Net.WebClient.DownloadDataInternal(Uri address, WebRequest& request)
at System.Net.WebClient.DownloadString(Uri address)
PowerShell 7.5 ставит в конце сообщения точку и добавляет сверху ещё три строки: HttpWebRequest.GetResponse(), GetWebResponse и DownloadBits.
В .NET Framework DownloadDataInternal открывает трассировку любой ошибки DownloadString и DownloadData (throw e; сбрасывает стек), так что смотрите на текст сообщения:
- «The operation has timed out»: сервер не ответил за
HttpWebRequest.Timeout(по умолчанию 100 секунд). Поменяйте значение через наследника выше или перейдите на командлеты. Для ASP.NET за корпоративным прокси проверьте настройки прокси в web.config. - «(407) Proxy Authentication Required»: задайте
$wc.Proxy.Credentials = [Net.CredentialCache]::DefaultNetworkCredentialsилиuseDefaultCredentials="true"у<defaultProxy>в web.config. - «Could not create SSL/TLS secure channel»: перед запросом выполните
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor 'Tls12'.
Таймаут ловите по типу исключения и смотрите на Status:
try {
$wc.DownloadString($url)
}
catch [System.Net.WebException] {
if ($_.Exception.Status -eq [System.Net.WebExceptionStatus]::Timeout) {
Write-Warning "Таймаут: $url"
}
else { throw }
}
Если зависло тело ответа (сработал ReadWriteTimeout), PowerShell 7 пишет другое: «An exception occurred during a WebClient request.», Status равен UnknownError, внутри лежит IOException. Проверка Status такой случай пропустит, поэтому допишите в условие -or $_.Exception.InnerException -is [System.IO.IOException].
Invoke-WebRequest в PowerShell 7 по таймауту подключения бросает TaskCanceledException («The request was canceled due to the configured HttpClient.Timeout of 30 seconds elapsing.»), а по таймауту операции — TimeoutException (с -OutFile он приходит внутри AggregateException):
try {
Invoke-RestMethod -Uri $url -TimeoutSec 30
}
catch [System.Threading.Tasks.TaskCanceledException] {
Write-Warning "Нет ответа за 30 секунд: $url"
}
Windows PowerShell 5.1 здесь бросает WebException (документация Microsoft), поэтому там подойдёт вариант для WebClient.
Асинхронные методы WebClient игнорируют Timeout в Windows PowerShell 5.1
В .NET Framework HttpWebRequest.Timeout не действует на асинхронные запросы, а DownloadStringAsync, DownloadFileAsync и их версии TaskAsync работают через BeginGetResponse. В PowerShell 7 DownloadStringTaskAsync в моём тесте оборвался по таймауту через 3 секунды. В 5.1 берите синхронные методы или HttpClient.
Таймаут WebClient в C#
Для .NET 6+ Microsoft советует HttpClient:
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
try
{
Console.WriteLine(await http.GetStringAsync(url));
}
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
{
Console.WriteLine("Таймаут");
}
Если старый код завязан на WebClient:
class TimeoutWebClient : WebClient
{
public int TimeoutMs { get; set; } = 30_000;
protected override WebRequest GetWebRequest(Uri address)
{
var request = base.GetWebRequest(address);
request.Timeout = TimeoutMs;
if (request is HttpWebRequest http) http.ReadWriteTimeout = TimeoutMs;
return request;
}
}
В .NET 6+ добавьте #pragma warning disable SYSLIB0014.