private void HttpGetResponseThread() { try { // List<IAsyncResult> results = new List<IAsyncResult>(); bool isEOM = false; // ProcessResponseDelegate p = ProcessResponses; int responseCount = 0; // while (!isEOM) // { SessionBase.TraceSource.TraceInformation("Begin PullResponse : count {0} : clientId {1}", this.count, this.clientId); BrokerResponseMessages responseMessages = this.controller.PullResponses(this.action, this.resetToBegin, this.count, this.clientId); SessionBase.TraceSource.TraceInformation("End PullResponse : count {0} : isEOM {1}", responseMessages.SOAPMessage.Length, responseMessages.EOM); // responseCount += responseMessages.SOAPMessage.Length; responseCount = responseMessages.SOAPMessage.Length; // results.Add(p.BeginInvoke(responseMessages, clientData, null, null)); this.ProcessResponses(responseMessages, this.clientData); Interlocked.Add(ref this.responseClient.totalResponseCount, responseCount); isEOM = responseMessages.EOM; if (isEOM) { // construct endofreponses message TypedMessageConverter converter = TypedMessageConverter.Create(typeof(EndOfResponses), Constant.EndOfMessageAction); EndOfResponses endOfResponses = new EndOfResponses(); endOfResponses.Count = this.responseClient.totalResponseCount; endOfResponses.Reason = EndOfResponsesReason.Success; Message eom = converter.ToMessage(endOfResponses, MessageVersion.Soap11); eom.Headers.Add(MessageHeader.CreateHeader(Constant.ResponseCallbackIdHeaderName, Constant.ResponseCallbackIdHeaderNS, this.clientData)); this.callback.SendResponse(eom); } } catch (Exception e) { SessionBase.TraceSource.TraceInformation("PullResponse Exception: {0}", e.ToString()); if (this.disposeFlag) { return; } Message exceptionMessage = Message.CreateMessage(MessageVersion.Soap11, @"http://hpc.microsoft.com/ClientSideExeption"); exceptionMessage.Headers.Add(MessageHeader.CreateHeader(Constant.ResponseCallbackIdHeaderName, Constant.ResponseCallbackIdHeaderNS, this.clientData)); exceptionMessage.Properties.Add(@"HttpClientException", e); this.callback.SendResponse(exceptionMessage); } finally { if (this.Completed != null) { this.Completed(this, EventArgs.Empty); } } }
/// <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 response messages from broker's response service /// </summary> /// <param name="message">Response message</param> public void SendResponse(Message message) { try { int currentResponseCount = 0; // Reset the heartbeat since operation just succeeded this.session.ResetHeartbeat(); // Reset timeout timer since a response was received if (this.responseHandlerTimeoutTimer != null) { this.responseHandlerTimeoutTimer.Change(this.responseHanderTimeout, 0); } // Bug #15946: handling client side exception thrown from WebResponseHandler(when talking to rest service) if (string.Equals(message.Headers.Action, Constant.WebAPI_ClientSideException, StringComparison.Ordinal)) { Exception e = message.Properties[Constant.WebAPI_ClientSideException] as Exception; this.InvokeCallback(new BrokerResponse <TMessage>(e, message.Headers.RelatesTo)); return; } // TODO: Consider whether user callback should get an EOM message if (!Utility.IsEOM(message)) { // If the handler is closed, timed out, closed or broker heartbeat signaled, dont forward any more requests to the user. // NOTE: If some responses already slipped through its OK because we prefer that over adding a lock here if (this.shuttingDown) { return; } // Create a BrokerResponse object wrapper for the message object. A copy of the message must be created MessageBuffer messageBuffer = null; try { messageBuffer = message.CreateBufferedCopy(Constant.MaxBufferSize); } catch (Exception e) { Utility.LogError("AsyncResponseCallback.SendResponse received exception - {0}", e); this.InvokeCallback(new BrokerResponse <TMessage>(e, message.Headers.RelatesTo)); return; } BrokerResponse <TMessage> brokerResponse = this.CreateResponse(BrokerClientBase.GetActionFromResponseMessage(message), !message.IsFault ? message.Headers.Action : String.Empty, messageBuffer, message.Headers.RelatesTo == null ? SoaHelper.GetMessageId(message) : message.Headers.RelatesTo); BrokerResponse <TMessage> lastResponse = null; if (this.ignoreIsLastResponseProperty) { lastResponse = brokerResponse; } else { // Atomically swap out the last response. lastResponse = Interlocked.Exchange <BrokerResponse <TMessage> >(ref this.lastBrokerResponse, brokerResponse); } // If there was a previous last response if (lastResponse != null) { // Send it to the callback this.InvokeCallback(lastResponse); // Increment response count currentResponseCount = Interlocked.Increment(ref this.currentResponseCount); } // If the caller wants all messages at once, do so. // Else we always request ResponseWindowSize * 2 messages. If we get to // the end of a window, request another if (this.responseWindowSize != Constant.GetResponse_All && currentResponseCount != 0 && (currentResponseCount + 1) % this.responseWindowSize == 0) { // Call async version and block on completion in order to workaround System.Net.Socket bug #750028 try { SessionBase.TraceSource.TraceInformation("GetResponse : currentResponseCount {0} : clientId {1}", currentResponseCount, this.clientId); this.responseService.GetResponses( this.action, this.callbackManagerId, GetResponsePosition.Current, this.responseWindowSize, this.clientId); } catch (FaultException <SessionFault> e) { throw Utility.TranslateFaultException(e); } } } // If this is a client purged fault, return that exception in a BrokerResponse else { TypedMessageConverter endOfResponsesConverter = TypedMessageConverter.Create(typeof(EndOfResponses), Constant.EndOfMessageAction); EndOfResponses endOfResponses = (EndOfResponses)endOfResponsesConverter.FromMessage(message); switch (endOfResponses.Reason) { case EndOfResponsesReason.ClientPurged: this.InvokeCallback(new BrokerResponse <TMessage>(SessionBase.ClientPurgedException, new UniqueId(Guid.Empty))); break; case EndOfResponsesReason.ClientTimeout: this.InvokeCallback(new BrokerResponse <TMessage>(SessionBase.ClientTimeoutException, new UniqueId(Guid.Empty))); break; default: // Save how many responses are expected (minus 1 for the last response) System.Diagnostics.Debug.Assert(endOfResponses.Count > 0, "Response count should also be positive number"); this.expectedResponseCount = endOfResponses.Count - 1; break; } } if (!this.ignoreIsLastResponseProperty) { // Check to see if we receive the EOM message and all the responses. // If so pass last response to callback marked as such. Also make // sure this isnt called again with a lock. if (this.expectedResponseCount == this.currentResponseCount) { lock (this.lastResponseLock) { if (this.expectedResponseCount == this.currentResponseCount) { // Mark the response as the last one this.lastBrokerResponse.isLastResponse = true; // Send last response to callback. Note since currentResponseCount // is incremented after responses are sent to the callback, the // callback will not be processing any other responses at this time. this.InvokeCallback(this.lastBrokerResponse); this.expectedResponseCount = -1; } } } } } catch (Exception e) { // Log and eat unhandled user exceptions from their calback Utility.LogError("Unhandled exception processing response - {0}", e); } }
/// <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); } }