HttpClient cannot send second request after retry

1

Initially I want to send a first request without client certificates and if the error is "an authentication error" then I want to resend the request with the certificates attached. So the code first is

HttpStringContent ehttpContent = new HttpStringContent(content);
HttpRequestMessage req2 = new HttpRequestMessage(method, resourceUri);
req2.Headers.Add("SOAPAction", soapAction);
req2.Content = ehttpContent;
req2.Content.Headers.ContentType = HttpMediaTypeHeaderValue.Parse("text/xml; charset=utf-8");

bool retry= await FirstLoginAttempt(...);

if( retry) {

 newHttpClient = new HttpClient(baseFilter2);

 try
 {
     cts.CancelAfter(Constants.HttpTimeOut);
     System.Diagnostics.Debug.WriteLine(ehttpContent);
     eresponse = await newHttpClient.SendRequestAsync(req2).AsTask(cts.Token);

     if (cts != null)
     {
        cts.Dispose();
        cts = null;
 } catch { ... }


}

the previous code calls FirstLoginAttempt that it might fail with a certificate error. It returns true or false and then the previous code tries to send a new httpClient with certificates.

public static async Task<bool> FirstLoginAttempt() {

httpClient = new HttpClient();

try
{
    cts.CancelAfter(Constants.HttpTimeOut);
    var operation = httpClient.SendRequestAsync(ereq).AsTask(cts.Token);
    eresponse = await operation;
    if (cts != null) { cts.Dispose(); cts = null; }

}
catch (TaskCanceledException e)
{
  if (cts != null) { cts.Dispose(); cts = null; }
   throw e;
}
catch (Exception exx)
{ ... }
}

When the first login attempt returns true in order to try again the second httpclient (that has the same contect) fails and the exception says that I cannot send the same request twice. that probably means that the previous operation is not finished? How can I resolve my problem? Can I somehow get sure that the first request lifecycle is over in order to retry?

My environment is Windows Phone 8.1

Update #1. After using lambda

StackTrace = " at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n ... Message = "Exception from HRESULT: 0x80072EFD" (Throw for non success)

above the stack trace ... ...

 Func<HttpRequestMessage> httpRequestCreator = () =>
                {
     HttpStringContent ehttpContent = new HttpStringContent(content);

     var req = new HttpRequestMessage(method, resourceUri)
     {
         Content = ehttpContent
     };
     req.Headers.Add("SOAPAction", soapAction);
     req.Content.Headers.ContentType = HttpMediaTypeHeaderValue.Parse("text/xml; charset=utf-8");
     return req;
};

if (cts != null) { cts.Dispose(); cts = null; }

cts = new System.Threading.CancellationTokenSource();
newHttpClient = new HttpClient();

try
{
    cts.CancelAfter(Constants.HttpTimeOut);
    eresponse = await newHttpClient.SendRequestAsync(httpRequestCreator()).AsTask(cts.Token);

    if (cts != null) { cts.Dispose(); cts = null; }

}
catch (TaskCanceledException e
{
    if (cts != null) { cts.Dispose(); cts = null; }
    throw e;
}
catch (Exception exx)
{
    exception2 = exx;
    ...
    ...
}
c#
.net
windows-phone-8.1
dotnet-httpclient
asynchttpclient
asked on Stack Overflow Sep 19, 2015 by cateof • edited Sep 22, 2015 by cateof

1 Answer

1

The problem is in the fact you're re-using your HttpRequestMessage for both requests, which you can't do. Instead, create a new request for each message you want to send. One way to deal with this is create a Func<HttpRequestMessage> which returns a fresh instance each time:

Func<HttpRequestMessage> httpRequestCreator = () => 
{
     var request = new HttpRequestMessage(method, resourceUri)
     {
          Content = content
     };
     request.Headers.Add("SOAPAction", soapAction);
     request.Content.Headers.ContentType =
                            HttpMediaTypeHeaderValue.Parse("text/xml; charset=utf-8"); 
     return request;
};

bool retry = await FirstLoginAttempt(httpRequestCreator());
// If re-try is needed:
var operation = await httpClient.SendRequestAsync(httpRequestCreator()).AsTask(cts.Token);

As a side note - Don't relay on exception handling for normal code execution paths. Instead, consider sending the authentication information even if it might not be needed, or rather request that the service you're querying provide information at run-time regarding if you need to pass that information along.

answered on Stack Overflow Sep 19, 2015 by Yuval Itzchakov • edited Sep 22, 2015 by Yuval Itzchakov

User contributions licensed under CC BY-SA 3.0