示例#1
0
        public override int EndRead(IAsyncResult asyncResult)
        {
            GlobalLog.Print("TlsStream#" + ValidationHelper.HashString(this) + "::EndRead() IAsyncResult#" + ValidationHelper.HashString(asyncResult));
            // on earlier error throw an exception
            if (InnerException != null)
            {
                throw InnerException;
            }
            // after shutdown/Close throw an exception
            if (m_ShutDown > 0)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }
            //
            // parameter validation
            //
            Interlocked.Decrement(ref m_NestCounter);

            if (asyncResult == null)
            {
                throw new ArgumentNullException("asyncResult");
            }
            NestedSingleAsyncResult castedAsyncResult = asyncResult as NestedSingleAsyncResult;

            if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this)
            {
                throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult));
            }
            if (castedAsyncResult.EndCalled)
            {
                throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndRead"));
            }

            castedAsyncResult.InternalWaitForCompletion();
            castedAsyncResult.EndCalled = true;

            if (m_ArrivingData == null)
            {
                return(0);
            }

            int tranferredData = TransferData(castedAsyncResult.Buffer, castedAsyncResult.Offset, castedAsyncResult.Size);

            return(tranferredData);
        }
示例#2
0
        /*++
            BeginReadWithoutValidation - Read from the connection.

            internal version of BeginRead above, without validation

            This method reads from the network, or our internal buffer if there's
            data in that. If there's not, we'll read from the network. If we're
            doing chunked decoding, we'll decode it before returning from this
            call.


            Input:

                buffer          - Buffer to read into.
                offset          - Offset in buffer to read into.
                size           - Size in bytes to read.

            Returns:
                Nothing.

        --*/

        private IAsyncResult BeginReadWithoutValidation(byte[] buffer, int offset, int size, AsyncCallback callback, object state) {
            GlobalLog.Enter("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginReadWithoutValidation", ValidationHelper.HashString(m_Connection) + ", " + offset.ToString() + ", " + size.ToString());
            GlobalLog.ThreadContract(ThreadKinds.Unknown, "ConnectStream#" + ValidationHelper.HashString(this) + "::BeginReadWithoutValidation");

            //
            // Figure out how much we should really read.
            //

            int bytesToRead = 0;

            if (m_Chunked) {
                if (!m_ChunkEofRecvd) {
                    return m_ChunkParser.ReadAsync(this, buffer, offset, size, callback, state);
                }
            }
            else {

                //
                // Not doing chunked, so don't read more than is left.
                //

                if (m_ReadBytes != -1) {
                    bytesToRead = (int)Math.Min(m_ReadBytes, (long)size);
                }
                else {
                    bytesToRead = size;
                }
            }

            // If we're not going to read anything, either because they're
            // not asking for anything or there's nothing left, bail
            // out now.

            if (bytesToRead == 0 || this.Eof) {
                NestedSingleAsyncResult completedResult = new NestedSingleAsyncResult(this, state, callback, ZeroLengthRead);
                GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginReadWithoutValidation() completed, bytesToRead: " + bytesToRead + " Eof: " + this.Eof.ToString());
                return completedResult;
            }

            try
            {
                int bytesAlreadyRead = 0;
                if (m_ReadBufferSize > 0)
                {
                    bytesAlreadyRead = FillFromBufferedData(buffer, ref offset, ref bytesToRead);
                    if (bytesToRead == 0)
                    {
                        NestedSingleAsyncResult completedResult = new NestedSingleAsyncResult(this, state, callback, bytesAlreadyRead);
                        GlobalLog.Leave("ConnectStream::BeginReadWithoutValidation");
                        return completedResult;
                    }
                }

                if (ErrorInStream)
                {
                    GlobalLog.LeaveException("ConnectStream::BeginReadWithoutValidation", m_ErrorException);
                    throw m_ErrorException;
                }

                GlobalLog.Assert(m_DoneCalled == 0 || m_ReadBytes != -1, "BeginRead: Calling BeginRead after ReadDone.|m_DoneCalled > 0 && m_ReadBytes == -1");

                // Keep track of this during the read so it can be added back at the end.
                m_BytesAlreadyTransferred = bytesAlreadyRead;

                IAsyncResult asyncResult = m_Connection.BeginRead(buffer, offset, bytesToRead, callback, state);

                // a null return indicates that the connection was closed underneath us.
                if (asyncResult == null)
                {
                    m_BytesAlreadyTransferred = 0;
                    m_ErrorException = new WebException(
                        NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled),
                        WebExceptionStatus.RequestCanceled);

                    GlobalLog.LeaveException("ConnectStream::BeginReadWithoutValidation", m_ErrorException);
                    throw m_ErrorException;
                }

                GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginReadWithoutValidation() called BeginRead");
                return asyncResult;
            }
            catch (Exception exception) {
                IOError(exception);
                GlobalLog.LeaveException("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginReadWithoutValidation", exception);
                throw;
            }
        }
