public Task<HttpResponseMessage> SendAsync(HttpRequestMessage message, HttpCompletionOption option, CancellationToken token)
        {
            #if NET_3_5
            if(_connectionCount >= _connectionLimit) {
                return Task.Delay(500).ContinueWith(t => SendAsync(message, option, token)).Unwrap();
            }

            Interlocked.Increment(ref _connectionCount);
            #else
            return _sendSemaphore.WaitAsync().ContinueWith(t =>
            {
            #endif
                var challengeResponseAuth = Authenticator as IChallengeResponseAuthenticator;
                if (challengeResponseAuth != null) {
                    if (_authHandler != null) {
                        _authHandler.Authenticator = challengeResponseAuth;
                    }

                    challengeResponseAuth.PrepareWithRequest(message);
                }

                var authHeader = AuthUtils.GetAuthenticationHeaderValue(Authenticator, message.RequestUri);
                if (authHeader != null) {
                    _httpClient.DefaultRequestHeaders.Authorization = authHeader;
                }

                return _httpClient.SendAsync(message, option, token);
            #if !NET_3_5
            }).Unwrap().ContinueWith(t =>
            {
                _sendSemaphore.Release();
                return t.Result;
            });
            #endif
        }
        public void TestGetAuthenticationHeaderValue()
        {
            var username1  = "username1";
            var password1  = "password1";
            var credParam1 = Convert.ToBase64String(
                Encoding.UTF8.GetBytes(string.Format("{0}:{1}", username1, password1)));

            var auth       = AuthenticatorFactory.CreateBasicAuthenticator(username1, password1);
            var authHeader = AuthUtils.GetAuthenticationHeaderValue(auth, null);

            Assert.IsNotNull(authHeader);
            Assert.AreEqual(credParam1, authHeader.Parameter);

            var username2  = "username2";
            var password2  = "password2";
            var credParam2 = Convert.ToBase64String(
                Encoding.UTF8.GetBytes(string.Format("{0}:{1}", username2, password2)));

            var userinfo = username2 + ":" + password2;
            var uri      = new Uri("http://" + userinfo + "@couchbase.com");
            var request  = new HttpRequestMessage(HttpMethod.Get, uri);

            authHeader = AuthUtils.GetAuthenticationHeaderValue(auth, request.RequestUri);
            Assert.IsNotNull(authHeader);
            Assert.AreEqual(credParam2, authHeader.Parameter);

            uri        = new Uri("http://www.couchbase.com");
            request    = new HttpRequestMessage(HttpMethod.Get, uri);
            authHeader = AuthUtils.GetAuthenticationHeaderValue(null, request.RequestUri);
            Assert.IsNull(authHeader);

            auth       = AuthenticatorFactory.CreateFacebookAuthenticator("1234");
            authHeader = AuthUtils.GetAuthenticationHeaderValue(auth, null);
            Assert.IsNull(authHeader);
        }
        public virtual void Run()
        {
            Log.V(Tag, "{0}: RemoteRequest run() called, url: {1}".Fmt(this, url));

            HttpClient httpClient = null;

            try
            {
                httpClient = clientFactory.GetHttpClient();

                //var manager = httpClient.GetConnectionManager();
                var authHeader = AuthUtils.GetAuthenticationHeaderValue(Authenticator, requestMessage.RequestUri);
                if (authHeader != null)
                {
                    httpClient.DefaultRequestHeaders.Authorization = authHeader;
                }

                requestMessage.Headers.Add("Accept", "multipart/related, application/json");
                AddRequestHeaders(requestMessage);

                SetBody(requestMessage);

                ExecuteRequest(httpClient, requestMessage);

                Log.V(Tag, "{0}: RemoteRequest run() finished, url: {1}".Fmt(this, url));
            }
            finally
            {
                if (httpClient != null)
                {
                    httpClient.Dispose();
                }
            }
        }
Exemple #4
0
        public override void Run()
        {
            HttpClient httpClient = null;

            try
            {
                httpClient = clientFactory.GetHttpClient();
                requestMessage.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("multipart/related"));

                var authHeader = AuthUtils.GetAuthenticationHeaderValue(Authenticator, requestMessage.RequestUri);
                if (authHeader != null)
                {
                    httpClient.DefaultRequestHeaders.Authorization = authHeader;
                }

                //TODO: implement gzip support for server response see issue #172
                //request.addHeader("X-Accept-Part-Encoding", "gzip");
                AddRequestHeaders(requestMessage);
                SetBody(requestMessage);
                ExecuteRequest(httpClient, requestMessage);
            }
            finally
            {
                if (httpClient != null)
                {
                    httpClient.Dispose();
                }
            }
        }
