Esempio n. 1
0
        private void ProcessLongPollStream(Task <Stream> t)
        {
            Log.D(TAG, "Got stream from change tracker response");
            bool beforeFirstItem = true;
            bool responseOK      = false;

            using (var jsonReader = Manager.GetObjectMapper().StartIncrementalParse(t.Result)) {
                responseOK = ReceivedPollResponse(jsonReader, ref beforeFirstItem);
            }

            Log.D(TAG, "Finished polling change tracker");

            if (responseOK)
            {
                Log.V(TAG, "Starting new longpoll");
                backoff.ResetBackoff();
                WorkExecutor.StartNew(Run);
            }
            else
            {
                backoff.SleepAppropriateAmountOfTime();
                if (beforeFirstItem)
                {
                    var elapsed = DateTime.Now - _startTime;
                    Log.W(TAG, "Longpoll connection closed (by proxy?) after {0} sec", elapsed.TotalSeconds);

                    // Looks like the connection got closed by a proxy (like AWS' load balancer) while the
                    // server was waiting for a change to send, due to lack of activity.
                    // Lower the heartbeat time to work around this, and reconnect:
                    _heartbeatMilliseconds = (int)(elapsed.TotalMilliseconds * 0.75f);
                    Log.V(TAG, "    Starting new longpoll");
                    backoff.ResetBackoff();
                    WorkExecutor.StartNew(Run);
                }
                else
                {
                    Log.W(TAG, "Change tracker calling stop");
                    WorkExecutor.StartNew(Stop);
                }
            }
        }
        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();
            }
        }
Esempio n. 3
0
        public virtual void Run()
        {
            running = true;
            HttpClient httpClient;

            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(Database.Tag, "ChangeTracker run() loop aborting because client == null");
                return;
            }
            if (mode == ChangeTracker.ChangeTrackerMode.Continuous)
            {
                // there is a failing unit test for this, and from looking at the code the Replication
                // object will never use Continuous mode anyway.  Explicitly prevent its use until
                // it is demonstrated to actually work.
                throw new RuntimeException("ChangeTracker does not correctly support continuous mode"
                                           );
            }
            httpClient = client.GetHttpClient();
            ChangeTrackerBackoff backoff = new ChangeTrackerBackoff();

            while (running)
            {
                Uri url = GetChangesFeedURL();
                request = new HttpRequestMessage(url.ToString());
                AddRequestHeaders(request);
                // if the URL contains user info AND if this a DefaultHttpClient
                // then preemptively set the auth credentials
                if (url.GetUserInfo() != null)
                {
                    Log.V(Database.Tag, "url.getUserInfo(): " + url.GetUserInfo());
                    if (url.GetUserInfo().Contains(":") && !url.GetUserInfo().Trim().Equals(":"))
                    {
                        string[] userInfoSplit = url.GetUserInfo().Split(":");
                        throw new NotImplementedException();
//						Credentials creds = new UsernamePasswordCredentials(URIUtils.Decode(userInfoSplit
//							[0]), URIUtils.Decode(userInfoSplit[1]));
//						if (httpClient is DefaultHttpClient)
//						{
//							DefaultHttpClient dhc = (DefaultHttpClient)httpClient;
//							MessageProcessingHandler preemptiveAuth = new _MessageProcessingHandler_212(creds
//								);
//                            dhc.AddRequestInterceptor((HttpWebRequest request, HttpContext context)=>
//                                {
//                                    AuthState authState = (AuthState)context.GetAttribute(ClientContext.TargetAuthState
//                                    );
//                                    CredentialsProvider credsProvider = (CredentialsProvider)context.GetAttribute(ClientContext
//                                        .CredsProvider);
//                                    HttpHost targetHost = (HttpHost)context.GetAttribute(ExecutionContext.HttpTargetHost
//                                    );
//                                    if (authState.GetAuthScheme() == null)
//                                    {
//                                        AuthScope authScope = new AuthScope(targetHost.GetHostName(), targetHost.GetPort(
//                                        ));
//                                        authState.SetAuthScheme(new BasicScheme());
//                                        authState.SetCredentials(creds);
//                                    }
//                                }, 0);
//						}
                    }
                    else
                    {
                        Log.W(Database.Tag, "ChangeTracker Unable to parse user info, not setting credentials"
                              );
                    }
                }
                try
                {
                    string maskedRemoteWithoutCredentials = GetChangesFeedURL().ToString();
                    maskedRemoteWithoutCredentials = maskedRemoteWithoutCredentials.ReplaceAll("://.*:.*@"
                                                                                               , "://---:---@");
                    Log.V(Database.Tag, "Making request to " + maskedRemoteWithoutCredentials);
                    HttpResponse response = httpClient.Execute(request);
                    StatusLine   status   = response.GetStatusLine();
                    if (status.GetStatusCode() >= 300)
                    {
                        Log.E(Database.Tag, "Change tracker got error " + Sharpen.Extensions.ToString(status
                                                                                                      .GetStatusCode()));
                        string msg = string.Format(status.ToString());
                        this.error = new CouchbaseLiteException(msg, new Status(status.GetStatusCode()));
                        Stop();
                    }
                    HttpEntity  entity = response.GetEntity();
                    InputStream input  = null;
                    if (entity != null)
                    {
                        input = entity.GetContent();
                        if (mode == ChangeTracker.ChangeTrackerMode.LongPoll)
                        {
                            IDictionary <string, object> fullBody = Manager.GetObjectMapper().ReadValue <IDictionary
                                                                                                         >(input);
                            bool responseOK = ReceivedPollResponse(fullBody);
                            if (mode == ChangeTracker.ChangeTrackerMode.LongPoll && responseOK)
                            {
                                Log.V(Database.Tag, "Starting new longpoll");
                                continue;
                            }
                            else
                            {
                                Log.W(Database.Tag, "Change tracker calling stop");
                                Stop();
                            }
                        }
                        else
                        {
                            JsonFactory jsonFactory = Manager.GetObjectMapper().GetJsonFactory();
                            JsonParser  jp          = jsonFactory.CreateJsonParser(input);
                            while (jp.CurrentToken() != JsonToken.StartArray)
                            {
                            }
                            // ignore these tokens
                            while (jp.CurrentToken() == JsonToken.StartObject)
                            {
                                IDictionary <string, object> change = (IDictionary)Manager.GetObjectMapper().ReadValue
                                                                      <IDictionary>(jp);
                                if (!ReceivedChange(change))
                                {
                                    Log.W(Database.Tag, string.Format("Received unparseable change line from server: %s"
                                                                      , change));
                                }
                            }
                            Stop();
                            break;
                        }
                        backoff.ResetBackoff();
                    }
                }
                catch (Exception e)
                {
                    if (!running && e is IOException)
                    {
                    }
                    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(Database.Tag, "Exception in change tracker", e);
                    }
                    backoff.SleepAppropriateAmountOfTime();
                }
            }
            Log.V(Database.Tag, "Change tracker run loop exiting");
        }