示例#3
0
        public override IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback asyncCallback, object asyncState)
        {
            GlobalLog.Print("TlsStream#" + ValidationHelper.HashString(this) + "::BeginRead() SecureChannel#" + ValidationHelper.HashString(SecureChannel) + " offset:" + offset.ToString() + " size:" + size.ToString());
            // after shutdown/Close throw an exception
            if (m_ShutDown > 0)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }
            // on earlier error throw an exception
            if (InnerException != null)
            {
                throw InnerException;
            }
            //
            // parameter validation
            //
            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }
            if (offset < 0 || offset > buffer.Length)
            {
                throw new ArgumentOutOfRangeException("offset");
            }
            if (size < 0 || size > buffer.Length - offset)
            {
                throw new ArgumentOutOfRangeException("size");
            }

            // cannot handle nested BeginRead, BeginRead calls without corrsponding EndRead
            //GlobalLog.Assert(Interlocked.Increment( ref m_NestCounter ) == 1,
            //    "TlsStream::BeginRead m_NestCounter!=1, nesting not allowed of BeginRead",
            //    m_NestCounter.ToString());

            if (Interlocked.Increment(ref m_NestCounter) != 1)
            {
                throw new ArgumentException("TlsStream");
            }

            NestedSingleAsyncResult asyncResult = new NestedSingleAsyncResult(this, asyncState, asyncCallback, buffer, offset, size);

            // check if there is already data present
            if (m_ExistingAmount > 0)
            {
                GlobalLog.Print("BeginReceive: data already present in buffer!");

                // In this case no I/O is performed-- data is copied
                // from internal buffer to the user space

                asyncResult.InvokeCallback(true);
            }
            else
            {
                // otherwise we have to read data from the network and decrypt
                // to preserve asynchronous mode, the I/O operation will be
                // qued as a work item for the runtime thread pool

                GlobalLog.Print("BeginReceive: must issue read from network");

                SecureChannel chkSecureChannel = SecureChannel;
                if (chkSecureChannel == null)
                {
                    //this object was disposed from other thread
                    throw new ObjectDisposedException(this.GetType().FullName);
                }
                int bufferLength = chkSecureChannel.HeaderSize;
                m_AsyncResponseBuffer = new byte[bufferLength];
                try {
                    asyncResult.NestedAsyncResult = base.BeginRead(
                        m_AsyncResponseBuffer,
                        0,
                        bufferLength,
                        new AsyncCallback(AsyncReceiveComplete),
                        asyncResult
                        );
                } catch (Exception exception) {
                    GlobalLog.Print("TlsStream#" + ValidationHelper.HashString(this) + "::BeginRead() exception: " + exception);
                    InnerException = new IOException(SR.GetString(SR.net_io_readfailure), exception);
                    throw;
                }
                if (asyncResult.NestedAsyncResult == null)
                {
                    GlobalLog.Print("TlsStream#" + ValidationHelper.HashString(this) + "::BeginRead(): base.BeginRead() returns null");
                }
            }
            return(asyncResult);
        }