Exemple #5
0
        public override bool Start()
        {
            if (IsRunning)
            {
                return(false);
            }

            IsRunning = true;
            Log.To.ChangeTracker.I(Tag, "Starting {0}...", this);
            _cts = new CancellationTokenSource();

            var authHeader = AuthUtils.GetAuthenticationHeaderValue(Authenticator, ChangesFeedUrl);

            // A WebSocket has to be opened with a GET request, not a POST (as defined in the RFC.)
            // Instead of putting the options in the POST body as with HTTP, we will send them in an
            // initial WebSocket message
            _usePost           = false;
            _caughtUp          = false;
            _client            = new WebSocket(ChangesFeedUrl.AbsoluteUri);
            _client.WaitTime   = TimeSpan.FromSeconds(2);
            _client.OnOpen    += OnConnect;
            _client.OnMessage += OnReceive;
            _client.OnError   += OnError;
            _client.OnClose   += OnClose;
            if (authHeader != null)
            {
                _client.CustomHeaders = new Dictionary <string, string> {
                    ["Authorization"] = authHeader.ToString()
                };
            }
            _client.ConnectAsync();

            return(true);
        }
        public Task<HttpResponseMessage> SendAsync(HttpRequestMessage message, HttpCompletionOption option, CancellationToken token)
        {
            #if NET_3_5
            if(_connectionCount >= _connectionLimit) {
                return Task.Delay(500).ContinueWith(t => SendAsync(message, option, token)).Unwrap();
            }

            Interlocked.Increment(ref _connectionCount);
            #else
            return _sendSemaphore?.WaitAsync()?.ContinueWith(t =>
            {
            #endif
                var challengeResponseAuth = Authenticator as IChallengeResponseAuthenticator;
                if (challengeResponseAuth != null) {
                    _authHandler.Borrow(x => x.Authenticator = challengeResponseAuth);
                    challengeResponseAuth.PrepareWithRequest(message);
                }

                var httpClient = default(HttpClient);
                if(!_httpClient.AcquireTemp(out httpClient)) {
                    return null;
                }

                var authHeader = AuthUtils.GetAuthenticationHeaderValue(Authenticator, message.RequestUri);
                if (authHeader != null) {
                    httpClient.DefaultRequestHeaders.Authorization = authHeader;
                }

                return httpClient.SendAsync(message, option, token);
            #if !NET_3_5
            })?.Unwrap()?.ContinueWith(t =>
            {
                _sendSemaphore?.Release();
                if(t.IsFaulted) {
                    var e = t.Exception;
                    if(Misc.UnwrapAggregate(e) is ObjectDisposedException) {
                        return null;
                    }

                    throw e;
                }

                return t.Result;
            });
            #endif
        }
Exemple #7
0
        public virtual void Run()
        {
            Log.V(Tag, "{0}: RemoteRequest run() called, url: {1}".Fmt(this, url));

            HttpClient httpClient     = null;
            var        requestMessage = CreateConcreteRequest();

            httpClient = clientFactory.GetHttpClient(false);
            var challengeResponseAuth = Authenticator as IChallengeResponseAuthenticator;

            if (challengeResponseAuth != null)
            {
                var authHandler = clientFactory.Handler as DefaultAuthHandler;
                if (authHandler != null)
                {
                    authHandler.Authenticator = challengeResponseAuth;
                }

                challengeResponseAuth.PrepareWithRequest(requestMessage);
            }

            var authHeader = AuthUtils.GetAuthenticationHeaderValue(Authenticator, requestMessage.RequestUri);

            if (authHeader != null)
            {
                httpClient.DefaultRequestHeaders.Authorization = authHeader;
            }

            requestMessage.Headers.Add("Accept", "multipart/related, application/json");
            AddRequestHeaders(requestMessage);

            SetBody(requestMessage);

            ExecuteRequest(httpClient, requestMessage).ContinueWith(t =>
            {
                Log.V(Tag, "{0}: RemoteRequest run() finished, url: {1}".Fmt(this, url));
                if (httpClient != null)
                {
                    httpClient.Dispose();
                }

                requestMessage.Dispose();
            });
        }