Esempio n. 4
0
		public virtual void Run()
		{
			running = true;
			HttpClient httpClient;
			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(Database.Tag, this + ": ChangeTracker run() loop aborting because client == null"
					);
				return;
			}
			if (mode == ChangeTracker.ChangeTrackerMode.Continuous)
			{
				// there is a failing unit test for this, and from looking at the code the Replication
				// object will never use Continuous mode anyway.  Explicitly prevent its use until
				// it is demonstrated to actually work.
				throw new RuntimeException("ChangeTracker does not correctly support continuous mode"
					);
			}
			httpClient = client.GetHttpClient();
			backoff = new ChangeTrackerBackoff();
			while (running)
			{
				Uri url = GetChangesFeedURL();
				request = new HttpGet(url.ToString());
				AddRequestHeaders(request);
				// if the URL contains user info AND if this a DefaultHttpClient
				// then preemptively set the auth credentials
				if (url.GetUserInfo() != null)
				{
					Log.V(Database.Tag, this + ": url.getUserInfo(): " + url.GetUserInfo());
					if (url.GetUserInfo().Contains(":") && !url.GetUserInfo().Trim().Equals(":"))
					{
						string[] userInfoSplit = url.GetUserInfo().Split(":");
						Credentials creds = new UsernamePasswordCredentials(URIUtils.Decode(userInfoSplit
							[0]), URIUtils.Decode(userInfoSplit[1]));
						if (httpClient is DefaultHttpClient)
						{
							DefaultHttpClient dhc = (DefaultHttpClient)httpClient;
							MessageProcessingHandler preemptiveAuth = new _MessageProcessingHandler_221(creds
								);
							dhc.AddRequestInterceptor(preemptiveAuth, 0);
						}
					}
					else
					{
						Log.W(Database.Tag, this + ": ChangeTracker Unable to parse user info, not setting credentials"
							);
					}
				}
				try
				{
					string maskedRemoteWithoutCredentials = GetChangesFeedURL().ToString();
					maskedRemoteWithoutCredentials = maskedRemoteWithoutCredentials.ReplaceAll("://.*:.*@"
						, "://---:---@");
					Log.V(Database.Tag, this + ": Making request to " + maskedRemoteWithoutCredentials
						);
					HttpResponse response = httpClient.Execute(request);
					StatusLine status = response.GetStatusLine();
					if (status.GetStatusCode() >= 300)
					{
						Log.E(Database.Tag, this + ": Change tracker got error " + Sharpen.Extensions.ToString
							(status.GetStatusCode()));
						string msg = string.Format(status.ToString());
						this.error = new CouchbaseLiteException(msg, new Status(status.GetStatusCode()));
						Stop();
					}
					HttpEntity entity = response.GetEntity();
					InputStream input = null;
					if (entity != null)
					{
						try
						{
							input = entity.GetContent();
							if (mode == ChangeTracker.ChangeTrackerMode.LongPoll)
							{
								// continuous replications
								IDictionary<string, object> fullBody = Manager.GetObjectMapper().ReadValue<IDictionary
									>(input);
								bool responseOK = ReceivedPollResponse(fullBody);
								if (mode == ChangeTracker.ChangeTrackerMode.LongPoll && responseOK)
								{
									Log.V(Database.Tag, this + ": Starting new longpoll");
									backoff.ResetBackoff();
									continue;
								}
								else
								{
									Log.W(Database.Tag, this + ": Change tracker calling stop (LongPoll)");
									Stop();
								}
							}
							else
							{
								// one-shot replications
								JsonFactory jsonFactory = Manager.GetObjectMapper().GetJsonFactory();
								JsonParser jp = jsonFactory.CreateJsonParser(input);
								while (jp.NextToken() != JsonToken.StartArray)
								{
								}
								// ignore these tokens
								while (jp.NextToken() == JsonToken.StartObject)
								{
									IDictionary<string, object> change = (IDictionary)Manager.GetObjectMapper().ReadValue
										<IDictionary>(jp);
									if (!ReceivedChange(change))
									{
										Log.W(Database.Tag, string.Format("Received unparseable change line from server: %s"
											, change));
									}
								}
								Log.W(Database.Tag, this + ": Change tracker calling stop (OneShot)");
								Stop();
								break;
							}
							backoff.ResetBackoff();
						}
						finally
						{
							try
							{
								entity.ConsumeContent();
							}
							catch (IOException)
							{
							}
						}
					}
				}
				catch (Exception e)
				{
					if (!running && e is IOException)
					{
					}
					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(Database.Tag, this + ": Exception in change tracker", e);
					}
					backoff.SleepAppropriateAmountOfTime();
				}
			}
			Log.V(Database.Tag, this + ": Change tracker run loop exiting");
		}
        // 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)
            {
                _startTime = DateTime.Now;
                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 successHandler;
                Task errorHandler;

                HttpClient httpClient = null;
                try {
                    httpClient = clientCopy.GetHttpClient(mode == ChangeTrackerMode.LongPoll);
                    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
                    );

                    successHandler = info.ContinueWith(
                        ChangeFeedResponseHandler, 
                        changesFeedRequestTokenSource.Token, 
                        TaskContinuationOptions.LongRunning | TaskContinuationOptions.OnlyOnRanToCompletion, 
                        WorkExecutor.Scheduler
                    );

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

                    try 
                    {
                        Task.WaitAll(successHandler, errorHandler);
                        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();
                    }
                }
            }
        }