示例#4
0
        /*++
            BeginWrite - Does async write to the Stream

            Splits the operation into two outcomes, for the
            non-chunking case, we calculate size to write,
            then call BeginWrite on the Connection directly,
            and then we're finish, for the Chunked case,
            we procede with use two callbacks to continue the
            chunking after the first write, and then second write.
            In order that all of the Chunk data/header/terminator,
            in the correct format are sent.

            Input:

                buffer          - Buffer to write into.
                offset          - Offset in buffer to write into.
                size           - Size in bytes to write.
                callback        - the callback to be called on result
                state           - object to be passed to callback

            Returns:
                IAsyncResult    - the async result

        --*/

        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state ) {
            GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginWrite " + ValidationHelper.HashString(m_Connection) + ", " + offset.ToString() + ", " + size.ToString());
            //
            // parameter validation
            //
            if (!m_WriteStream) {
                throw new NotSupportedException(SR.GetString(SR.net_readonlystream));
            }
            if (buffer==null) {
                throw new ArgumentNullException("buffer");
            }
            if (offset<0 || offset>buffer.Length) {
                throw new ArgumentOutOfRangeException("offset");
            }
            if (size<0 || size>buffer.Length-offset) {
                throw new ArgumentOutOfRangeException("size");
            }
            //
            // if we have a stream error, or we've already shut down this socket
            //  then we must prevent new BeginRead/BeginWrite's from getting
            //  submited to the socket, since we've already closed the stream.
            //
            if (ErrorInStream) {
                GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginWrite() throwing:" + m_ErrorException.ToString());
                throw m_ErrorException;
            }
            if (m_ShutDown == 1 && !IgnoreSocketWrite) {
                GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginWrite() throwing");
                throw new WebException(
                            NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.ConnectionClosed),
                            WebExceptionStatus.ConnectionClosed);
            }
            //
            // if we fail/hang this call for some reason,
            // this Nesting count we be non-0, so that when we
            // close this stream, we will abort the socket.
            //

            Interlocked.Increment( ref m_CallNesting );
            GlobalLog.Print("Inc: " + m_CallNesting.ToString());

            //
            // buffer data to the ScatterGatherBuffers
            // regardles of chunking, we buffer the data as if we were not chunking
            // and on resubmit, we don't bother chunking.
            //
            if (m_WriteBufferEnable) {
                //
                // if we don't need to, we shouldn't send data on the wire as well
                // but in this case we gave a stream to the user so we have transport
                //
                BufferedData.Write(buffer, offset, size);
            }

            if (BufferOnly || IgnoreSocketWrite) {
                //
                // We're not putting this data on the wire
                //
                NestedSingleAsyncResult asyncResult = new NestedSingleAsyncResult(this, state, callback, buffer, offset, size);

                //
                // done. complete the IO.
                //
                asyncResult.InvokeCallback(true);
                GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginWrite() BufferOnly");
                return asyncResult;
            }
            else if (WriteChunked) {
                //
                // We're chunking. Write the chunk header out first,
                // then the data, then a CRLF.
                // for this we'll use BeginMultipleSend();
                //
                int chunkHeaderOffset = 0;
                byte[] chunkHeaderBuffer = GetChunkHeader(size, out chunkHeaderOffset);

                BufferOffsetSize[] buffers = new BufferOffsetSize[3];
                buffers[0] = new BufferOffsetSize(chunkHeaderBuffer, chunkHeaderOffset, chunkHeaderBuffer.Length - chunkHeaderOffset, false);
                buffers[1] = new BufferOffsetSize(buffer, offset, size, false);
                buffers[2] = new BufferOffsetSize(m_CRLF, 0, m_CRLF.Length, false);

                NestedMultipleAsyncResult asyncResult = new NestedMultipleAsyncResult(this, state, callback, buffers);
                //
                // See if we're chunking. If not, make sure we're not sending too much data.
                //
                if (size == 0) {
                    asyncResult.InvokeCallback(true);

                    GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginWrite");
                    return asyncResult;
                }

                //
                // after setting up the buffers and error checking do the async Write Call
                //
                try {
                    asyncResult.NestedAsyncResult =
                        m_Connection.BeginMultipleWrite(
                            buffers,
                            m_WriteCallbackDelegate,
                            asyncResult );
                } catch (Exception exception) {
                    if (m_ErrorResponseStatus) {
                        // We already got a error response, hence server could drop the connection,
                        // Here we are recovering for future (optional) resubmit ...
                        m_IgnoreSocketWrite = true;
                        GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginWrite() IGNORE write fault");
                    }
                    else {
                        // Note we could swallow this since receive callback is already posted and
                        // should give us similar failure
                        IOError(exception);
                        GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginWrite() throwing:" + exception.ToString());
                        throw;
                    }
                }

                if (asyncResult.NestedAsyncResult == null) {
                    //
                    // we're buffering, IO is complete
                    //
                    asyncResult.InvokeCallback(true);
                }

                GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginWrite chunked");
                return asyncResult;
            }
            else {
                //
                // We're not chunking. See if we're sending too much; if not,
                // go ahead and write it.
                //
                NestedSingleAsyncResult asyncResult = new NestedSingleAsyncResult(this, state, callback, buffer, offset, size);
                //
                // See if we're chunking. If not, make sure we're not sending too much data.
                //
                if (size == 0) {
                    asyncResult.InvokeCallback(true);

                    GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginWrite");
                    return asyncResult;
                }

                if (BytesLeftToWrite != -1) {
                    //
                    // but only check if we aren't writing to an unknown content-length size,
                    // as we can be buffering.
                    //
                    if (BytesLeftToWrite < (long)size) {
                        //
                        // writing too much data.
                        //
                        GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginWrite()");
                        throw new ProtocolViolationException(SR.GetString(SR.net_entitytoobig));
                    }
                    //
                    // Otherwise update our bytes left to send and send it.
                    //
                    BytesLeftToWrite -= (long)size;
                }

                // we're going to nest this asyncResult below
                asyncResult.Nested = true;

                //
                // After doing, the m_WriteByte size calculations, and error checking
                //  here doing the async Write Call
                //

                try {
                    asyncResult.NestedAsyncResult =
                        m_Connection.BeginWrite(
                            buffer,
                            offset,
                            size,
                            m_WriteCallbackDelegate,
                            asyncResult );
                } catch (Exception exception) {
                    if (m_ErrorResponseStatus) {
                        // We already got a error response, hence server could drop the connection,
                        // Here we are recovering for future (optional) resubmit ...
                        m_IgnoreSocketWrite = true;
                        GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginWrite() IGNORE write fault");
                    }
                    else {
                        // Note we could swallow this since receive callback is already posted and
                        // should give us similar failure
                        IOError(exception);
                        GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginWrite() throwing:" + exception.ToString());
                        throw;
                    }
                }

                if (asyncResult.NestedAsyncResult == null) {
                    //
                    // we're buffering, IO is complete
                    //
                    asyncResult.InvokeCallback(true);
                }

                GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginWrite");
                return asyncResult;
            }
        }
