Search code examples
c#httpwindows-phone-8dotnet-httpclienthttp-status-code-302

Handling HTTP 302 with HttpClient on WP8


I am facing a really complicated problem with the app I am developing for Windows Phone 8. The following endpoint - http://speedmeter3.internetprovsechny.cz:81/ping/ - can be used to measure the latency of connected client. The result is stored as JSON string and is returned. However, to perform the measurement, there are several HTTP 302 (Found) redirects used (always returning to the same URL). The result is correctly returned in browsers and I have seen numerous ways to solve similar problem on Android, but have found none that works on Windows Phone. Here is my simple code snippet that should return the string:

HttpClientHandler handler = new HttpClientHandler();
handler.AllowAutoRedirect = true;     
HttpClient client = new HttpClient( handler );
string url = "http://speedmeter3.internetprovsechny.cz:81/ping/";
return await client.GetStringAsync( new Uri( url, UriKind.Absolute ) );

This code hangs for a while on the GetStringAsync method and eventually terminates with exception:

System.Net.Http.HttpRequestException: Response status code does not indicate success: 302 (Found).
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at IPVSpeedmeter.Helpers.RestClient.<SendGetSafe>d__8.MoveNext()}    System.Exception {System.Net.Http.HttpRequestException}

Anyone who is able to provide a solution for this will be awarded a bounty when it will be possible (after three days ;-) ).

Thank you all


Solution

  • I'm guessing that maybe the Win8 implementation doesn't impement autoredirects. Here is a redirect handler I use:

        public class GlobalRedirectHandler : DelegatingHandler {
    
        public GlobalRedirectHandler(HttpMessageHandler innerHandler) {
           InnerHandler = innerHandler;
        } 
    
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
            var tcs = new TaskCompletionSource<HttpResponseMessage>();
    
            base.SendAsync(request, cancellationToken)
                .ContinueWith(t => {
                    HttpResponseMessage response;
                    try {
                        response = t.Result;
                    }
                    catch (Exception e) {
                        response = new HttpResponseMessage(HttpStatusCode.ServiceUnavailable);
                        response.ReasonPhrase = e.Message;
                    }
                    if (response.StatusCode == HttpStatusCode.MovedPermanently
                        || response.StatusCode == HttpStatusCode.Moved
                        || response.StatusCode == HttpStatusCode.Redirect
                        || response.StatusCode == HttpStatusCode.Found
                        || response.StatusCode == HttpStatusCode.SeeOther
                        || response.StatusCode == HttpStatusCode.RedirectKeepVerb
                        || response.StatusCode == HttpStatusCode.TemporaryRedirect
    
                        || (int)response.StatusCode == 308) 
                    {
    
                        var newRequest = CopyRequest(response.RequestMessage);
    
                        if (response.StatusCode == HttpStatusCode.Redirect 
                            || response.StatusCode == HttpStatusCode.Found
                            || response.StatusCode == HttpStatusCode.SeeOther)
                        {
                            newRequest.Content = null;
                            newRequest.Method = HttpMethod.Get;
    
                        }
                        newRequest.RequestUri = response.Headers.Location;
    
                        base.SendAsync(newRequest, cancellationToken)
                            .ContinueWith(t2 => tcs.SetResult(t2.Result));
                    }
                    else {
                        tcs.SetResult(response);
                    }
                });
    
            return tcs.Task;
        }
    
        private static HttpRequestMessage CopyRequest(HttpRequestMessage oldRequest) {
            var newrequest = new HttpRequestMessage(oldRequest.Method, oldRequest.RequestUri);
    
            foreach (var header in oldRequest.Headers) {
                newrequest.Headers.TryAddWithoutValidation(header.Key, header.Value);
            }
            foreach (var property in oldRequest.Properties) {
                newrequest.Properties.Add(property);
            }
            if (oldRequest.Content != null) newrequest.Content = new StreamContent(oldRequest.Content.ReadAsStreamAsync().Result);
            return newrequest;
        }
    }
    

    I really should clean it up to use the async/await syntax though.