Esempio n. 6
0
        HttpResponseMessage ChangeFeedResponseHandler(Task <HttpResponseMessage> responseTask)
        {
            var response = responseTask.Result;

            if (response == null)
            {
                return(null);
            }
            var status = response.StatusCode;

            if ((Int32)status >= 300 && !Misc.IsTransientError(status))
            {
                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();
                return(response);
            }

            switch (mode)
            {
            case ChangeTrackerMode.LongPoll:
            {
                if (response.Content == null)
                {
                    throw new CouchbaseLiteException("Got empty change tracker response", status.GetStatusCode());
                }

                var content = response.Content.ReadAsByteArrayAsync().Result;
                IDictionary <string, object> fullBody;
                try
                {
                    fullBody = Manager.GetObjectMapper().ReadValue <IDictionary <string, object> >(content) ?? new Dictionary <string, object>();
                }
                catch (JsonSerializationException ex)
                {
                    const string timeoutContent = "{\"results\":[";
                    if (!Encoding.UTF8.GetString(content).Trim().Equals(timeoutContent))
                    {
                        throw ex;
                    }
                    Log.V(Tag, "Timeout while waiting for changes.");
                    backoff.SleepAppropriateAmountOfTime();
                    return(response);
                }
                var responseOK = ReceivedPollResponse(fullBody);
                if (responseOK)
                {
                    Log.V(Tag, "Starting new longpoll");
                    backoff.ResetBackoff();
                    return(response);
                }
                else
                {
                    Log.W(Tag, "Change tracker calling stop");
                    Stop();
                }
            }
            break;

            default:
            {
                var content = response.Content.ReadAsByteArrayAsync().Result;
                var results = Manager.GetObjectMapper().ReadValue <IDictionary <String, Object> >(content.AsEnumerable());
                Log.D(Tag, "Received results from changes feed: {0}", results);
                var resultsValue = results["results"] as JArray;
                foreach (var item in resultsValue)
                {
                    IDictionary <String, Object> change = null;
                    try
                    {
                        change = item.ToObject <IDictionary <String, Object> >();
                    }
                    catch (Exception)
                    {
                        Log.E(Tag, this + string.Format(": Received unparseable change line from server: {0}", change));
                    }
                    if (!ReceivedChange(change))
                    {
                        Log.W(Tag, this + string.Format(": Received unparseable change line from server: {0}", change));
                    }
                }

                // As ReceivedChange() dispatches the change event to its client via WorkExecutor,
                // to avoid the Stop() to be called before the client is done handling the change event,
                // we need to setup the Stop() call with the WorkExecutor as well
                // (Assuming that the WorkExecutor is a single thread executor).

                WorkExecutor.StartNew(Stop);

                return(response);
            }
            }

            backoff.ResetBackoff();
            return(response);
        }
Esempio n. 7
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();
                    }
                }
            }
        }
        // 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 (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 = client.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
                    );
                    var infoAwaiter = info.ConfigureAwait(false).GetAwaiter();
                    infoAwaiter.OnCompleted(()=>
                        evt.Set()
                    );
                    evt.WaitOne(ManagerOptions.Default.RequestTimeout);

                    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 
                    {
                        var completedTask = TaskEx.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 OperationCanceledException))
                            throw ex;
                    } 
                    finally 
                    {
                        if (changesRequestTask.IsCompleted) 
                        {
                        changesRequestTask.Dispose();
                        }
                        changesRequestTask = null;

                        if (successHandler.IsCompleted) 
                        {
                        successHandler.Dispose();
                        }

                        successHandler = null;

                        if (errorHandler.IsCompleted) 
                        {
                        errorHandler.Dispose();
                        }

                        errorHandler = null;

                        Request.Dispose();
                        Request = 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();
                    }
                }
            }
        }
