/*++ RemoveTrailers This handles possible trailer headers that are found after the last chunk. Currently we throw them away for this version. Input: ReadByteBuffer - Returns: None - throws on exception --*/ private void RemoveTrailers(StreamChunkBytes ReadByteBuffer) { while (m_TempBuffer[0] != '\r' && m_TempBuffer[1] != '\n') { int BytesRead = ChunkParse.SkipPastCRLF(ReadByteBuffer); if (BytesRead <= 0) { throw new IOException(SR.GetString(SR.net_io_readfailure, SR.GetString(SR.net_io_connectionclosed))); } ReadCRLF(m_TempBuffer); } }
/*++ ProcessReadChunkedSize This is a continuation of the ReadChunkedCallback, and is used to parse out the size of a chunk Input: TheByteRead - single byte read from wire to process castedAsyncResult - Async Chunked State information Returns: None --*/ private int ProcessReadChunkedSize(StreamChunkBytes ReadByteBuffer) { GlobalLog.Enter("ConnectStream#" + ValidationHelper.HashString(this) + "::ProcessReadChunkedSize"); // now get the chunk size. int chunkSize; int BytesRead = ChunkParse.GetChunkSize(ReadByteBuffer, out chunkSize); if (BytesRead <= 0) { GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::ProcessReadChunkedSize - error"); throw new IOException(SR.GetString(SR.net_io_readfailure, SR.GetString(SR.net_io_connectionclosed))); } // Now skip past and extensions and the CRLF. BytesRead = ChunkParse.SkipPastCRLF(ReadByteBuffer); if (BytesRead <= 0) { GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::ProcessReadChunkedSize - error"); throw new IOException(SR.GetString(SR.net_io_readfailure, SR.GetString(SR.net_io_connectionclosed))); } GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::ProcessReadChunkedSize", chunkSize); return chunkSize; }
/*++ ReadChunkedCallback This is callback, that parses and does a chunked read. It is here that we attempt to Read enough bytes to determine the size of the next chunk of data, and parse through any headers/control information asscoiated with that chunk. Input: asyncResult - IAsyncResult generated from ConnectStream.BeginRead --*/ private static void ReadChunkedCallback(object state) { #if DEBUG GlobalLog.SetThreadSource(ThreadKinds.Worker); using (GlobalLog.SetThreadKind(ThreadKinds.System | ThreadKinds.Sync)) { #endif // ********** WARNING - updating logic below should also be updated in ReadChunkedSync ***************** NestedSingleAsyncResult castedAsyncResult = state as NestedSingleAsyncResult; ConnectStream thisConnectStream = castedAsyncResult.AsyncObject as ConnectStream; GlobalLog.Enter("ConnectStream#" + ValidationHelper.HashString(thisConnectStream) + "::ReadChunkedCallback", ValidationHelper.HashString(castedAsyncResult)); try { if (!thisConnectStream.m_Draining && thisConnectStream.IsClosed) { // throw on shutdown only if we're not draining the socket. Exception exception = new WebException( NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.ConnectionClosed), WebExceptionStatus.ConnectionClosed); castedAsyncResult.InvokeCallback(exception); GlobalLog.LeaveException("ReadChunkedCallback", exception); return; } else if (thisConnectStream.m_ErrorException!=null) { // throw on IO error even if we're draining the socket. castedAsyncResult.InvokeCallback(thisConnectStream.m_ErrorException); GlobalLog.LeaveException("ReadChunkedCallback", thisConnectStream.m_ErrorException); return; } if (thisConnectStream.m_ChunkedNeedCRLFRead) { thisConnectStream.ReadCRLF(thisConnectStream.m_TempBuffer); thisConnectStream.m_ChunkedNeedCRLFRead = false; } StreamChunkBytes ReadByteBuffer = new StreamChunkBytes(thisConnectStream); // We need to determine size of next chunk, // by carefully reading, byte by byte thisConnectStream.m_ChunkSize = thisConnectStream.ProcessReadChunkedSize(ReadByteBuffer); // If this isn't a zero length chunk, read it. if (thisConnectStream.m_ChunkSize != 0) { thisConnectStream.m_ChunkedNeedCRLFRead = true; int bytesToRead = Math.Min(castedAsyncResult.Size, thisConnectStream.m_ChunkSize); // // Attempt to fill in our entired read from, // data previously buffered, if this completely // satisfies us, then we are done, complete sync // int bytesAlreadyRead = 0; if (thisConnectStream.m_ReadBufferSize > 0) { bytesAlreadyRead = thisConnectStream.FillFromBufferedData(castedAsyncResult.Buffer, ref castedAsyncResult.Offset, ref bytesToRead); if (bytesToRead == 0) { castedAsyncResult.InvokeCallback(bytesAlreadyRead); GlobalLog.Leave("ConnectStream::ReadChunkedCallback"); return; } } // // otherwise, we need to read more data from the connection. // if (thisConnectStream.ErrorInStream) { GlobalLog.LeaveException("ConnectStream::ReadChunkedCallback", thisConnectStream.m_ErrorException); throw thisConnectStream.m_ErrorException; } GlobalLog.Assert(thisConnectStream.m_DoneCalled == 0 || thisConnectStream.m_ReadBytes != -1, "ConnectStream::ReadChunkedCallback|Calling BeginRead after ReadDone."); // Keep track of this during the read so it can be added back at the end. thisConnectStream.m_BytesAlreadyTransferred = bytesAlreadyRead; IAsyncResult asyncResult = thisConnectStream.m_Connection.BeginRead(castedAsyncResult.Buffer, castedAsyncResult.Offset, bytesToRead, m_ReadCallbackDelegate, castedAsyncResult); // a null return indicates that the connection was closed underneath us. if (asyncResult == null) { thisConnectStream.m_BytesAlreadyTransferred = 0; thisConnectStream.m_ErrorException = new WebException( NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled), WebExceptionStatus.RequestCanceled); GlobalLog.LeaveException("ConnectStream::ReadChunkedCallback", thisConnectStream.m_ErrorException); throw thisConnectStream.m_ErrorException; } } else { // We've found the terminating 0 length chunk. We may be very well looking // at an extension footer or the very final CRLF. thisConnectStream.ReadCRLF(thisConnectStream.m_TempBuffer); thisConnectStream.RemoveTrailers(ReadByteBuffer); // Remember that we've found this, so we don't try and dechunk // more. thisConnectStream.m_ReadBytes = 0; thisConnectStream.m_ChunkEofRecvd = true; thisConnectStream.CallDone(); // we're done reading, return 0 bytes castedAsyncResult.InvokeCallback(0); } GlobalLog.Leave("ReadChunkedCallback"); } catch (Exception exception) { if (NclUtilities.IsFatal(exception)) throw; castedAsyncResult.InvokeCallback(exception); GlobalLog.LeaveException("ConnectStream::ReadChunkedCallback", exception); } // ********** WARNING - updating logic above should also be updated in ReadChunkedSync ***************** #if DEBUG } #endif }
/*++ ReadChunkedSync Parses and does a chunked read. It is here that we attempt to Read enough bytes to determine the size of the next chunk of data, and parse through any headers/control information asscoiated with that chunk. Returns: None --*/ private int ReadChunkedSync(byte[] buffer, int offset, int size) { GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::ReadChunkedSync"); // ********** WARNING - updating logic below should also be updated in ReadChunkedCallback ***************** if (!m_Draining && IsClosed) { // throw on shutdown only if we're not draining the socket. Exception exception = new WebException( NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.ConnectionClosed), WebExceptionStatus.ConnectionClosed); throw exception; } else if (m_ErrorException!=null) { // throw on IO error even if we're draining the socket. throw m_ErrorException; } if (m_ChunkedNeedCRLFRead) { ReadCRLF(m_TempBuffer); m_ChunkedNeedCRLFRead = false; } StreamChunkBytes ReadByteBuffer = new StreamChunkBytes(this); // We need to determine size of next chunk, // by carefully reading, byte by byte m_ChunkSize = ProcessReadChunkedSize(ReadByteBuffer); // If this isn't a zero length chunk, read it. if (m_ChunkSize != 0) { m_ChunkedNeedCRLFRead = true; return InternalRead(buffer, offset, Math.Min(size, m_ChunkSize)); } else { // We've found the terminating 0 length chunk. We may be very well looking // at an extension footer or the very final CRLF. ReadCRLF(m_TempBuffer); RemoveTrailers(ReadByteBuffer); // Remember that we've found this, so we don't try and dechunk // more. m_ReadBytes = 0; m_ChunkEofRecvd = true; CallDone(); // we're done reading, return 0 bytes return 0; } // ********** WARNING - updating logic above should also be updated in ReadChunkedAsync ***************** }
/*++ ReadChunkedCallback This is callback, that parses and does a chunked read. It is here that we attempt to Read enough bytes to determine the size of the next chunk of data, and parse through any headers/control information asscoiated with that chunk. Input: asyncResult - IAsyncResult generated from ConnectStream.BeginRead Returns: None --*/ private static void ReadChunkedCallback(object state) { NestedSingleAsyncResult castedAsyncResult = state as NestedSingleAsyncResult; ConnectStream thisConnectStream = castedAsyncResult.AsyncObject as ConnectStream; GlobalLog.Enter("ConnectStream#" + ValidationHelper.HashString(thisConnectStream) + "::ReadChunkedCallback", ValidationHelper.HashString(castedAsyncResult)); try { if (!thisConnectStream.m_Draining && thisConnectStream.m_ShutDown==1) { // throw on shutdown only if we're not draining the socket. Exception exception = new WebException( NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.ConnectionClosed), WebExceptionStatus.ConnectionClosed); castedAsyncResult.InvokeCallback(false, exception); GlobalLog.LeaveException("ReadChunkedCallback", exception); return; } else if (thisConnectStream.m_ErrorException!=null) { // throw on IO error even if we're draining the socket. castedAsyncResult.InvokeCallback(false, thisConnectStream.m_ErrorException); GlobalLog.LeaveException("ReadChunkedCallback", thisConnectStream.m_ErrorException); return; } if (thisConnectStream.m_ChunkedNeedCRLFRead) { thisConnectStream.ReadCRLF(thisConnectStream.m_TempBuffer); thisConnectStream.m_ChunkedNeedCRLFRead = false; } StreamChunkBytes ReadByteBuffer = new StreamChunkBytes(thisConnectStream); // We need to determine size of next chunk, // by carefully reading, byte by byte thisConnectStream.m_ChunkSize = thisConnectStream.ProcessReadChunkedSize(ReadByteBuffer); // If this isn't a zero length chunk, read it. if (thisConnectStream.m_ChunkSize != 0) { thisConnectStream.m_ChunkedNeedCRLFRead = true; thisConnectStream.InternalBeginRead(Math.Min(castedAsyncResult.Size, thisConnectStream.m_ChunkSize), castedAsyncResult, true); } else { // We've found the terminating 0 length chunk. We may be very well looking // at an extension footer or the very final CRLF. thisConnectStream.ReadCRLF(thisConnectStream.m_TempBuffer); thisConnectStream.RemoveTrailers(ReadByteBuffer); // Remember that we've found this, so we don't try and dechunk // more. thisConnectStream.m_ReadBytes = 0; thisConnectStream.m_ChunkEofRecvd = true; thisConnectStream.CallDone(); // we're done reading, return 0 bytes castedAsyncResult.InvokeCallback(false, 0); } GlobalLog.Leave("ReadChunkedCallback"); } catch (Exception exception) { castedAsyncResult.InvokeCallback(false, exception); GlobalLog.LeaveException("ReadChunkedCallback", exception); } }