示例#5
0
        //
        // calls the other version of FillFromBufferedData and returns
        // true if we've filled the entire buffer
        //
        private bool FillFromBufferedData(
            NestedSingleAsyncResult castedAsyncResult,
            byte[] buffer,
            ref int offset,
            ref int size ) {

            GlobalLog.Enter("FillFromBufferedData", offset.ToString() + ", " + size.ToString());

            // attempt to pull data from Buffer first.
            int BytesTransferred = FillFromBufferedData(buffer, ref offset, ref size);

            castedAsyncResult.BytesTransferred += BytesTransferred;

            if (size == 0) {
                GlobalLog.Leave("FillFromBufferedData", true);
                return true;
            }

            GlobalLog.Leave("FillFromBufferedData", false);
            return false;
        }
示例#6
0
        /*++
            EndReadWithoutValidation - Finishes off the Read for the Connection
                Called internally by EndRead.

            This method completes the async call created from BeginRead,
            it attempts to determine how many bytes were actually read,
            and if any errors occured.

            Input:
                asyncResult - created by BeginRead

            Returns:
                int - size of bytes read, or < 0 on error

        --*/
        private int EndReadWithoutValidation(NestedSingleAsyncResult castedAsyncResult) {
            GlobalLog.Enter("ConnectStream#" + ValidationHelper.HashString(this) + "::EndReadWithoutValidation", ValidationHelper.HashString(castedAsyncResult));
            //
            // Wait & get Response
            //
            object returnValue = castedAsyncResult.InternalWaitForCompletion();

            if (ErrorInStream) {
                GlobalLog.LeaveException("ConnectStream::EndReadWithoutValidation", m_ErrorException);
                throw m_ErrorException;
            }

            int BytesTransferred = 0;

            if (m_Chunked) {

                if (returnValue is Exception) {
                    IOError((Exception) returnValue);
                    BytesTransferred = -1;
                } else {
                    if (castedAsyncResult.Nested) {

                        BytesTransferred = -1;

                        if (returnValue is int) {
                            BytesTransferred = (int)returnValue;
                        } // otherwise its an exception ?

                        if (BytesTransferred < 0) {
                            IOError();
                            BytesTransferred = 0;
                        }
                    }

                    BytesTransferred += castedAsyncResult.BytesTransferred;

                    m_ChunkSize -= BytesTransferred;
                }

            } else {

                //
                // we're not chunking, a note about error
                //  checking here, in some cases due to 1.0
                //  servers we need to read until 0 bytes,
                //  or a server reset, therefore, we may need
                //  ignore sockets errors
                //

                bool DoneReading = false;

                // if its finished without async, just use what was read already from the buffer,
                // otherwise we call the Connection's EndRead to find out
                if (castedAsyncResult.Nested) {

                    BytesTransferred = -1;

                    if (returnValue is int) {
                        BytesTransferred = (int)returnValue;
                    }

                    //
                    // return the actual bytes read from the wire + plus possible bytes read from buffer
                    //
                    if (BytesTransferred < 0) {
                        //
                        // Had some sort of error on the read. Close
                        // the stream, which will clean everything up.
                        // Sometimes a server will abruptly drop the connection,
                        // if we don't have content-length and chunk info, then it may not be an error
                        //

                        if (m_ReadBytes != -1) {
                            IOError();
                        }
                        else {
                            DoneReading = true;
                            //
                            // Note,
                            //  we should probaly put more logic in here, as to when to actually
                            //  except this an error and when to just silently close
                            //
                            BytesTransferred = 0;
                        }
                    }

                    if (BytesTransferred == 0) {
                        //
                        // We read 0 bytes from the connection. This is OK if we're
                        // reading to end, it's an error otherwise.
                        //
                        if (m_ReadBytes != -1) {
                            IOError();
                        }
                        else {
                            //
                            // We're reading to end, and we found the end, by reading 0 bytes
                            //
                            DoneReading = true;
                        }
                    }
                }

                BytesTransferred += castedAsyncResult.BytesTransferred;

                //
                // Not chunking. Update our read bytes state and return what we've read.
                //
                if (m_ReadBytes != -1) {
                    m_ReadBytes -= BytesTransferred;

                    GlobalLog.Assert(
                                m_ReadBytes >= 0,
                                "ConnectStream: Attempting to read more bytes than avail",
                                "m_ReadBytes < 0"
                                );

                    GlobalLog.Print("m_ReadBytes = "+m_ReadBytes);
                }

                if (m_ReadBytes == 0 || DoneReading) {
                    // We're all done reading, tell the connection that.
                    m_ReadBytes = 0;

                    //
                    // indicate to cache that read completed OK
                    //

                    CallDone();
                }
            }

            GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::EndRead", BytesTransferred);
            return BytesTransferred;
        }