Esempio n. 9
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();
            }
        }
Esempio n. 10
0
        public virtual void Run()
        {
            running = true;
            HttpClient httpClient;

            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(Log.TagChangeTracker, "%s: ChangeTracker run() loop aborting because client == null"
                      , this);
                return;
            }
            if (mode == ChangeTracker.ChangeTrackerMode.Continuous)
            {
                // there is a failing unit test for this, and from looking at the code the Replication
                // object will never use Continuous mode anyway.  Explicitly prevent its use until
                // it is demonstrated to actually work.
                throw new RuntimeException("ChangeTracker does not correctly support continuous mode"
                                           );
            }
            httpClient = client.GetHttpClient();
            backoff    = new ChangeTrackerBackoff();
            while (running)
            {
                Uri url = GetChangesFeedURL();
                if (usePOST)
                {
                    HttpPost postRequest = new HttpPost(url.ToString());
                    postRequest.SetHeader("Content-Type", "application/json");
                    StringEntity entity;
                    try
                    {
                        entity = new StringEntity(ChangesFeedPOSTBody());
                    }
                    catch (UnsupportedEncodingException e)
                    {
                        throw new RuntimeException(e);
                    }
                    postRequest.SetEntity(entity);
                    request = postRequest;
                }
                else
                {
                    request = new HttpGet(url.ToString());
                }
                AddRequestHeaders(request);
                // Perform BASIC Authentication if needed
                bool isUrlBasedUserInfo = false;
                // If the URL contains user info AND if this a DefaultHttpClient then preemptively set the auth credentials
                string userInfo = url.GetUserInfo();
                if (userInfo != null)
                {
                    isUrlBasedUserInfo = true;
                }
                else
                {
                    if (authenticator != null)
                    {
                        AuthenticatorImpl auth = (AuthenticatorImpl)authenticator;
                        userInfo = auth.AuthUserInfo();
                    }
                }
                if (userInfo != null)
                {
                    if (userInfo.Contains(":") && !userInfo.Trim().Equals(":"))
                    {
                        string[] userInfoElements = userInfo.Split(":");
                        string   username         = isUrlBasedUserInfo ? URIUtils.Decode(userInfoElements[0]) : userInfoElements
                                                    [0];
                        string password = isUrlBasedUserInfo ? URIUtils.Decode(userInfoElements[1]) : userInfoElements
                                          [1];
                        Credentials credentials = new UsernamePasswordCredentials(username, password);
                        if (httpClient is DefaultHttpClient)
                        {
                            DefaultHttpClient        dhc            = (DefaultHttpClient)httpClient;
                            MessageProcessingHandler preemptiveAuth = new _MessageProcessingHandler_285(credentials
                                                                                                        );
                            dhc.AddRequestInterceptor(preemptiveAuth, 0);
                        }
                    }
                    else
                    {
                        Log.W(Log.TagChangeTracker, "RemoteRequest Unable to parse user info, not setting credentials"
                              );
                    }
                }
                try
                {
                    string maskedRemoteWithoutCredentials = GetChangesFeedURL().ToString();
                    maskedRemoteWithoutCredentials = maskedRemoteWithoutCredentials.ReplaceAll("://.*:.*@"
                                                                                               , "://---:---@");
                    Log.V(Log.TagChangeTracker, "%s: Making request to %s", this, maskedRemoteWithoutCredentials
                          );
                    HttpResponse response = httpClient.Execute(request);
                    StatusLine   status   = response.GetStatusLine();
                    if (status.GetStatusCode() >= 300 && !Utils.IsTransientError(status))
                    {
                        Log.E(Log.TagChangeTracker, "%s: Change tracker got error %d", this, status.GetStatusCode
                                  ());
                        this.error = new HttpResponseException(status.GetStatusCode(), status.GetReasonPhrase
                                                                   ());
                        Stop();
                    }
                    HttpEntity  entity = response.GetEntity();
                    InputStream input  = null;
                    if (entity != null)
                    {
                        try
                        {
                            input = entity.GetContent();
                            if (mode == ChangeTracker.ChangeTrackerMode.LongPoll)
                            {
                                // continuous replications
                                IDictionary <string, object> fullBody = Manager.GetObjectMapper().ReadValue <IDictionary
                                                                                                             >(input);
                                bool responseOK = ReceivedPollResponse(fullBody);
                                if (mode == ChangeTracker.ChangeTrackerMode.LongPoll && responseOK)
                                {
                                    Log.V(Log.TagChangeTracker, "%s: Starting new longpoll", this);
                                    backoff.ResetBackoff();
                                    continue;
                                }
                                else
                                {
                                    Log.W(Log.TagChangeTracker, "%s: Change tracker calling stop (LongPoll)", this);
                                    Stop();
                                }
                            }
                            else
                            {
                                // one-shot replications
                                JsonFactory jsonFactory = Manager.GetObjectMapper().GetJsonFactory();
                                JsonParser  jp          = jsonFactory.CreateJsonParser(input);
                                while (jp.NextToken() != JsonToken.StartArray)
                                {
                                }
                                // ignore these tokens
                                while (jp.NextToken() == JsonToken.StartObject)
                                {
                                    IDictionary <string, object> change = (IDictionary)Manager.GetObjectMapper().ReadValue
                                                                          <IDictionary>(jp);
                                    if (!ReceivedChange(change))
                                    {
                                        Log.W(Log.TagChangeTracker, "Received unparseable change line from server: %s", change
                                              );
                                    }
                                }
                                Log.W(Log.TagChangeTracker, "%s: Change tracker calling stop (OneShot)", this);
                                Stop();
                                break;
                            }
                            backoff.ResetBackoff();
                        }
                        finally
                        {
                            try
                            {
                                entity.ConsumeContent();
                            }
                            catch (IOException)
                            {
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    if (!running && e is IOException)
                    {
                    }
                    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(Log.TagChangeTracker, this + ": Exception in change tracker", e);
                    }
                    backoff.SleepAppropriateAmountOfTime();
                }
            }
            Log.V(Log.TagChangeTracker, "%s: Change tracker run loop exiting", this);
        }
 public virtual void Run()
 {
     running = true;
     HttpClient httpClient;
     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(Log.TagChangeTracker, "%s: ChangeTracker run() loop aborting because client == null"
             , this);
         return;
     }
     if (mode == ChangeTracker.ChangeTrackerMode.Continuous)
     {
         // there is a failing unit test for this, and from looking at the code the Replication
         // object will never use Continuous mode anyway.  Explicitly prevent its use until
         // it is demonstrated to actually work.
         throw new RuntimeException("ChangeTracker does not correctly support continuous mode"
             );
     }
     httpClient = client.GetHttpClient();
     backoff = new ChangeTrackerBackoff();
     while (running)
     {
         Uri url = GetChangesFeedURL();
         if (usePOST)
         {
             HttpPost postRequest = new HttpPost(url.ToString());
             postRequest.SetHeader("Content-Type", "application/json");
             StringEntity entity;
             try
             {
                 entity = new StringEntity(ChangesFeedPOSTBody());
             }
             catch (UnsupportedEncodingException e)
             {
                 throw new RuntimeException(e);
             }
             postRequest.SetEntity(entity);
             request = postRequest;
         }
         else
         {
             request = new HttpGet(url.ToString());
         }
         AddRequestHeaders(request);
         // Perform BASIC Authentication if needed
         bool isUrlBasedUserInfo = false;
         // If the URL contains user info AND if this a DefaultHttpClient then preemptively set the auth credentials
         string userInfo = url.GetUserInfo();
         if (userInfo != null)
         {
             isUrlBasedUserInfo = true;
         }
         else
         {
             if (authenticator != null)
             {
                 AuthenticatorImpl auth = (AuthenticatorImpl)authenticator;
                 userInfo = auth.AuthUserInfo();
             }
         }
         if (userInfo != null)
         {
             if (userInfo.Contains(":") && !userInfo.Trim().Equals(":"))
             {
                 string[] userInfoElements = userInfo.Split(":");
                 string username = isUrlBasedUserInfo ? URIUtils.Decode(userInfoElements[0]) : userInfoElements
                     [0];
                 string password = isUrlBasedUserInfo ? URIUtils.Decode(userInfoElements[1]) : userInfoElements
                     [1];
                 Credentials credentials = new UsernamePasswordCredentials(username, password);
                 if (httpClient is DefaultHttpClient)
                 {
                     DefaultHttpClient dhc = (DefaultHttpClient)httpClient;
                     MessageProcessingHandler preemptiveAuth = new _MessageProcessingHandler_285(credentials
                         );
                     dhc.AddRequestInterceptor(preemptiveAuth, 0);
                 }
             }
             else
             {
                 Log.W(Log.TagChangeTracker, "RemoteRequest Unable to parse user info, not setting credentials"
                     );
             }
         }
         try
         {
             string maskedRemoteWithoutCredentials = GetChangesFeedURL().ToString();
             maskedRemoteWithoutCredentials = maskedRemoteWithoutCredentials.ReplaceAll("://.*:.*@"
                 , "://---:---@");
             Log.V(Log.TagChangeTracker, "%s: Making request to %s", this, maskedRemoteWithoutCredentials
                 );
             HttpResponse response = httpClient.Execute(request);
             StatusLine status = response.GetStatusLine();
             if (status.GetStatusCode() >= 300 && !Utils.IsTransientError(status))
             {
                 Log.E(Log.TagChangeTracker, "%s: Change tracker got error %d", this, status.GetStatusCode
                     ());
                 this.error = new HttpResponseException(status.GetStatusCode(), status.GetReasonPhrase
                     ());
                 Stop();
             }
             HttpEntity entity = response.GetEntity();
             InputStream input = null;
             if (entity != null)
             {
                 try
                 {
                     input = entity.GetContent();
                     if (mode == ChangeTracker.ChangeTrackerMode.LongPoll)
                     {
                         // continuous replications
                         IDictionary<string, object> fullBody = Manager.GetObjectMapper().ReadValue<IDictionary
                             >(input);
                         bool responseOK = ReceivedPollResponse(fullBody);
                         if (mode == ChangeTracker.ChangeTrackerMode.LongPoll && responseOK)
                         {
                             Log.V(Log.TagChangeTracker, "%s: Starting new longpoll", this);
                             backoff.ResetBackoff();
                             continue;
                         }
                         else
                         {
                             Log.W(Log.TagChangeTracker, "%s: Change tracker calling stop (LongPoll)", this);
                             Stop();
                         }
                     }
                     else
                     {
                         // one-shot replications
                         JsonFactory jsonFactory = Manager.GetObjectMapper().GetJsonFactory();
                         JsonParser jp = jsonFactory.CreateJsonParser(input);
                         while (jp.NextToken() != JsonToken.StartArray)
                         {
                         }
                         // ignore these tokens
                         while (jp.NextToken() == JsonToken.StartObject)
                         {
                             IDictionary<string, object> change = (IDictionary)Manager.GetObjectMapper().ReadValue
                                 <IDictionary>(jp);
                             if (!ReceivedChange(change))
                             {
                                 Log.W(Log.TagChangeTracker, "Received unparseable change line from server: %s", change
                                     );
                             }
                         }
                         Log.W(Log.TagChangeTracker, "%s: Change tracker calling stop (OneShot)", this);
                         Stop();
                         break;
                     }
                     backoff.ResetBackoff();
                 }
                 finally
                 {
                     try
                     {
                         entity.ConsumeContent();
                     }
                     catch (IOException)
                     {
                     }
                 }
             }
         }
         catch (Exception e)
         {
             if (!running && e is IOException)
             {
             }
             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(Log.TagChangeTracker, this + ": Exception in change tracker", e);
             }
             backoff.SleepAppropriateAmountOfTime();
         }
     }
     Log.V(Log.TagChangeTracker, "%s: Change tracker run loop exiting", this);
 }
        // 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);
            }
        }
        // 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);
            }
        }
