/// <summary> /// Informs that the client is disposed /// </summary> /// <param name="reason">indicating the reason</param> public void ClientDisposed(EndOfResponsesReason reason) { if (this.queue.ProcessedRequestsCount <= 0) { this.GenerateFaultMessage(reason); this.eom = true; this.completeEvent.Set(); } }
/// <summary> /// Throw session exception according to the reason if needed /// </summary> /// <param name="reason">indicating the reason</param> private static void ThrowIfNeed(EndOfResponsesReason reason) { switch (reason) { case EndOfResponsesReason.ClientPurged: throw SessionBase.ClientPurgedException; case EndOfResponsesReason.ClientTimeout: throw SessionBase.ClientTimeoutException; } }
/// <summary> /// generate the fault message when the session failed. /// </summary> private void GenerateFaultMessage(EndOfResponsesReason reason) { XmlDocument doc = new XmlDocument() { XmlResolver = null }; Message soap11FaultMessage = null; switch (reason) { case EndOfResponsesReason.ClientPurged: soap11FaultMessage = FrontEndFaultMessage.GenerateFaultMessage(null, MessageVersion.Soap11, SOAFaultCode.ClientPurged, SR.ClientPurged); break; case EndOfResponsesReason.ClientTimeout: soap11FaultMessage = FrontEndFaultMessage.GenerateFaultMessage(null, MessageVersion.Soap11, SOAFaultCode.ClientTimeout, SR.ClientTimeout); break; } XPathNavigator nav = soap11FaultMessage.CreateBufferedCopy(BrokerEntry.MaxMessageSize).CreateNavigator(); doc.Load(nav.ReadSubtree()); this.messages.Add(doc.DocumentElement); }
/// <summary> /// reply the end of message to the client. /// </summary> /// <param name="callback">the response service callback.</param> /// <param name="clientData">the client data.</param> /// <param name="clientPurged">indicating the client purged flag</param> private void ReplyEndOfMessage(IResponseServiceCallback callback, string clientData, EndOfResponsesReason reason) { if (this.callbackChannelDisposed) { return; } BrokerTracing.TraceInfo("[GetResponsesHandler] Client {0}: Send end of response, clientPurged = {1}", this.clientId, reason); this.ResetTimeout(); TypedMessageConverter converter = TypedMessageConverter.Create(typeof(EndOfResponses), Constant.EndOfMessageAction); EndOfResponses endOfResponses = new EndOfResponses(); endOfResponses.Count = this.ResponsesCount; endOfResponses.Reason = reason; Message eom = converter.ToMessage(endOfResponses, this.Version); eom.Headers.Add(MessageHeader.CreateHeader(Constant.ResponseCallbackIdHeaderName, Constant.ResponseCallbackIdHeaderNS, clientData)); this.eorReplied = true; try { if (callback is AzureQueueProxy) { callback.SendResponse(eom, clientData); } else { callback.SendResponse(eom); } } catch (ObjectDisposedException) { BrokerTracing.TraceEvent(System.Diagnostics.TraceEventType.Error, 0, "[BrokerClient] Client {0}: Send end of response error: communication object is disposed.", this.clientId); this.callbackChannelDisposed = true; this.Queue.ResetResponsesCallback(); } catch (Exception ce) { BrokerTracing.TraceEvent(System.Diagnostics.TraceEventType.Error, 0, "[BrokerClient] Client {0}: Send end of response error: {1}", this.clientId, ce); // Swallow exception } }
/// <summary> /// Receives new response messages from broker /// </summary> /// <param name="response">Response message</param> void IResponseServiceCallback.SendResponse(Message response) { MessageBuffer messageBuffer = null; EndOfResponses endOfResponses = null; Exception clientSideException = null; bool isEOM = false; // Reset the heartbeat since operation just succeeded this.session.ResetHeartbeat(); try { // Check whether this is an EOM message isEOM = Utility.IsEOM(response); if (!isEOM) { // If not EOM, create a copy of the message's buffer so it can be deserialized // A copy is needed because WCF will dispose this message when the callback returns // Alternatively we could deserialize the entire message but we want // callback to be quick and we may wind up deserializing messages the user // never looks at (i.e. just checks user data or exits enum early) try { messageBuffer = response.CreateBufferedCopy(Constant.MaxBufferSize); } catch (Exception e) { SessionBase.TraceSource.TraceEvent(TraceEventType.Error, 0, "[Session:{0}][BrokerResponseEnumerator] Failed to create message buffer from the response message: {1}", this.session.Id, e); clientSideException = e; } } else { // If EOM, deserialize EndOfResponses message endOfResponses = (EndOfResponses)this.endOfResponsesConverter.FromMessage(response); } string messageId = String.Empty; try { messageId = SoaHelper.GetMessageId(response).ToString(); } catch { // Swallow possible ObjectDisposedException } SessionBase.TraceSource.TraceEvent(TraceEventType.Verbose, 0, "[Session:{0}][BrokerResponseEnumerator] Response received from broker. IsEOM = {1}, MessageId = {2}", this.session.Id, isEOM, messageId); lock (this.responsesLock) { // If the enumerator is disposing or disposed, return if (this.isDisposeCalled) { return; } // If this is not an EOM message if (!isEOM) { string actionFromResponse = BrokerClientBase.GetActionFromResponseMessage(response); // Save the response in the current window MessageWindowItem messageItem = new MessageWindowItem( messageBuffer, actionFromResponse, !response.IsFault ? response.Headers.Action : String.Empty, response.Headers.RelatesTo == null ? SoaHelper.GetMessageId(response) : response.Headers.RelatesTo); // Bug #15946: handling client side exception thrown from WebResponseHandler(when talking to rest service) // To propagate the client side exception to BrokerResponseEnumerator api, store the exception in MessageWindowItem if (string.Equals(response.Headers.Action, Constant.WebAPI_ClientSideException, StringComparison.Ordinal)) { clientSideException = response.Properties[Constant.WebAPI_ClientSideException] as Exception; } messageItem.CarriedException = clientSideException; this.currentReceiveWindow.Enqueue(messageItem); // Increment the current number of responses this.currentResponseCount++; // Lower number of outstanding requests if (Interlocked.Decrement(ref this.outstandingRequestCount) == 0) { try { this.GetMoreResponses(false, ResponseWindowSize); } catch (Exception e) { SessionBase.TraceSource.TraceEvent(TraceEventType.Error, 0, "[Session:{0}][BrokerResponseEnumerator] Failed to get more responses in SendResponse context: {1}", this.session.Id, e); // Send broker down signal since connection to broker might be bad ((IResponseServiceCallback)this).SendBrokerDownSignal(false); } } // If the current window is full or there are no more responses if (this.currentReceiveWindow.Count == ResponseWindowSize || this.currentResponseCount == this.totalResponseCount) { // Disable the flush timer this.flushResponsesTimer.Change(Timeout.Infinite, Timeout.Infinite); // Immediately flush the receive window this.FlushCurrentReceiveWindow(); } // Otherwise reset the flushtimer to the full interval else { this.flushResponsesTimer.Change(this.flushResponsesTimerInterval, Timeout.Infinite); } } else { // If we already received an EOM, ignore any others. We may get multiple EOM since // more responses are requested until we get EOM. This can lead to overrequesting. if (this.totalResponseCount != -1) { return; } // Save total response count this.totalResponseCount = endOfResponses.Count; this.endOfResponsesReason = endOfResponses.Reason; // If there are no more responses if (this.currentResponseCount == this.totalResponseCount) { // Make the current receive window available to enumerator if it has responses if (this.currentReceiveWindow.Count != 0) { this.responsesWindows.Enqueue(this.currentReceiveWindow); this.currentReceiveWindow = new Queue <MessageWindowItem>(); } // Notify enumerator that a new window is available. Notify even if // its empty since it allows MoveNext to exit and return there are no // more items this.newResponseWindowOrEOM.Set(); } } } } catch (Exception e) { // If we get an exception here log and dispose the enum this.Dispose(); SessionBase.TraceSource.TraceEvent(TraceEventType.Error, 0, "[Session:{0}][BrokerResponseEnumerator] Unhandled exception in response enumerator callback :{1}", this.session.Id, e); } }