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
                ThreadPool.QueueUserWorkItem(new WaitCallback(this.ReceiveChunkLoop), helper);
                return(currentInputMessage);
            }
            else
            {
                this.currentMessageCompleted.Set();
                return(message);
            }
        }
        void ReceiveChunkLoop(object state)
        {
            TimeoutHelper timeouthelper = (TimeoutHelper)state;
            //receive loop on a worker thread
            Message message = null;

            while (this.State == CommunicationState.Opened)
            {
                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
                ProcessReceivedChunk(message);

                if (IsLastChunk(message))
                {
                    //we're done with the current message
                    this.currentInputMessage = null;
                    this.currentMessageCompleted.Set();
                    //stop this chunk receive loop
                    //will start again when above layer calls Receive and a new message is received
                    break;
                }
            }
            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);
        }