Esempio n. 14
0
        // TODO: Needs to refactored into smaller calls. Each continuation could be its own method, for example.
        public void Run()
        {
            running = true;
            HttpClient httpClient;

            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, this + ": ChangeTracker run() loop aborting because client == null");
                return;
            }
            if (mode == ChangeTracker.ChangeTrackerMode.Continuous)
            {
                // there is a failing unit test for this, and from looking at the code the Replication
                // object will never use Continuous mode anyway.  Explicitly prevent its use until
                // it is demonstrated to actually work.
                throw new RuntimeException("ChangeTracker does not correctly support continuous mode");
            }

            httpClient = client.GetHttpClient();
            backoff    = new ChangeTrackerBackoff();

            var shouldBreak = false;

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

                var url = GetChangesFeedURL();
                Request = new HttpRequestMessage(HttpMethod.Get, url);

                AddRequestHeaders(Request);

                // if the URL contains user info AND if this a DefaultHttpClient
                // then preemptively set/update the auth credentials
                if (url.UserInfo != null)
                {
                    Log.V(Tag, "url.getUserInfo(): " + url.GetUserInfo());

                    var credentials = Request.ToCredentialsFromUri();
                    if (credentials != null)
                    {
                        var handler = client.HttpHandler;
                        if (handler.Credentials == null || !handler.Credentials.Equals(credentials))
                        {
                            client.HttpHandler.Credentials = credentials;
                        }
                    }
                    else
                    {
                        Log.W(Tag, this + ": ChangeTracker Unable to parse user info, not setting credentials");
                    }
                }

                try
                {
                    var requestStatus = CurrentRequest == null
                        ? TaskStatus.Canceled
                        : CurrentRequest.Status;

                    Log.V(Tag, this + ": Current Request Status: " + requestStatus);

                    if (requestStatus == TaskStatus.Running || requestStatus == TaskStatus.WaitingForActivation)
                    {
                        //System.Threading.Thread.Sleep(5000);
                        continue;
                    }
                    var maskedRemoteWithoutCredentials = GetChangesFeedURL().ToString();
                    maskedRemoteWithoutCredentials = maskedRemoteWithoutCredentials.ReplaceAll("://.*:.*@", "://---:---@");
                    Log.V(Tag, this + ": Making request to " + maskedRemoteWithoutCredentials);
                    if (tokenSource.Token.IsCancellationRequested)
                    {
                        break;
                    }
                    CurrentRequest = httpClient.SendAsync(Request)
                                     .ContinueWith <HttpResponseMessage>(request =>
                    {
                        if (request.Status != System.Threading.Tasks.TaskStatus.RanToCompletion && request.IsFaulted)
                        {
                            Log.E(Tag, this + ": Change tracker got error " + Extensions.ToString(request.Status));
                            throw request.Exception;
                        }
                        return(request.Result);
                    }, this.tokenSource.Token)
                                     .ContinueWith <Task <Byte[]> >((request) =>
                    {
                        var status = request.Result.StatusCode;
                        if ((Int32)status >= 300)
                        {
                            var msg = String.Format("Change tracker got error: {0}", status);
                            Log.E(Tag, msg);
                            Error = new CouchbaseLiteException(msg, new Status(status.GetStatusCode()));
                            Stop();
                        }
                        return(request.Result.Content.ReadAsByteArrayAsync());
                    }, this.tokenSource.Token)
                                     .ContinueWith((Task <Task <Byte[]> > response) =>
                    {
                        if (response.Status != System.Threading.Tasks.TaskStatus.RanToCompletion &&
                            !response.IsFaulted &&
                            response.Result != null)
                        {
                            return;
                        }

                        var result = response.Result.Result;

                        if (mode == ChangeTrackerMode.LongPoll)
                        {
                            var fullBody   = Manager.GetObjectMapper().ReadValue <IDictionary <string, object> >(result.AsEnumerable());
                            var responseOK = ReceivedPollResponse(fullBody);
                            if (mode == ChangeTracker.ChangeTrackerMode.LongPoll && responseOK)
                            {
                                Log.V(Tag, this + ": Starting new longpoll");
                                backoff.ResetBackoff();
                                return;
                            }
                            else
                            {
                                Log.W(Tag, this + ": Change tracker calling stop");
                                Stop();
                            }
                        }
                        else
                        {
                            var results      = Manager.GetObjectMapper().ReadValue <IDictionary <String, Object> >(result.AsEnumerable());
                            var resultsValue = results["results"] as Newtonsoft.Json.Linq.JArray;
                            foreach (var item in resultsValue)
                            {
                                IDictionary <String, Object> change = null;
                                try {
                                    change = item.ToObject <IDictionary <String, Object> >();
                                } catch (Exception) {
                                    Log.E(Tag, this + string.Format(": Received unparseable change line from server: {0}", change));
                                }
                                if (!ReceivedChange(change))
                                {
                                    Log.W(Tag, this + string.Format(": Received unparseable change line from server: {0}", change));
                                }
                            }
                            Stop();
                            shouldBreak = true;
                            return;
                        }
                        backoff.ResetBackoff();
                    }, tokenSource.Token);
                }
                catch (Exception e)
                {
                    if (!running && e 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, this + ": Exception in change tracker", e);
                    }
                    backoff.SleepAppropriateAmountOfTime();
                }
                if (shouldBreak)
                {
                    break;
                }
            }
            if (!tokenSource.Token.IsCancellationRequested)
            {   // Handle cancellation requests while we are waiting.
                // e.g. when Stop() is called from another thread.
                try {
                    CurrentRequest.Wait(tokenSource.Token);
                } catch (Exception) {
                    Log.V(Tag, this + ": Run loop was cancelled.");
                }
            }
            Log.V(Tag, this + ": Change tracker run loop exiting");
        }