Exemple #8
0
        public Task <HttpResponseMessage> SendAsync(HttpRequestMessage message, HttpCompletionOption option, CancellationToken token)
        {
            var challengeResponseAuth = Authenticator as IChallengeResponseAuthenticator;

            if (challengeResponseAuth != null)
            {
                if (_authHandler != null)
                {
                    _authHandler.Authenticator = challengeResponseAuth;
                }

                challengeResponseAuth.PrepareWithRequest(message);
            }

            var authHeader = AuthUtils.GetAuthenticationHeaderValue(Authenticator, message.RequestUri);

            if (authHeader != null)
            {
                _httpClient.DefaultRequestHeaders.Authorization = authHeader;
            }

            return(_httpClient.SendAsync(message, option, token));
        }
Exemple #9
0
        // TODO: Needs to refactored into smaller calls. Each continuation could be its own method, for example.
        public void Run()
        {
            IsRunning = true;

            var clientCopy = client;

            if (clientCopy == null)
            {
                // This is a race condition that can be reproduced by calling cbpuller.start() and cbpuller.stop()
                // directly afterwards.  What happens is that by the time the Changetracker thread fires up,
                // the cbpuller has already set this.client to null.  See issue #109
                Log.W(Tag, "ChangeTracker run() loop aborting because client == null");
                return;
            }

            if (tokenSource.IsCancellationRequested)
            {
                tokenSource.Dispose();
                tokenSource = new CancellationTokenSource();
            }

            backoff = new ChangeTrackerBackoff();

            while (IsRunning && !tokenSource.Token.IsCancellationRequested)
            {
                if (Request != null)
                {
                    Request.Dispose();
                    Request = null;
                }

                var url = GetChangesFeedURL();
                if (UsePost)
                {
                    Request = new HttpRequestMessage(HttpMethod.Post, url);
                    var body = GetChangesFeedPostBody();
                    Request.Content = new StringContent(body);
                    Request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
                }
                else
                {
                    Request = new HttpRequestMessage(HttpMethod.Get, url);
                }

                AddRequestHeaders(Request);

                var maskedRemoteWithoutCredentials = url.ToString();
                maskedRemoteWithoutCredentials = maskedRemoteWithoutCredentials.ReplaceAll("://.*:.*@", "://---:---@");
                Log.V(Tag, "Making request to " + maskedRemoteWithoutCredentials);

                if (tokenSource.Token.IsCancellationRequested)
                {
                    break;
                }

                Task <HttpResponseMessage> changesRequestTask = null;
                Task <HttpResponseMessage> successHandler;
                Task <Boolean>             errorHandler;

                HttpClient httpClient = null;
                try {
                    httpClient = clientCopy.GetHttpClient();
                    var authHeader = AuthUtils.GetAuthenticationHeaderValue(Authenticator, Request.RequestUri);
                    if (authHeader != null)
                    {
                        httpClient.DefaultRequestHeaders.Authorization = authHeader;
                    }

                    changesFeedRequestTokenSource = CancellationTokenSource.CreateLinkedTokenSource(tokenSource.Token);

                    var evt = new ManualResetEvent(false);

                    // We do this akward set of calls in order
                    // to help minimize the frequency of the error:
                    //
                    //   "Cannot re-call start of asynchronous method
                    //    while a previous call is still in progress."
                    //
                    // There's got to be a better way to deal with this.
                    var info = httpClient.SendAsync(
                        Request,
                        changesFeedRequestTokenSource.Token
                        );

                    info.ContinueWith((t) =>
                                      evt.Set()
                                      );

                    if (evt.WaitOne(ManagerOptions.Default.RequestTimeout) == false)
                    {
                        Log.W(Tag, "SendAsync timeout");
                        continue;
                    }

                    changesRequestTask = info;

                    successHandler = changesRequestTask.ContinueWith <HttpResponseMessage>(
                        ChangeFeedResponseHandler,
                        changesFeedRequestTokenSource.Token,
                        TaskContinuationOptions.LongRunning | TaskContinuationOptions.OnlyOnRanToCompletion,
                        WorkExecutor.Scheduler
                        );

                    errorHandler = changesRequestTask.ContinueWith(t =>
                    {
                        if (t.IsCanceled)
                        {
                            return(false); // Not a real error.
                        }
                        var err = t.Exception.Flatten();
                        Log.D(Tag, "ChangeFeedResponseHandler faulted.", err.InnerException ?? err);
                        Error = err.InnerException ?? err;
                        backoff.SleepAppropriateAmountOfTime();
                        return(true); // a real error.
                    }, changesFeedRequestTokenSource.Token, TaskContinuationOptions.OnlyOnFaulted, WorkExecutor.Scheduler);

                    try
                    {
                        Task.WaitAll(new Task[] { successHandler, errorHandler }, (Int32)ManagerOptions.Default.RequestTimeout.TotalMilliseconds, changesFeedRequestTokenSource.Token);
                        Log.D(Tag, "Finished processing changes feed.");
                    }
                    catch (Exception ex) {
                        var e = ex.InnerException ?? ex;
                        // Swallow TaskCancelledExceptions, which will always happen
                        // if either errorHandler or successHandler don't need to fire.
                        if (!(e is OperationCanceledException))
                        {
                            throw ex;
                        }
                    }
                    finally
                    {
                        if (changesRequestTask != null)
                        {
                            if (changesRequestTask.IsCompleted)
                            {
                                changesRequestTask.Dispose();
                            }

                            changesRequestTask = null;
                        }

                        if (successHandler != null)
                        {
                            if (successHandler.IsCompleted)
                            {
                                successHandler.Dispose();
                            }

                            successHandler = null;
                        }

                        if (errorHandler != null)
                        {
                            if (errorHandler.IsCompleted)
                            {
                                errorHandler.Dispose();
                            }

                            errorHandler = null;
                        }

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

                        if (changesFeedRequestTokenSource != null)
                        {
                            changesFeedRequestTokenSource.Dispose();
                            changesFeedRequestTokenSource = null;
                        }
                    }
                }
                catch (Exception e)
                {
                    if (!IsRunning && e.InnerException is IOException)
                    {
                        // swallow
                    }
                    else
                    {
                        // in this case, just silently absorb the exception because it
                        // frequently happens when we're shutting down and have to
                        // close the socket underneath our read.
                        Log.E(Tag, "Exception in change tracker", e);
                    }
                    backoff.SleepAppropriateAmountOfTime();
                }
                finally
                {
                    if (httpClient != null)
                    {
                        httpClient.Dispose();
                    }

                    if (mode == ChangeTrackerMode.OneShot)
                    {
                        Stop();
                    }
                }
            }
        }
