Why after adding a header to HttpClientRequestMessage CancellationToken.IsCancellationRequested changes to true

617 views Asked by At

I use custom HttpClientHandler to authorize test if it is not authorized. Windows Store Unit Test App project type is used

using Microsoft.WindowsAzure.MobileServices;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.Security.Credentials;
using Windows.UI.Popups;
using XperiAndri.Efficiency.Common.Http.Handlers;

namespace XperiAndri.Efficiency.Common.Tests.Online
{
    public class AuthenticationHandler : DelegatingHandler
    {
        private const string user = "";
        private const string password = "";
        private const string MobileServiceAuthenticationTokenHeader = "X-ZUMO-AUTH";

        private readonly PasswordVault vault = new PasswordVault();

        public IMobileServiceClient Client { get; set; }

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

            if (response.StatusCode == HttpStatusCode.Unauthorized)
            {
                var headers = request.Headers;

                PasswordCredential credential = FindCredential();
                if (credential != null)
                {
                    Client.CurrentUser = new MobileServiceUser(user) { MobileServiceAuthenticationToken = credential.Password };
                    headers.Remove(MobileServiceAuthenticationTokenHeader);
                    credential.RetrievePassword();
                    headers.Add(MobileServiceAuthenticationTokenHeader, credential.Password);
                    response = await base.SendAsync(request, cancellationToken);
                }

                if (response.StatusCode == HttpStatusCode.Unauthorized)
                {
                    JObject content = new JObject { { "user", user }, { "password", password } };
                    var result = await Client.InvokeApiAsync("UnitTestLogin", content, HttpMethod.Post, null);
                    string token = result["token"].Value<string>();
                    if (Client.CurrentUser != null)
                        Client.CurrentUser.MobileServiceAuthenticationToken = token;
                    else
                        Client.CurrentUser = new MobileServiceUser(user) { MobileServiceAuthenticationToken = token };
                    headers.Remove(MobileServiceAuthenticationTokenHeader);
                    headers.Add(MobileServiceAuthenticationTokenHeader, token); // After execution of this line cancellationToken.IsCancellationRequested changes to true
                    // try again!
                    response = await base.SendAsync(request, cancellationToken);
                    if (response.StatusCode != HttpStatusCode.Unauthorized)
                    {
                        if (credential != null)
                            credential.Password = token;
                        else
                        {
                            credential = new PasswordCredential(Client.ApplicationUri.ToString(), user, token);
                            vault.Add(credential);
                        }
                    }
                }
            }

            return response;
        }

        private PasswordCredential FindCredential()
        {
            try
            {
                return vault.FindAllByResource(Client.ApplicationUri.ToString()).FirstOrDefault();
            }
            catch
            {
                return null;
            }
        }

    }
}

Look at the comment at the line

headers.Add(MobileServiceAuthenticationTokenHeader, token); 

the second time it is executed.

Can somebody explain why does it happened? Why request becomes cancelled?

1

There are 1 answers

2
RReverser On

You're trying to add header to request after actually receiving response (since you already awaited SendAsync) which logically is not possible and causes cancellation of original request.