Esempio n. 15
0
        void ChangeFeedResponseHandler(Task <HttpResponseMessage> responseTask)
        {
            if (responseTask.IsCanceled || responseTask.IsFaulted)
            {
                if (!responseTask.IsCanceled)
                {
                    var err = responseTask.Exception.Flatten();
                    Log.D(Tag, "ChangeFeedResponseHandler faulted.", err.InnerException ?? err);
                    Error = err.InnerException ?? err;
                    backoff.SleepAppropriateAmountOfTime();
                    responseTask.Result.Dispose();
                }

                return;
            }

            var response = responseTask.Result;

            if (response == null)
            {
                return;
            }

            var status = response.StatusCode;

            if ((Int32)status >= 300 && !Misc.IsTransientError(status))
            {
                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();
                response.Dispose();
                return;
            }

            try {
                switch (mode)
                {
                case ChangeTrackerMode.LongPoll:
                {
                    if (response.Content == null)
                    {
                        throw new CouchbaseLiteException("Got empty change tracker response", status.GetStatusCode());
                    }

                    Log.D(Tag, "Getting stream from change tracker response");
                    Stream stream = response.Content.ReadAsStreamAsync().Result;
                    Log.D(Tag, "Got stream from change tracker response");
                    bool beforeFirstItem = true;
                    bool responseOK      = false;
                    using (var jsonReader = Manager.GetObjectMapper().StartIncrementalParse(stream)) {
                        responseOK = ReceivedPollResponse(jsonReader, ref beforeFirstItem);
                    }

                    Log.D(Tag, "Finished polling change tracker");

                    if (responseOK)
                    {
                        Log.V(Tag, "Starting new longpoll");
                        backoff.ResetBackoff();
                    }
                    else
                    {
                        backoff.SleepAppropriateAmountOfTime();
                        if (beforeFirstItem)
                        {
                            var elapsed = DateTime.Now - _startTime;
                            Log.W(Tag, "Longpoll connection closed (by proxy?) after {0} sec", elapsed.TotalSeconds);

                            // Looks like the connection got closed by a proxy (like AWS' load balancer) while the
                            // server was waiting for a change to send, due to lack of activity.
                            // Lower the heartbeat time to work around this, and reconnect:
                            _heartbeatMilliseconds = (int)(elapsed.TotalMilliseconds * 0.75f);
                            Log.V(Tag, "    Starting new longpoll");
                            backoff.ResetBackoff();
                        }
                        else
                        {
                            Log.W(Tag, "Change tracker calling stop");
                            WorkExecutor.StartNew(Stop);
                        }
                    }
                }
                break;

                default:
                {
                    Stream content = response.Content.ReadAsStreamAsync().Result;
                    using (var jsonReader = Manager.GetObjectMapper().StartIncrementalParse(content)) {
                        bool timedOut = false;
                        ReceivedPollResponse(jsonReader, ref timedOut);
                    }

                    IsRunning = false;
                    Stopped();
                }
                break;
                }

                backoff.ResetBackoff();
            } finally {
                response.Dispose();
            }
        }
