System.Net.Http.HttpRequestException Ошибка при копировании содержимого в поток
Я использую класс HttpClient в.Net 4.5.2 framework. Я делаю PostAsync со сторонним веб-сервисом. 80% времени, когда этот пост работает, в 20% случаев наш ответ прерывается. В этой ситуации мы получаем следующее исключение:
System.Net.Http.HttpRequestException: ошибка при копировании содержимого в поток. ---> System.IO.IOException: невозможно прочитать данные из транспортного соединения: существующее соединение было принудительно закрыто удаленным хостом. ---> System.Net.Sockets.SocketException: существующее соединение было принудительно закрыто удаленным хостом в System.Net.Sockets.NetworkStream.BeginRead(буфер Byte[], смещение Int32, размер Int32, обратный вызов AsyncCallback, состояние объекта) --- Конец внутренней трассировки стека исключений --- в System.Net.Sockets.NetworkStream.BeginRead(буфер Byte[], смещение Int32, размер Int32, обратный вызов AsyncCallback, состояние объекта) в System.Net.FixedSizeReader.StartReading() в System.Net.Security._SslStream.StartFrameHeader(буфер Byte[], смещение Int32, подсчет Int32, AsyncProtocolRequest asyncRequest) в буфере System.Net.Security._SslStream.StartReading(смещение Byte[], смещение Int32, подсчет Int32, запрос AsyncynRequest) в System.Net.Security._SslStream.ProcessRead(буфер Byte[], смещение Int32, число Int32, AsyncProtocolRequest asyncRequest) в System.Net.TlsStream.BeginRead(буфер Byte[], смещение Int32, размер Int32, AsyncCallback asyncCallback, асинхронный вызов объекта AsyncCallback, асинхронный вызов объекта AsyncCallback)) в System.Net.ConnectStream.BeginReadWithoutValidation(Byte[] буфер, смещение Int32, размер Int32, обратный вызов AsyncCallback, состояние объекта) в System.Net.ConnectStream.BeginRead(буфер Byte[], смещение Int32, размер Int32, обратный вызов AsyncCallback, состояние объекта) в System.Net.Http.HttpClientHandler.WebExceptionWrapperStream.BeginRead(буфер Byte[], смещение Int32, число Int32, обратный вызов AsyncCallback, состояние объекта) в System.Net.Http.StreamToStreamCopy.StartRead()
Последующий идентичный запрос выполняется успешно. Мы не можем повторить запрос, так как бизнес уже размещен. Так что это оставляет нас в неловкой ситуации.
Это мой код:
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = authorizationHeader;
HttpContent httpContent = new StringContent(someXml);
//Exception occurs on next line...
var response = await httpClient.PostAsync("https://thirdpartyendpoint", httpContent);
var responseXml = await response.Content.ReadAsStringAsync();
//convert to Dto
}
Сторонние службы успешно сохраняют записи в своей базе данных и не видят никаких очевидных исключений в их конце. Они отметили, что ошибочные запросы обычно занимали больше времени (около 18-30 секунд) для записи в базу данных, чем успешные запросы.
Спасибо за помощь
1 ответ
Мы решили эту проблему с двумя изменениями кода:
Избавьтесь от httpResponseMessage и просто работайте с простым DTO
using (var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage)) { return await CreateDto(httpResponseMessage); }
Понизьте версию HTTP до версии 1.0
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, new Uri(url)) { Version = HttpVersion.Version10, Content = httpContent }; await client.SendAsync(httpRequestMessage);
который имеет эффект добавления этого заголовка Http
Connection: close
а не это
Connection: keep-alive
У меня была похожая проблема с использованием общего HttpClient, подключающегося к серверу для вызовов REST. В результате возникла проблема с несоответствием тайм-аута KeepAlive на клиенте и сервере. Тайм- аут на стороне клиента задается параметром MaxServicePointIdleTime в ServicePointManager и по умолчанию равен 100 с. Время ожидания на стороне сервера было установлено на нашем сервере более коротким.
Сокращение времени ожидания на сервере по сравнению с клиентом привело к тому, что сервер время от времени закрывал соединение, когда клиент пытался подключиться. Это привело к сообщенному исключению.
Обратите внимание, что я в конечном итоге обнаружил проблему, потому что я также получил это исключение при тех же условиях:
System.Net.WebException: The underlying connection was closed: A connection that was expected to be kept alive was closed by the server.
У меня была такая же ошибка (ошибка при копировании контента в поток) с методом HTTPClient PutAsync():
using (StreamContent content = new StreamContent(stream))
{
HttpResponseMessage response = await client.PutAsync(url, content))
}
Вам нужно указать флаг HttpCompletionOption.ResponseHeadersRead, который недоступен в PutAsync, поэтому я переключился на SendAsync:
using (StreamContent content = new StreamContent(stream))
{
var httpRequest = new HttpRequestMessage(HttpMethod.Put, url);
httpRequest.Content = content;
HttpResponseMessage response = await client.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead);
}