void ReadDone(IAsyncResult result) { WebConnectionData data = Data; Stream ns = nstream; if (ns == null) { Close(true); return; } int nread = -1; try { nread = ns.EndRead(result); } catch (ObjectDisposedException) { return; } catch (Exception e) { if (e.InnerException is ObjectDisposedException) { return; } HandleError(WebExceptionStatus.ReceiveFailure, e, "ReadDone1"); return; } if (nread == 0) { HandleError(WebExceptionStatus.ReceiveFailure, null, "ReadDone2"); return; } if (nread < 0) { HandleError(WebExceptionStatus.ServerProtocolViolation, null, "ReadDone3"); return; } int pos = -1; nread += position; if (data.ReadState == ReadState.None) { Exception exc = null; try { pos = GetResponse(data, sPoint, buffer, nread); } catch (Exception e) { exc = e; } if (exc != null || pos == -1) { HandleError(WebExceptionStatus.ServerProtocolViolation, exc, "ReadDone4"); return; } } if (data.ReadState == ReadState.Aborted) { HandleError(WebExceptionStatus.RequestCanceled, null, "ReadDone"); return; } if (data.ReadState != ReadState.Content) { int est = nread * 2; int max = (est < buffer.Length) ? buffer.Length : est; byte [] newBuffer = new byte [max]; Buffer.BlockCopy(buffer, 0, newBuffer, 0, nread); buffer = newBuffer; position = nread; data.ReadState = ReadState.None; InitRead(); return; } position = 0; WebConnectionStream stream = new WebConnectionStream(this, data); bool expect_content = ExpectContent(data.StatusCode, data.request.Method); string tencoding = null; if (expect_content) { tencoding = data.Headers ["Transfer-Encoding"]; } chunkedRead = (tencoding != null && tencoding.IndexOf("chunked", StringComparison.OrdinalIgnoreCase) != -1); if (!chunkedRead) { stream.ReadBuffer = buffer; stream.ReadBufferOffset = pos; stream.ReadBufferSize = nread; try { stream.CheckResponseInBuffer(); } catch (Exception e) { HandleError(WebExceptionStatus.ReceiveFailure, e, "ReadDone7"); } } else if (chunkStream == null) { try { chunkStream = new MonoChunkStream(buffer, pos, nread, data.Headers); } catch (Exception e) { HandleError(WebExceptionStatus.ServerProtocolViolation, e, "ReadDone5"); return; } } else { chunkStream.ResetBuffer(); try { chunkStream.Write(buffer, pos, nread); } catch (Exception e) { HandleError(WebExceptionStatus.ServerProtocolViolation, e, "ReadDone6"); return; } } data.stream = stream; if (!expect_content) { stream.ForceCompletion(); } data.request.SetResponseData(data); }
internal async Task InitReadAsync(CancellationToken cancellationToken) { WebConnection.Debug($"{ME} INIT READ ASYNC"); var buffer = new BufferOffsetSize(new byte[4096], false); var state = ReadState.None; int position = 0; while (true) { Operation.ThrowIfClosedOrDisposed(cancellationToken); WebConnection.Debug($"{ME} INIT READ ASYNC LOOP: {state} {position} - {buffer.Offset}/{buffer.Size}"); var nread = await InnerStream.ReadAsync( buffer.Buffer, buffer.Offset, buffer.Size, cancellationToken).ConfigureAwait(false); WebConnection.Debug($"{ME} INIT READ ASYNC LOOP #1: {state} {position} - {buffer.Offset}/{buffer.Size} - {nread}"); if (nread == 0) { throw GetReadException(WebExceptionStatus.ReceiveFailure, null, "ReadDoneAsync2"); } if (nread < 0) { throw GetReadException(WebExceptionStatus.ServerProtocolViolation, null, "ReadDoneAsync3"); } buffer.Offset += nread; buffer.Size -= nread; if (state == ReadState.None) { try { var oldPos = position; if (!GetResponse(buffer, ref position, ref state)) { position = oldPos; } } catch (Exception e) { WebConnection.Debug($"{ME} INIT READ ASYNC FAILED: {e.Message}\n{e}"); throw GetReadException(WebExceptionStatus.ServerProtocolViolation, e, "ReadDoneAsync4"); } } if (state == ReadState.Aborted) { throw GetReadException(WebExceptionStatus.RequestCanceled, null, "ReadDoneAsync5"); } if (state == ReadState.Content) { buffer.Size = buffer.Offset - position; buffer.Offset = position; break; } int est = nread * 2; if (est > buffer.Size) { var newBuffer = new byte [buffer.Buffer.Length + est]; Buffer.BlockCopy(buffer.Buffer, 0, newBuffer, 0, buffer.Offset); buffer = new BufferOffsetSize(newBuffer, buffer.Offset, newBuffer.Length - buffer.Offset, false); } state = ReadState.None; position = 0; } WebConnection.Debug($"{ME} INIT READ ASYNC LOOP DONE: {buffer.Offset} {buffer.Size}"); try { Operation.ThrowIfDisposed(cancellationToken); await Initialize(buffer, cancellationToken).ConfigureAwait(false); } catch (Exception e) { throw GetReadException(WebExceptionStatus.ReceiveFailure, e, "ReadDoneAsync6"); } }
WebHeaderCollection ReadHeaders(Stream stream, out byte [] retBuffer, out int status) { retBuffer = null; status = 200; byte [] buffer = new byte [1024]; MemoryStream ms = new MemoryStream(); while (true) { int n = stream.Read(buffer, 0, 1024); if (n == 0) { HandleError(WebExceptionStatus.ServerProtocolViolation, null, "ReadHeaders"); return(null); } ms.Write(buffer, 0, n); int start = 0; string str = null; bool gotStatus = false; WebHeaderCollection headers = new WebHeaderCollection(); while (ReadLine(ms.GetBuffer(), ref start, (int)ms.Length, ref str)) { if (str == null) { int contentLen = 0; try { contentLen = int.Parse(headers["Content-Length"]); } catch { contentLen = 0; } if (ms.Length - start - contentLen > 0) { // we've read more data than the response header and conents, // give back extra data to the caller retBuffer = new byte[ms.Length - start - contentLen]; Buffer.BlockCopy(ms.GetBuffer(), start + contentLen, retBuffer, 0, retBuffer.Length); } else { // haven't read in some or all of the contents for the response, do so now FlushContents(stream, contentLen - (int)(ms.Length - start)); } return(headers); } if (gotStatus) { headers.Add(str); continue; } string[] parts = str.Split(' '); if (parts.Length < 2) { HandleError(WebExceptionStatus.ServerProtocolViolation, null, "ReadHeaders2"); return(null); } if (String.Compare(parts [0], "HTTP/1.1", true) == 0) { Data.ProxyVersion = HttpVersion.Version11; } else if (String.Compare(parts [0], "HTTP/1.0", true) == 0) { Data.ProxyVersion = HttpVersion.Version10; } else { HandleError(WebExceptionStatus.ServerProtocolViolation, null, "ReadHeaders2"); return(null); } status = (int)UInt32.Parse(parts [1]); if (parts.Length >= 3) { Data.StatusDescription = String.Join(" ", parts, 2, parts.Length - 2); } gotStatus = true; } } }
internal async Task ReadAllAsync(bool resending, CancellationToken cancellationToken) { WebConnection.Debug($"{ME} READ ALL ASYNC: resending={resending} eof={read_eof} total={totalRead} " + "length={contentLength} nextReadCalled={nextReadCalled}"); if (read_eof || totalRead >= contentLength || nextReadCalled) { if (!nextReadCalled) { nextReadCalled = true; Operation.CompleteResponseRead(true); } return; } var timeoutTask = Task.Delay(ReadTimeout); var myReadTcs = new TaskCompletionSource <int> (); while (true) { /* * 'readTcs' is set by ReadAsync(). */ cancellationToken.ThrowIfCancellationRequested(); var oldReadTcs = Interlocked.CompareExchange(ref readTcs, myReadTcs, null); if (oldReadTcs == null) { break; } // ReadAsync() is in progress. var anyTask = await Task.WhenAny(oldReadTcs.Task, timeoutTask).ConfigureAwait(false); if (anyTask == timeoutTask) { throw new WebException("The operation has timed out.", WebExceptionStatus.Timeout); } } WebConnection.Debug($"{ME} READ ALL ASYNC #1"); cancellationToken.ThrowIfCancellationRequested(); try { if (totalRead >= contentLength) { return; } byte[] b = null; int new_size; if (contentLength == Int64.MaxValue && !ChunkedRead) { WebConnection.Debug($"{ME} READ ALL ASYNC - NEITHER LENGTH NOR CHUNKED"); /* * This is a violation of the HTTP Spec - the server neither send a * "Content-Length:" nor a "Transfer-Encoding: chunked" header. * * When we're redirecting or resending for NTLM, then we can simply close * the connection here. * * However, if it's the final reply, then we need to try our best to read it. */ if (resending) { Close(); return; } KeepAlive = false; } if (contentLength == Int64.MaxValue) { MemoryStream ms = new MemoryStream(); BufferOffsetSize buffer = null; if (readBuffer != null && readBuffer.Size > 0) { ms.Write(readBuffer.Buffer, readBuffer.Offset, readBuffer.Size); readBuffer.Offset = 0; readBuffer.Size = readBuffer.Buffer.Length; if (readBuffer.Buffer.Length >= 8192) { buffer = readBuffer; } } if (buffer == null) { buffer = new BufferOffsetSize(new byte[8192], false); } int read; while ((read = await InnerReadAsync(buffer.Buffer, buffer.Offset, buffer.Size, cancellationToken)) != 0) { ms.Write(buffer.Buffer, buffer.Offset, read); } new_size = (int)ms.Length; contentLength = new_size; readBuffer = new BufferOffsetSize(ms.GetBuffer(), 0, new_size, false); } else { new_size = (int)(contentLength - totalRead); b = new byte[new_size]; int readSize = 0; if (readBuffer != null && readBuffer.Size > 0) { readSize = readBuffer.Size; if (readSize > new_size) { readSize = new_size; } Buffer.BlockCopy(readBuffer.Buffer, readBuffer.Offset, b, 0, readSize); } int remaining = new_size - readSize; int r = -1; while (remaining > 0 && r != 0) { r = await InnerReadAsync(b, readSize, remaining, cancellationToken); remaining -= r; readSize += r; } } readBuffer = new BufferOffsetSize(b, 0, new_size, false); totalRead = 0; nextReadCalled = true; myReadTcs.TrySetResult(new_size); } catch (Exception ex) { WebConnection.Debug($"{ME} READ ALL ASYNC EX: {ex.Message}"); myReadTcs.TrySetException(ex); throw; } finally { WebConnection.Debug($"{ME} READ ALL ASYNC #2"); readTcs = null; } Operation.CompleteResponseRead(true); }
async Task <(int, int)> ProcessRead(byte[] buffer, int offset, int size, CancellationToken cancellationToken) { WebConnection.Debug($"{ME} PROCESS READ: {totalRead} {contentLength}"); cancellationToken.ThrowIfCancellationRequested(); if (totalRead >= contentLength) { read_eof = true; contentLength = totalRead; return(0, 0); } int oldBytes = 0; int remaining = readBuffer?.Size ?? 0; if (remaining > 0) { int copy = (remaining > size) ? size : remaining; Buffer.BlockCopy(readBuffer.Buffer, readBuffer.Offset, buffer, offset, copy); readBuffer.Offset += copy; readBuffer.Size -= copy; offset += copy; size -= copy; totalRead += copy; if (totalRead >= contentLength) { contentLength = totalRead; read_eof = true; } if (size == 0 || totalRead >= contentLength) { return(0, copy); } oldBytes = copy; } if (contentLength != Int64.MaxValue && contentLength - totalRead < size) { size = (int)(contentLength - totalRead); } WebConnection.Debug($"{ME} PROCESS READ #1: {oldBytes} {size} {read_eof}"); if (read_eof) { contentLength = totalRead; return(oldBytes, 0); } var ret = await InnerReadAsync(buffer, offset, size, cancellationToken).ConfigureAwait(false); if (ret <= 0) { read_eof = true; contentLength = totalRead; return(oldBytes, 0); } totalRead += ret; return(oldBytes, ret); }
public override IAsyncResult BeginWrite(byte [] buffer, int offset, int size, AsyncCallback cb, object state) { if (request.Aborted) { throw new WebException("The request was canceled.", null, WebExceptionStatus.RequestCanceled); } if (isRead) { throw new NotSupportedException("this stream does not allow writing"); } if (buffer == null) { throw new ArgumentNullException("buffer"); } int length = buffer.Length; if (offset < 0 || length < offset) { throw new ArgumentOutOfRangeException("offset"); } if (size < 0 || (length - offset) < size) { throw new ArgumentOutOfRangeException("size"); } if (sendChunked) { lock (locker) { pendingWrites++; pending.Reset(); } } WebAsyncResult result = new WebAsyncResult(cb, state); if (!sendChunked) { CheckWriteOverflow(request.ContentLength, totalWritten, size); } if (allowBuffering && !sendChunked) { if (writeBuffer == null) { writeBuffer = new MemoryStream(); } writeBuffer.Write(buffer, offset, size); totalWritten += size; if (request.ContentLength > 0 && totalWritten == request.ContentLength) { try { result.AsyncWriteAll = true; result.InnerAsyncResult = WriteRequestAsync(new AsyncCallback(WriteRequestAsyncCB), result); if (result.InnerAsyncResult == null) { if (!result.IsCompleted) { result.SetCompleted(true, 0); } result.DoCallback(); } } catch (Exception exc) { result.SetCompleted(true, exc); result.DoCallback(); } } else { result.SetCompleted(true, 0); result.DoCallback(); } return(result); } AsyncCallback callback = null; if (cb != null) { callback = cb_wrapper; } if (sendChunked) { WriteRequest(); string cSize = String.Format("{0:X}\r\n", size); byte [] head = Encoding.ASCII.GetBytes(cSize); int chunkSize = 2 + size + head.Length; byte [] newBuffer = new byte [chunkSize]; Buffer.BlockCopy(head, 0, newBuffer, 0, head.Length); Buffer.BlockCopy(buffer, offset, newBuffer, head.Length, size); Buffer.BlockCopy(crlf, 0, newBuffer, head.Length + size, crlf.Length); buffer = newBuffer; offset = 0; size = chunkSize; } try { result.InnerAsyncResult = cnc.BeginWrite(request, buffer, offset, size, callback, result); } catch (Exception) { if (!IgnoreIOErrors) { throw; } result.SetCompleted(true, 0); result.DoCallback(); } totalWritten += size; return(result); }
public override IAsyncResult BeginRead(byte [] buffer, int offset, int size, AsyncCallback cb, object state) { if (!isRead) { throw new NotSupportedException("this stream does not allow reading"); } if (buffer == null) { throw new ArgumentNullException("buffer"); } int length = buffer.Length; if (offset < 0 || length < offset) { throw new ArgumentOutOfRangeException("offset"); } if (size < 0 || (length - offset) < size) { throw new ArgumentOutOfRangeException("size"); } lock (locker) { pendingReads++; pending.Reset(); } WebAsyncResult result = new WebAsyncResult(cb, state, buffer, offset, size); if (totalRead >= contentLength) { result.SetCompleted(true, -1); result.DoCallback(); return(result); } int remaining = readBufferSize - readBufferOffset; if (remaining > 0) { int copy = (remaining > size) ? size : remaining; Buffer.BlockCopy(readBuffer, readBufferOffset, buffer, offset, copy); readBufferOffset += copy; offset += copy; size -= copy; totalRead += copy; if (size == 0 || totalRead >= contentLength) { result.SetCompleted(true, copy); result.DoCallback(); return(result); } result.NBytes = copy; } if (cb != null) { cb = cb_wrapper; } if (contentLength != Int32.MaxValue && contentLength - totalRead < size) { size = contentLength - totalRead; } if (!read_eof) { result.InnerAsyncResult = cnc.BeginRead(request, buffer, offset, size, cb, result); } else { result.SetCompleted(true, result.NBytes); result.DoCallback(); } return(result); }
internal void ReadAll() { if (!isRead || read_eof || totalRead >= contentLength || nextReadCalled) { if (isRead && !nextReadCalled) { nextReadCalled = true; cnc.NextRead(); } return; } pending.WaitOne(); lock (locker) { if (totalRead >= contentLength) { return; } byte [] b = null; int diff = readBufferSize - readBufferOffset; int new_size; if (contentLength == Int32.MaxValue) { MemoryStream ms = new MemoryStream(); byte [] buffer = null; if (readBuffer != null && diff > 0) { ms.Write(readBuffer, readBufferOffset, diff); if (readBufferSize >= 8192) { buffer = readBuffer; } } if (buffer == null) { buffer = new byte [8192]; } int read; while ((read = cnc.Read(request, buffer, 0, buffer.Length)) != 0) { ms.Write(buffer, 0, read); } b = ms.GetBuffer(); new_size = (int)ms.Length; contentLength = new_size; } else { new_size = contentLength - totalRead; b = new byte [new_size]; if (readBuffer != null && diff > 0) { if (diff > new_size) { diff = new_size; } Buffer.BlockCopy(readBuffer, readBufferOffset, b, 0, diff); } int remaining = new_size - diff; int r = -1; while (remaining > 0 && r != 0) { r = cnc.Read(request, b, diff, remaining); remaining -= r; diff += r; } } readBuffer = b; readBufferOffset = 0; readBufferSize = new_size; totalRead = 0; nextReadCalled = true; } cnc.NextRead(); }