Exemple #10
0
        public void Run()
        {
            IsRunning = true;

            var clientCopy = client;

            if (clientCopy == null)
            {
                // This is a race condition that can be reproduced by calling cbpuller.start() and cbpuller.stop()
                // directly afterwards.  What happens is that by the time the Changetracker thread fires up,
                // the cbpuller has already set this.client to null.  See issue #109
                Log.W(TAG, "ChangeTracker run() loop aborting because client == null");
                return;
            }

            if (tokenSource.IsCancellationRequested)
            {
                tokenSource.Dispose();
                tokenSource = new CancellationTokenSource();
            }

            backoff    = new ChangeTrackerBackoff();
            _startTime = DateTime.Now;
            if (Request != null)
            {
                Request.Dispose();
                Request = null;
            }

            var url = GetChangesFeedURL();

            Request = new HttpRequestMessage(HttpMethod.Post, url);
            var body = GetChangesFeedPostBody();

            Request.Content = new StringContent(body);
            Request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            AddRequestHeaders(Request);

            var maskedRemoteWithoutCredentials = url.ToString();

            maskedRemoteWithoutCredentials = maskedRemoteWithoutCredentials.ReplaceAll("://.*:.*@", "://---:---@");
            Log.V(TAG, "Making request to " + maskedRemoteWithoutCredentials);

            if (tokenSource.Token.IsCancellationRequested)
            {
                return;
            }

            HttpClient httpClient = null;

            try {
                httpClient = clientCopy.GetHttpClient(mode == ChangeTrackerMode.LongPoll);
                var challengeResponseAuth = Authenticator as IChallengeResponseAuthenticator;
                if (challengeResponseAuth != null)
                {
                    challengeResponseAuth.PrepareWithRequest(Request);
                }

                var authHeader = AuthUtils.GetAuthenticationHeaderValue(Authenticator, Request.RequestUri);
                if (authHeader != null)
                {
                    httpClient.DefaultRequestHeaders.Authorization = authHeader;
                }

                changesFeedRequestTokenSource = CancellationTokenSource.CreateLinkedTokenSource(tokenSource.Token);

                var option = mode == ChangeTrackerMode.LongPoll ? HttpCompletionOption.ResponseHeadersRead : HttpCompletionOption.ResponseContentRead;
                var info   = httpClient.SendAsync(
                    Request,
                    option,
                    changesFeedRequestTokenSource.Token
                    );

                info.ContinueWith(t1 => {
                    ChangeFeedResponseHandler(t1).ContinueWith(t2 =>
                    {
                        if (httpClient != null)
                        {
                            httpClient.Dispose();
                        }
                    });
                }, changesFeedRequestTokenSource.Token,
                                  TaskContinuationOptions.LongRunning,
                                  TaskScheduler.Default);
            }
            catch (Exception e)
            {
                if (!IsRunning && e.InnerException is IOException)
                {
                    // swallow
                }
                else
                {
                    // in this case, just silently absorb the exception because it
                    // frequently happens when we're shutting down and have to
                    // close the socket underneath our read.
                    Log.E(TAG, "Exception in change tracker", e);
                }
                backoff.SleepAppropriateAmountOfTime();
            }
        }
        // TODO: Needs to refactored into smaller calls. Each continuation could be its own method, for example.
        public void Run()
        {
            IsRunning = true;

            if (client == null)
            {
                // This is a race condition that can be reproduced by calling cbpuller.start() and cbpuller.stop()
                // directly afterwards.  What happens is that by the time the Changetracker thread fires up,
                // the cbpuller has already set this.client to null.  See issue #109
                Log.W(Tag, "ChangeTracker run() loop aborting because client == null");
                return;
            }

            if (tokenSource.IsCancellationRequested)
            {
                tokenSource.Dispose();
                tokenSource = new CancellationTokenSource();
            }

            backoff = new ChangeTrackerBackoff();

            while (IsRunning && !tokenSource.Token.IsCancellationRequested)
            {
//                if (changesRequestTask != null && !changesRequestTask.IsCanceled && !changesRequestTask.IsFaulted)
//                {
//                    Thread.Sleep(500);
//                    continue;
//                }
                var httpClient = client.GetHttpClient();

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

                var url = GetChangesFeedURL();
                if (UsePost)
                {
                    Request = new HttpRequestMessage(HttpMethod.Post, url);
                    var body = GetChangesFeedPostBody();
                    Request.Content = new StringContent(body);
                    Request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
                }
                else
                {
                    Request = new HttpRequestMessage(HttpMethod.Get, url);
                }

                AddRequestHeaders(Request);

                var authHeader = AuthUtils.GetAuthenticationHeaderValue(Authenticator, Request.RequestUri);
                if (authHeader != null)
                {
                    httpClient.DefaultRequestHeaders.Authorization = authHeader;
                }

                var maskedRemoteWithoutCredentials = url.ToString();
                maskedRemoteWithoutCredentials = maskedRemoteWithoutCredentials.ReplaceAll("://.*:.*@", "://---:---@");
                Log.V(Tag, "Making request to " + maskedRemoteWithoutCredentials);

                if (tokenSource.Token.IsCancellationRequested)
                {
                    break;
                }

                Task <HttpResponseMessage> changesRequestTask = null;
                Task <HttpResponseMessage> successHandler;
                Task <Boolean>             errorHandler;

                try {
                    changesFeedRequestTokenSource = CancellationTokenSource.CreateLinkedTokenSource(tokenSource.Token);

                    var evt = new ManualResetEvent(false);
                    //successHandler.ConfigureAwait(false).GetAwaiter().OnCompleted(()=>evt.Set());
                    //                    ChangeFeedResponseHandler(response);


                    var info = httpClient.SendAsync(
                        Request,
                        HttpCompletionOption.ResponseContentRead,
                        changesFeedRequestTokenSource.Token
                        );
                    var infoAwaiter = info.ConfigureAwait(false).GetAwaiter();
                    infoAwaiter.OnCompleted(() =>
                                            evt.Set()
                                            );
                    evt.WaitOne(ManagerOptions.Default.RequestTimeout);

                    changesRequestTask = info; //Task.FromResult(info.Result);

                    successHandler = changesRequestTask.ContinueWith <HttpResponseMessage>(
                        ChangeFeedResponseHandler,
                        changesFeedRequestTokenSource.Token,
                        TaskContinuationOptions.LongRunning | TaskContinuationOptions.OnlyOnRanToCompletion,
                        WorkExecutor.Scheduler
                        );

                    errorHandler = changesRequestTask.ContinueWith(t =>
                    {
                        if (t.IsCanceled)
                        {
                            return(false); // Not a real error.
                        }
                        var err = t.Exception.Flatten();
                        Log.D(Tag, "ChangeFeedResponseHandler faulted.", err.InnerException ?? err);
                        Error = err.InnerException ?? err;
                        return(true); // a real error.
                    }, changesFeedRequestTokenSource.Token, TaskContinuationOptions.OnlyOnFaulted, WorkExecutor.Scheduler);

                    try {
                        var completedTask = Task.WhenAll(successHandler, errorHandler);
                        completedTask.Wait((Int32)ManagerOptions.Default.RequestTimeout.TotalMilliseconds, changesFeedRequestTokenSource.Token);
                        Log.D(Tag, "Finished processing changes feed.");
                    } catch (Exception ex) {
                        // Swallow TaskCancelledExceptions, which will always happen
                        // if either errorHandler or successHandler don't need to fire.
                        if (!(ex.InnerException is TaskCanceledException))
                        {
                            throw ex;
                        }
                    } finally {
                        changesRequestTask.Dispose();
                        changesRequestTask = null;
                        successHandler.Dispose();
                        successHandler = null;
                        errorHandler.Dispose();
                        errorHandler = null;
                        Request.Dispose();
                        Request = null;
                        changesFeedRequestTokenSource.Dispose();
                        if (httpClient != null)
                        {
                            httpClient.Dispose();
                        }
                    }
                }
                catch (Exception e)
                {
                    if (!IsRunning && e.InnerException is IOException)
                    {
                        // swallow
                    }
                    else
                    {
                        // in this case, just silently absorb the exception because it
                        // frequently happens when we're shutting down and have to
                        // close the socket underneath our read.
                        Log.E(Tag, "Exception in change tracker", e);
                    }
                    backoff.SleepAppropriateAmountOfTime();
                }
                finally
                {
                    if (mode == ChangeTrackerMode.OneShot)
                    {
                        Stop();
                    }
                }
//                var singleRequestTokenSource = CancellationTokenSource.CreateLinkedTokenSource(tokenSource.Token);
//                var cTask = httpClient.SendAsync(Request, HttpCompletionOption.ResponseHeadersRead, singleRequestTokenSource.Token);
//                var bTask = cTask
//                    .ContinueWith<HttpResponseMessage>(t =>
//                    {
//                        if (!IsRunning)
//                        {
//                            return null;
//                            // swallow
//                        }
//                        if (t.IsFaulted && t.Exception.InnerException is IOException)
//                        {
//                            // in this case, just silently absorb the exception because it
//                            // frequently happens when we're shutting down and have to
//                            // close the socket underneath our read.
//                            Log.E(Tag, "Exception in change tracker", t.Exception);
//                            return null;
//                        }
//                        if (!singleRequestTokenSource.IsCancellationRequested && t.Exception != null)
//                        {
//                            var e = t.Exception.InnerException as WebException;
//                            var status = (HttpStatusCode)e.Status;
//                            if ((Int32)status >= 300 && !Misc.IsTransientError(status))
//                            {
//                                var response = t.Result;
//                                var msg = response.Content != null
//                                    ? String.Format("Change tracker got error with status code: {0}", status)
//                                    : String.Format("Change tracker got error with status code: {0} and null response content", status);
//                                Log.E(Tag, msg);
//                                Error = new CouchbaseLiteException(msg, new Status(status.GetStatusCode()));
//                                Stop();
//                            }
//                            backoff.SleepAppropriateAmountOfTime();
//                        }
//                        return t.Result;
//                    }, singleRequestTokenSource.Token, TaskContinuationOptions.OnlyOnFaulted, WorkExecutor.Scheduler)
//                    .ContinueWith<HttpResponseMessage>(ChangeFeedResponseHandler, singleRequestTokenSource.Token, TaskContinuationOptions.OnlyOnRanToCompletion, WorkExecutor.Scheduler)
//                    .ContinueWith<HttpResponseMessage>(t =>
//                    {
//                        Log.D(Tag, "ChangeFeedResponseHandler finished.");
//                        singleRequestTokenSource.Token.ThrowIfCancellationRequested();
//                        if (t != null) t.Result.Dispose();
//                        return null;
//                    }, singleRequestTokenSource.Token, TaskContinuationOptions.OnlyOnRanToCompletion, WorkExecutor.Scheduler);
//                changesRequestTask = bTask;
//                    .ContinueWith((t) =>
//                    {
//                        Log.D(Tag, "ChangeFeedResponseHandler faulted.");
//                    }, singleRequestTokenSource.Token, TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.AttachedToParent, TaskScheduler.Default);
            }
        }