Esempio n. 16
0
        // TODO: Needs to refactored into smaller calls. Each continuation could be its own method, for example.
		public void Run()
		{
			running = true;
			HttpClient httpClient;

			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, this + ": ChangeTracker run() loop aborting because client == null");
				return;
			}
			if (mode == ChangeTracker.ChangeTrackerMode.Continuous)
			{
				// there is a failing unit test for this, and from looking at the code the Replication
				// object will never use Continuous mode anyway.  Explicitly prevent its use until
				// it is demonstrated to actually work.
				throw new RuntimeException("ChangeTracker does not correctly support continuous mode");
			}

			httpClient = client.GetHttpClient();
            backoff = new ChangeTrackerBackoff();

            var shouldBreak = false;
			while (running)
			{
                if (tokenSource.Token.IsCancellationRequested)
                    break;

                var url = GetChangesFeedURL();
                Request = new HttpRequestMessage(HttpMethod.Get, url);

				AddRequestHeaders(Request);

				// if the URL contains user info AND if this a DefaultHttpClient
                // then preemptively set/update the auth credentials
				if (url.UserInfo != null)
				{
					Log.V(Tag, "url.getUserInfo(): " + url.GetUserInfo());

                    var credentials = Request.ToCredentialsFromUri();
                    if (credentials != null)
					{
                        var handler = client.HttpHandler;
                        if (handler.Credentials == null || !handler.Credentials.Equals(credentials))
                            client.HttpHandler.Credentials = credentials;
					}
					else
					{
                        Log.W(Tag, this + ": ChangeTracker Unable to parse user info, not setting credentials");
					}
				}

				try
				{
                    var requestStatus = CurrentRequest == null 
                        ? TaskStatus.Canceled 
                        : CurrentRequest.Status;

                    Log.V(Tag, this + ": Current Request Status: " + requestStatus);

                    if (requestStatus == TaskStatus.Running || requestStatus == TaskStatus.WaitingForActivation) 
                    {
                        //System.Threading.Thread.Sleep(5000);
                        continue;
                    }
                    var maskedRemoteWithoutCredentials = GetChangesFeedURL().ToString();
                    maskedRemoteWithoutCredentials = maskedRemoteWithoutCredentials.ReplaceAll("://.*:.*@", "://---:---@");
                    Log.V(Tag, this + ": Making request to " + maskedRemoteWithoutCredentials);
                    if (tokenSource.Token.IsCancellationRequested)
                        break;
                    CurrentRequest = httpClient.SendAsync(Request)
                        .ContinueWith<HttpResponseMessage>(request=>
                        {
                            if (request.Status != System.Threading.Tasks.TaskStatus.RanToCompletion && request.IsFaulted)
                            {
                                Log.E(Tag, this + ": Change tracker got error " + Extensions.ToString(request.Status));
                                throw request.Exception;
                            }
                            return request.Result;
                        }, this.tokenSource.Token)
                        .ContinueWith<Task<Byte[]>>((request) =>
                        {
                            var status = request.Result.StatusCode;
                            if ((Int32)status >= 300)
                            {
                                var msg = String.Format("Change tracker got error: {0}", status);
                                Log.E(Tag, msg);
                                Error = new CouchbaseLiteException (msg, new Status (status.GetStatusCode ()));
                                Stop();
                            }
                            return request.Result.Content.ReadAsByteArrayAsync();
                        }, this.tokenSource.Token)
                        .ContinueWith((Task<Task<Byte[]>> response) => 
                        {
                            if (response.Status != System.Threading.Tasks.TaskStatus.RanToCompletion
                                && !response.IsFaulted
                                && response.Result != null)
                                return;

                            var result = response.Result.Result;

                            if (mode == ChangeTrackerMode.LongPoll)
                            {
                                var fullBody = Manager.GetObjectMapper().ReadValue<IDictionary<string, object>>(result.AsEnumerable());
                                var responseOK = ReceivedPollResponse(fullBody);
                                if (mode == ChangeTracker.ChangeTrackerMode.LongPoll && responseOK)
                                {
                                    Log.V(Tag, this + ": Starting new longpoll");
                                    backoff.ResetBackoff();
                                    return;
                                }
                                else
                                {
                                    Log.W(Tag, this + ": Change tracker calling stop");
                                    Stop();
                                }
                            }
                            else
                            {
                                var results = Manager.GetObjectMapper().ReadValue<IDictionary<String, Object>>(result.AsEnumerable());
                                var resultsValue = results["results"] as Newtonsoft.Json.Linq.JArray;
                                foreach (var item in resultsValue)
                                {
                                    IDictionary<String, Object> change = null;
                                    try {
                                        change = item.ToObject<IDictionary<String, Object>>();
                                    } catch (Exception) {
                                        Log.E(Tag, this + string.Format(": Received unparseable change line from server: {0}", change));
                                    }
                                    if (!ReceivedChange(change))
                                    {
                                        Log.W(Tag, this + string.Format(": Received unparseable change line from server: {0}", change));
                                    }
                                }
                                Stop();
                                shouldBreak = true;
                                return;
                            }
                            backoff.ResetBackoff();
                        }, tokenSource.Token);
				}
				catch (Exception e)
				{
					if (!running && e 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, this + ": Exception in change tracker", e);
					}
					backoff.SleepAppropriateAmountOfTime();
				}
                if (shouldBreak) break;
			}
            if (!tokenSource.Token.IsCancellationRequested)
            {   // Handle cancellation requests while we are waiting.
                // e.g. when Stop() is called from another thread.
                try {
                    CurrentRequest.Wait (tokenSource.Token);
                } catch (Exception) {
                    Log.V(Tag, this + ": Run loop was cancelled.");
                }
            }
            Log.V(Tag, this + ": Change tracker run loop exiting");
		}