// Called when the web socket establishes a connection
        private void OnConnect(object sender, EventArgs args)
        {
            if (_cts.IsCancellationRequested)
            {
                Log.To.ChangeTracker.I(Tag, "{0} Cancellation requested, aborting in OnConnect", this);
                return;
            }

            Misc.SafeDispose(ref _responseLogic);
            _responseLogic               = new WebSocketLogic();
            _responseLogic.OnCaughtUp    = () => Client?.ChangeTrackerCaughtUp(this);
            _responseLogic.OnChangeFound = (change) =>
            {
                if (!ReceivedChange(change))
                {
                    Log.To.ChangeTracker.W(Tag, String.Format("change is not parseable"));
                }
            };

            Backoff.ResetBackoff();
            Log.To.ChangeTracker.V(Tag, "{0} websocket opened", this);

            // Now that the WebSocket is open, send the changes-feed options (the ones that would have
            // gone in the POST body if this were HTTP-based.)
            var bytes = GetChangesFeedPostBody().ToArray();

            _client?.SendAsync(bytes, null);
        }
        private Task ChangeFeedResponseHandler(Task <HttpResponseMessage> responseTask)
        {
            if (ResponseFailed(responseTask))
            {
                return(Task.FromResult(false));
            }

            var response = responseTask.Result;

            UpdateServerType(response);

            if (response.Content == null)
            {
                throw Misc.CreateExceptionAndLog(Log.To.ChangeTracker, response.StatusCode.GetStatusCode(), Tag,
                                                 "Got empty change tracker response");
            }

            Log.To.ChangeTracker.D(Tag, "Getting stream from change tracker response");
            return(response.Content.ReadAsStreamAsync().ContinueWith((Task <Stream> t) =>
            {
                try {
                    var result = _responseLogic.ProcessResponseStream(t?.Result, changesFeedRequestTokenSource == null ? CancellationToken.None : changesFeedRequestTokenSource.Token);
                    Backoff.ResetBackoff();
                    if (result == ChangeTrackerResponseCode.ChangeHeartbeat)
                    {
                        Heartbeat = _responseLogic.Heartbeat;
                        _workExecutor.StartNew(Run);
                    }
                } catch (CouchbaseLiteException e) {
                    if (e.Code == StatusCode.BadJson)
                    {
                        if (Backoff.CanContinue)
                        {
                            Log.To.ChangeTracker.W(Tag, "{0} Couldn't parse JSON from remote, " +
                                                   "retrying in {1}ms", this, Backoff.GetSleepTime().TotalMilliseconds);
                            Backoff.DelayAppropriateAmountOfTime().ContinueWith(t1 => {
                                Log.To.ChangeTracker.I(Tag, "{0} retrying NOW...", this);
                                _workExecutor.StartNew(Run);
                            });
                        }
                        else
                        {
                            RetryOrStopIfNecessary(e);
                        }
                    }
                } catch (Exception e) {
                    RetryOrStopIfNecessary(e);
                } finally {
                    Misc.SafeDispose(ref changesFeedRequestTokenSource);
                    response.Dispose();
                }
            }));
        }
예제 #3
0
        public override bool Start()
        {
            if (IsRunning)
            {
                return(false);
            }

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

            var authHeader = (_remoteSession.Authenticator as ICustomHeadersAuthorizer)?.AuthorizationHeaderValue;

            // A WebSocket has to be opened with a GET request, not a POST (as defined in the RFC.)
            // Instead of putting the options in the POST body as with HTTP, we will send them in an
            // initial WebSocket message
            _usePost  = false;
            _caughtUp = false;
            _client   = new WebSocket(ChangesFeedUrl.AbsoluteUri);
            var systemProxy      = WebRequest.DefaultWebProxy?.GetProxy(DatabaseUrl)?.ToString();
            var proxyCredentials = WebRequest.DefaultWebProxy?.Credentials?.GetCredential(DatabaseUrl, "");

            if (systemProxy != null && proxyCredentials != null)
            {
                _client.SetProxy(systemProxy, proxyCredentials.UserName, proxyCredentials.Password);
            }

            _client.WaitTime   = TimeSpan.FromSeconds(5);
            _client.OnOpen    += OnConnect;
            _client.OnMessage += OnReceive;
            _client.OnError   += OnError;
            _client.OnClose   += OnClose;
            _client.SslConfiguration.EnabledSslProtocols  = (SslProtocols)ServicePointManager.SecurityProtocol;
            _client.SslConfiguration.EnabledSslProtocols &= ~SslProtocols.Ssl3;
            _client.SslConfiguration.EnabledSslProtocols |= SslProtocols.Tls;
            foreach (Cookie cookie in Client.GetCookieStore().GetCookies(ChangesFeedUrl))
            {
                _client.SetCookie(new WebSocketSharp.Net.Cookie(cookie.Name, cookie.Value, cookie.Path, cookie.Domain));
            }

            var additionalHeaders = new Dictionary <string, string>();

            if (authHeader != null)
            {
                additionalHeaders["Authorization"] = authHeader.ToString();
            }

            foreach (var header in _remoteSession.RequestHeaders)
            {
                additionalHeaders[header.Key] = header.Value;
            }

            if (additionalHeaders.Any())
            {
                _client.CustomHeaders = additionalHeaders;
            }

            Backoff.ResetBackoff();
            _client.ConnectAsync();
            return(true);
        }