private async Task ReadLoop(HttpResponseMessage httpResponse, CancellationToken token) { try { await Task.Run(async() => { using (httpResponse) { using (var content = await httpResponse.Content.ReadAsStreamAsync()) { using (var sr = new StreamReader(content)) { bool?isFirstTimeRetrieval = null; #region ----- While true ----- while (true) { CancellationToken.Token.ThrowIfCancellationRequested(); string line; #region ----- Read line line = await sr.ReadLineAsync(); if (string.IsNullOrEmpty(line)) { continue; } if (OnEventStreaming != null) { OnEventStreaming(this, new EventStreamingEventArgs(line)); } #endregion string eventName; string dataLine; #region ----- Read event name / data line if (!line.StartsWith("event: ")) { var message = string.Format("Expecting line to start with 'event: ', line: {0}", line); Debug.WriteLine("[StreamingResponse.Exception] " + message); throw new FirebaseEventStreamingException(null, null, message); } eventName = line.Substring(7); dataLine = await sr.ReadLineAsync(); if (OnEventStreamingResponseRaw != null) { OnEventStreamingResponseRaw(this, new EventStreamingResponseRawEventArgs(eventName, dataLine)); } if (EXTRA_DEBUG) { Debug.WriteLine(string.Format("[StreamingResponse.Content] {0} | {1}", line, dataLine)); } #endregion if (eventName == "keep-alive") { } else if (eventName == "put" || eventName == "patch") { if (dataLine.StartsWith("data: ")) { if (OnEventStreamingResponse == null && OnObjectRootPatchReceived == null && OnObjectPropertyPatchReceived == null) { return; // Not watching } #region ----- Read path / data var dataRecord = dataLine.Substring(6); var jData = JObject.Parse(dataRecord); var pathToken = jData["path"]; if (pathToken == null) { var message = "Firebase stream response should contain a token 'path'."; Debug.WriteLine("[StreamingResponse.##### WARNING #####] " + message); throw new FirebaseEventStreamingException(eventName, dataRecord, message); } if (pathToken.Type != JTokenType.String) { var message = "Path token received in Firebase stream response is not of type String."; Debug.WriteLine("[StreamingResponse.##### WARNING #####] " + message); throw new FirebaseEventStreamingException(eventName, dataRecord, message); } JToken dataToken = jData["data"]; if (dataToken == null) { var message = "Firebase stream response should contain a token 'data'."; Debug.WriteLine("[StreamingResponse.##### WARNING #####] " + message); throw new FirebaseEventStreamingException(eventName, dataRecord, message); } var responseData = new EventStreamingResponseData((string)pathToken, dataToken); var jsonDataBeginning = responseData.Data.Substring(0, Math.Min(50, responseData.Data.Length)) + (responseData.Data.Length > 50 ? "..." : ""); Debug.WriteLine(string.Format("[StreamingResponse.{0}] {1}", eventName, responseData.Path)); Debug.WriteLine(string.Format(" ---> {0} <---", jsonDataBeginning)); var response = new EventStreamingResponse(eventName, responseData); if (OnEventStreamingResponse != null) { OnEventStreamingResponse(this, new EventStreamingResponseEventArgs <T>(response)); } #endregion #region ----- Deal with object data received var jsonPatch = _jsonPatchManager.GeneratePatchFrom(response); if (responseData.Path == "/" && eventName == "put") { isFirstTimeRetrieval = isFirstTimeRetrieval == null ? true : false; if (OnObjectRootPatchReceived != null) { // var obj = _serializer.Deserialize<T>(responseData.Data); OnObjectRootPatchReceived(this, jsonPatch, isFirstTimeRetrieval.Value); } } else if (OnObjectPropertyPatchReceived != null) { if (isFirstTimeRetrieval == null) { var message = "Firebase streaming did not emit first time retrieval of object."; Debug.WriteLine("[StreamingResponse.##### WARNING #####] " + message); throw new FirebaseEventStreamingException(eventName, dataRecord, message); } OnObjectPropertyPatchReceived(this, jsonPatch); } #endregion } } else { Debug.WriteLine("[StreamingResponse.Exception] " + string.Format("Firebase stream response not handled. Event: {0} | Data: {1}", eventName, dataLine)); throw new FirebaseEventStreamingException(eventName, dataLine, "Firebase stream response not handled."); } } #endregion ----- While true (end) } // Using sr = StreamReader } // Using content } // Using httpResponse }, cancellationToken : token); // Task.Run } catch (Exception exceptionInLongRunningTask) { if (this.ExceptionHandler == null) { throw; } this.ExceptionHandler(exceptionInLongRunningTask); } }