web-dev-qa-db-fra.com

System.Net.Http.HttpRequestException Erreur lors de la copie du contenu dans un flux

J'utilise le HttpClient class dans le framework .Net 4.5.2. Je fais un PostAsync à un service Web tiers. 80% du temps que cet article fonctionne, 20% du temps, notre réponse est abrégée. Dans cette situation, nous obtenons l'exception suivante: 

System.Net.Http.HttpRequestException: Erreur lors de la copie du contenu vers un courant. ---> System.IO.IOException: impossible de lire les données à partir du fichier connexion de transport: une connexion existante a été fermée de force par l'hôte distant. ---> System.Net.Sockets.SocketException: un .__ existant. la connexion a été fermée de force par l'hôte distant à l'adresse System.Net.Sockets.NetworkStream.BeginRead (tampon Byte [], Int32 Offset, taille Int32, rappel AsyncCallback, état de l'objet) --- End de trace de pile d'exception interne --- at System.Net.Sockets.NetworkStream.BeginRead (tampon Byte [], Int32 Offset, taille Int32, rappel AsyncCallback, état de l'objet) à System.Net.FixedSizeReader.StartReading () à System.Net.Security._SslStream.StartFrameHeader (tampon Byte [], Int32 Offset, nombre Int32, AsyncProtocolRequest asyncRequest) à l'adresse System.Net.Security._SslStream.StartReading (tampon Byte [], Int32 Offset, compte Int32, AsyncProtocolRequest asyncRequest) à l'adresse System.Net.Security._SslStream.ProcessRead (tampon Byte [], Int32 Offset, compte Int32, AsyncProtocolRequest asyncRequest) à l'adresse System.Net.TlsStream.BeginRead (tampon Byte [], décalage Int32, taille Int32 , AsyncCallback AsyncCallback, objet asyncState) à l'adresse System.Net.ConnectStream.BeginReadWithoutValidation (tampon Byte [], Int32 offset, taille Int32, rappel AsyncCallback, état de l'objet) à l'adresse System.Net.ConnectStream.BeginRead (tampon Byte [], décalage Int32, taille Int32 , Rappel AsyncCallback, état de l'objet) à System.Net.Http.HttpClientHandler.WebExceptionWrapperStream.BeginRead (Byte [] Buffer, décalage Int32, nombre Int32, rappel AsyncCallback, Object À) à System.Net.Http.StreamToStreamCopy.Start (Lecture)

Une demande identique ultérieure aboutit . Ce n'est pas une demande que nous pouvons réessayer car l'entreprise a déjà été placée. Cela nous laisse donc dans une situation délicate.

Ceci est mon code:

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              
}

Les services tiers enregistrent avec succès l'enregistrement dans leur base de données et ne voient aucune exception évidente à la fin. Ils ont noté que les demandes échouées prenaient généralement plus de temps (environ 18-30 secondes) pour écrire dans la base de données que les demandes retenues.

Merci de votre aide

10
jonho

nous avons résolu ce problème avec 2 changements de code:

  1. Jeter le httpResponseMessage et simplement travailler avec un simple DTO

     using (var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage))
        {
            return await CreateDto(httpResponseMessage);
        }
    
  2. Rétrograder la version de HTTP vers la version 1.0 

    var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, new Uri(url))
    {
        Version = HttpVersion.Version10,
        Content = httpContent
    };
    
    await client.SendAsync(httpRequestMessage);
    

ce qui a pour effet d'ajouter cet en-tête HTTP

    Connection: close 

plutôt que cela

    Connection: keep-alive
15
jonho

J'ai eu un problème similaire avec l'utilisation d'un HttpClient partagé se connectant à un serveur pour les appels REST. Le problème a fini par être une incompatibilité entre le délai d'expiration KeepAlive sur le client et le serveur. Le délai d'expiration côté client est défini par le paramètre MaxServicePointIdleTime sur le ServicePointManager et sa valeur par défaut est 100. Le délai d'inactivité du serveur a été défini sur une valeur plus courte sur notre serveur.

Le délai d'attente sur le serveur étant plus court que celui du client, le serveur fermait sporadiquement une connexion au moment même où le client tentait de se connecter. Cela a abouti à l'exception signalée.

Notez que j'ai finalement trouvé le problème car j'ai également reçu cette exception dans les mêmes conditions:

System.Net.WebException: The underlying connection was closed: A connection that was expected to be kept alive was closed by the server.
0
Steve Wranovsky