public Message Receive(TimeSpan timeout) { ThrowIfDisposedOrNotOpen(); //if we're receiving chunks, wait till that's done TimeoutHelper helper = new TimeoutHelper(timeout); if (!this.currentMessageCompleted.WaitOne(TimeoutHelper.ToMilliseconds(timeout), false)) { throw new TimeoutException("Receive timed out waiting for previous message receive to complete"); } //call receive on inner channel Message message = innerChannel.Receive(helper.RemainingTime()); if (message != null && message.Headers.Action == ChunkingUtils.ChunkAction) { this.currentInputMessage = GetNewChunkingMessage(message, helper); //start receiving chunks again this.currentMessageCompleted.Reset(); this.stopReceive = false; ThreadPool.QueueUserWorkItem(new WaitCallback(this.ReceiveChunkLoop), helper); return(currentInputMessage); } else { return(message); } }
void ReceiveChunkLoop(object state) { TimeoutHelper timeouthelper = (TimeoutHelper)state; //receive loop on a worker thread Message message = null; do { //don't pass default timeout so that we can react to stopReceive //which will be set from Close message = innerChannel.Receive(timeouthelper.RemainingTime()); if (message == null) { //a null message should not have been chunked to begin with throw new CommunicationException("A null chunk message was received. Null message indicates the end of a session so a null chunk should never be received"); } //add this chunk to current message //this can throw a TimeoutException if ProcessReceivedChunk(message); if (IsLastChunk(message)) { //we're done with the current message this.currentMessageCompleted.Set(); this.currentInputMessage = null; //stop this chunk receive loop //will start again when above layer calls Receive and a new message is received this.stopReceive = true; } //stopReceive is Set if stopClose is called or if we received the endChunk message //also stop if we received a null message indicating the session was closed } while (!stopReceive && message != null); receiveStopped.Set(); }
ChunkingMessage GetNewChunkingMessage(Message receivedMessage, TimeoutHelper timeoutHelper) { if (receivedMessage == null) { //end of session return(null); } Guid messageId = GetMessageId(receivedMessage); if (this.currentInputMessage != null && messageId == this.currentInputMessage.MessageId) { throw new InvalidOperationException("A new ChunkingMessage was requested but the received message's id matches the current message id. The received message is a chunk of the current message"); } ChunkingReader reader = new ChunkingReader(receivedMessage, this.maxBufferedChunks, timeoutHelper); string originalAction = receivedMessage.Headers.GetHeader <string>( ChunkingUtils.OriginalAction, ChunkingUtils.ChunkNs); ChunkingMessage dechunkedMessage = new ChunkingMessage(receivedMessage.Version, originalAction, reader, messageId); string addressingNs = ChunkingUtils.GetAddressingNamespace(receivedMessage.Version); //read original headers from body of startingChunk and add them to headers of dechunkedMessage for (int i = 0; i < receivedMessage.Headers.Count; i++) { MessageHeaderInfo headerInfo = receivedMessage.Headers[i]; if (headerInfo.Namespace != ChunkingUtils.ChunkNs && !(headerInfo.Name == "Action" && headerInfo.Namespace == addressingNs)) { //not a chunking header and not the chunking wsa:Action header //copy it to dechunkedMessage dechunkedMessage.Headers.CopyHeaderFrom( receivedMessage.Headers, i); } } return(dechunkedMessage); }