1

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

2 Answers 2

4
+200

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.

Sign up to request clarification or add additional context in comments.

6 Comments

I will try it tomorrow and report back to you :-) . Thank you very much!
Sorry for such a late reply, but your solution is incredible and works perfectly. Thank you so much! I will award you with 200 bounty, but they say me I will have to wait 24 hour to do so :-D .
@MZetko no worries, I don't need the rep. Just happy it worked for you.
I think your answer really deserves it. I started the bounty already anyway ;-) .
we just need use it as a handler in Httpclient object?
|
1

I stumbled on a similar problem but related to the HttpClient not redirecting from a https to a http site. This seems to be by design (See GitHub issue).

However I require the http client to follow redirects even if they were insecure so here is a solution I got to, which is very similar to Darrel Miller.

public class RedirectHandler : DelegatingHandler
{
    private static readonly HttpStatusCode[] RedirectOn = {
        HttpStatusCode.MovedPermanently,
        HttpStatusCode.Found
    };

    public RedirectHandler(HttpMessageHandler innerHandler)
    {
        InnerHandler = innerHandler;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var response = await base.SendAsync(request, cancellationToken)
            .ConfigureAwait(false);

        if (request.Method == HttpMethod.Get && RedirectOn.Contains(response.StatusCode))
        {
            request.RequestUri = response.Headers.Location;

            return await base.SendAsync(request, cancellationToken)
                .ConfigureAwait(false);
        }

        return response;
    }
}

This can be used by the following:

var client = new HttpClient(new RedirectHandler(new HttpClientHandler()))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.