示例#7
0
        /*++
            InternalBeginRead

            This is an interal version of BeginRead without validation,
             that is called from the Chunked code as well the normal codepaths.

            Input:

               castedAsyncResult - IAsyncResult generated from BeginRead

            Returns:

               None

        --*/

        private void InternalBeginRead(int bytesToRead, NestedSingleAsyncResult castedAsyncResult, bool fromCallback) {
            GlobalLog.Enter("ConnectStream::InternalBeginRead", bytesToRead.ToString()+", "+ValidationHelper.HashString(castedAsyncResult)+", "+fromCallback.ToString());

            //
            // Attempt to fill in our entired read from,
            //  data previously buffered, if this completely
            //  satisfies us, then we are done, complete sync
            //

            bool completedSync = (m_ReadBufferSize > 0) ?
                FillFromBufferedData(
                    castedAsyncResult,
                    castedAsyncResult.Buffer,
                    ref castedAsyncResult.Offset,
                    ref bytesToRead ) : false;

            if (completedSync) {
                castedAsyncResult.InvokeCallback(!fromCallback, bytesToRead);
                GlobalLog.Leave("ConnectStream::InternalBeginRead");
                return;
            }

            // we're going to nest this asyncResult below
            // this is needed because a callback may be invoked
            // before the call itsefl returns to set NestedAsyncResult

            castedAsyncResult.Nested = true;

            //
            // otherwise, we need to read more data from the connection.
            //

            if (ErrorInStream) {
                GlobalLog.LeaveException("ConnectStream::InternalBeginRead", m_ErrorException);
                throw m_ErrorException;
            }

            GlobalLog.Assert(
                (m_DoneCalled == 0 || m_ReadBytes != -1),
                "BeginRead: Calling BeginRead after ReadDone", "(m_DoneCalled > 0 && m_ReadBytes == -1)");

            castedAsyncResult.NestedAsyncResult =
                m_Connection.BeginRead(
                    castedAsyncResult.Buffer,
                    castedAsyncResult.Offset,
                    bytesToRead,
                    m_ReadCallbackDelegate,
                    castedAsyncResult );

            // a null return indicates that the connection was closed underneath us.
            if (castedAsyncResult.NestedAsyncResult == null) {
                m_ErrorException =
                    new WebException(
                            NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled),
                            WebExceptionStatus.RequestCanceled);

                GlobalLog.LeaveException("ConnectStream::InternalBeginRead", m_ErrorException);
                throw m_ErrorException;
            }

            GlobalLog.Leave("ConnectStream::InternalBeginRead");
        }
