System.Net.HttpWebRequest выдает различные webExceptionStatus для SSL-запросов и запросов, отличных от SSL, при особых условиях

Эта статья поможет вам устранить проблему, из-за которой при использовании класса возникает другое WebExceptionStatusSystem.Net.HttpWebRequest .

Исходная версия продукта: .NET Framework
Исходный номер базы знаний: 2007873

Симптомы

Класс Microsoft .Net Framework используется System.Net.HttpWebRequest для отправки http- или HTTPS-запроса на сервер. Этот запрос занимает некоторое время, чтобы получить ответ от сервера. В течение этого времени ожидания, если время системных часов увеличивается вручную или системные часы отстают, а затем служба времени Windows адаптируется к фактическому местному времени, вы можете столкнуться с одним из следующих сценариев:

Для запроса, который был отправлен по протоколу HTTP в виде открытого System.Net.HttpWebRequest текста, класс вызовет следующее исключение:

Запрос прерван: истекло время ожидания операции.

Кроме того, Status свойство в вызываемом WebException объекте будет указывать значение WebExceptionStatus.Timeout.

Для запроса, который был отправлен по протоколу System.Net.HttpWebRequest HTTPS, класс вызовет одно из следующих исключений:

Базовое подключение было закрыто: произошла непредвиденная ошибка при получении.

Кроме того, Status свойство в вызываемом WebException объекте будет указывать значение WebExceptionStatus.ReceiveFailure.

Или

Базовое подключение было закрыто. Сервер закрыл подключение, которое должно было оставаться активным.

Кроме того, Status свойство в вызываемом WebException объекте будет указывать значение WebExceptionStatus.KeepAliveFailure.

Во всех описанных выше сценариях объект , который перехватил, имеет InnerException свойство . При перехвате WebException и ссылке на WebException.InnerException.InnerException свойство вы заметите, что во всех приведенных выше случаях Message строка будет указывать:

Произошел сбой при попытке подключения, поскольку истекло время ожидания ответа от подключаемой стороны, либо произошел сбой уже установленного подключения, поскольку подключенный узел не ответил.

Это сообщение является подробной интерпретацией кода ошибки Winsock 10060 = WSAETIMEDOUT.

Поэтому при увеличении системного времени вручную Winsock правильно выдает ошибку времени ожидания 10060, но она упаковывается в виде различных типов исключений для ssl-запросов и запросов, отличных от SSL.

При обычных обстоятельствах ожидания, когда системное время не подделывается, сценарии SSL и не ssl правильно отражают WebExceptionStatus.Timeout состояние и выдают распространенное исключение: истекло время ожидания операции.

Причина

При выполнении запроса по протоколу SSL или по протоколу System.Net.ServicePointManager , отличному от SSL, класс назначит запрос внутреннему подключению, которое в конечном итоге создаст подключение Winsock. В случае SSL-запросов этот запрос или подключение проходит через другой внутренний класс SSL/TLS, который отвечает за шифрование или расшифровку данных. Для подключений, отличных от SSL, этот внутренний класс SSL/TLS не используется вообще.

При изменении времени и обнаружении исключения на уровне Winsock эта ошибка теперь должна перемещаться вверх от Winsock до уровня приложения. Для подключений, отличных от SSL, это исключение перехватывалось непосредственно внутренним классом подключения, но для SSL-запросов эта ошибка обрабатывается внутренним классом SSL/TLS. Этот класс рассматривает эту ошибку, не связанную ReceiveFailure с SSL, как или KeepAliveFailure и, следовательно, имеет другое состояние исключения, в то время как при подключении, отличном от SSL, ошибка приводится правильно, так как она обрабатывается другим классом.

Статус

Такое поведение является особенностью данного продукта.

Решение

Чтобы устранить это несоответствие типов создаваемых исключений при этом специальном условии, когда системное время подделывается, приложению необходимо перехватывать WebException и ссылаться на WebException.InnerException.InnerException.Message свойство .

Message Если строка равна подробной ошибке winsock, эквивалентной 10060 = WSAETIMEDOUT, можно рассматривать ReceiveFailure или KeepAliveFailure как обычный тайм-аут и не рассматривать его как ReceiveFailure или KeepAliveFailure.

Приложение может использовать приведенный ниже обходной путь при выполнении catch()WebException для английской версии платформы. Для локализованной версии платформы необходимо изменить приведенное ниже решение в зависимости от локализации языка.

Важно!

Этот пример кода предоставляется как есть и предназначен только для демонстрационных целей. Он предоставляется без гарантий и не предоставляет никаких прав.

try
{
    ......
}
catch (WebException oWEx)
{
    WebExceptionStatus oStatus = oWEx.Status;
    String strTimeoutErrorMessage = "A connection attempt failed because the connected party did not properly respond "
                                  + "after a period of time, or established connection failed because connected host has failed to respond";
    switch (oStatus)
    {
        case WebExceptionStatus.KeepAliveFailure:
            if ((oWEx.InnerException != null) && (oWEx.InnerException.InnerException != null)
                && oWEx.InnerException.InnerException.Message.ToString().Equals(strTimeoutErrorMessage, StringComparison.CurrentCultureIgnoreCase))
            {   //----------------------------------------------------------------------
                // This is Timeout Error which is wrongly thrown as a ReceiveFailure for
                // SSL requests under this special condition.
                //
                // Handle this as a Timeout Error
                //----------------------------------------------------------------------
            }
            else
            {
                //----------------------------------------------------------------------
                // This is truly a KeepAliveFailure.
                //----------------------------------------------------------------------
            }
            break;
        case WebExceptionStatus.Timeout:
             //----------------------------------------------------------------------
             // This is a Timeout.
             //----------------------------------------------------------------------
             break;
        case WebExceptionStatus.ReceiveFailure:
            if ((oWEx.InnerException != null)
                && (oWEx.InnerException.InnerException != null)
                && oWEx.InnerException.InnerException.Message.ToString ().Equals (strTimeoutErrorMessage, StringComparison.CurrentCultureIgnoreCase))
            {    //----------------------------------------------------------------------
                 // This is Timeout Error which is wrongly thrown as a ReceiveFailure for
                 // SSL requests under this special condition.
                 //
                 // Handle this as a Timeout Error
                 //----------------------------------------------------------------------
            }
            else
            {    //----------------------------------------------------------------------
                 // This is truly a ReceiveFailure.
                 //----------------------------------------------------------------------
            }
            break;
        default:
               //----------------------------------------------------------------------
               //  This is some other Exception
               //----------------------------------------------------------------------
            break;
    }
}