// 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(); } })); }
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); }