示例#8
0
        /*++
            BeginReadWithoutValidation - Read from the connection.

            internal version of BeginRead above, without validation

            This method reads from the network, or our internal buffer if there's
            data in that. If there's not, we'll read from the network. If we're
            doing chunked decoding, we'll decode it before returning from this
            call.


            Input:

                buffer          - Buffer to read into.
                offset          - Offset in buffer to read into.
                size           - Size in bytes to read.

            Returns:
                Nothing.

        --*/

        private IAsyncResult BeginReadWithoutValidation(byte[] buffer, int offset, int size, AsyncCallback callback, object state) {
            GlobalLog.Enter("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginRead", ValidationHelper.HashString(m_Connection) + ", " + offset.ToString() + ", " + size.ToString());

            //
            // Figure out how much we should really read.
            //

            NestedSingleAsyncResult castedAsyncResult = new NestedSingleAsyncResult(this, state, callback, buffer, offset, size);

            int bytesToRead = 0;

            if (m_Chunked) {

                if (!m_ChunkEofRecvd) {

                    // See if we have more left from a previous
                    // chunk.

                    if (m_ChunkSize != 0) {
                        bytesToRead = Math.Min(size, m_ChunkSize);
                    } else {
                        // otherwise we queue a work item to parse the chunk
                        // Consider:
                        // Will we have an issue of thread dying off if we make a IO Read from that thread???
                        ThreadPool.QueueUserWorkItem(
                                                    m_ReadChunkedCallbackDelegate,
                                                    castedAsyncResult
                                                    );

                        GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginRead");
                        return castedAsyncResult;
                    }

                }
            } else {

                //
                // Not doing chunked, so don't read more than is left.
                //

                if (m_ReadBytes != -1) {
                    bytesToRead = Math.Min((int)m_ReadBytes, size);
                }
                else {
                    bytesToRead = size;
                }
            }

            // If we're not going to read anything, either because they're
            // not asking for anything or there's nothing left, bail
            // out now.

            if (bytesToRead == 0 || this.Eof) {
                castedAsyncResult.InvokeCallback(true, 0);
                GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginRead");
                return castedAsyncResult;
            }

            try {
                InternalBeginRead(bytesToRead, castedAsyncResult, false);
            }
            catch (Exception exception) {
                IOError(exception);
                GlobalLog.LeaveException("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginRead", exception);
                throw;
            }

            GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginRead");
            return castedAsyncResult;
        }