예제 #1
0
        async Task WriteAsyncInner(byte[] buffer, int offset, int size,
                                   WebCompletionSource completion,
                                   CancellationToken cancellationToken)
        {
            try {
                await ProcessWrite(buffer, offset, size, cancellationToken).ConfigureAwait(false);

                WebConnection.Debug($"{ME} WRITE ASYNC #1: {allowBuffering} {sendChunked} {Request.ContentLength} {totalWritten}");

                if (Request.ContentLength > 0 && totalWritten == Request.ContentLength)
                {
                    await FinishWriting(cancellationToken);
                }

                pendingWrite = null;
                completion.TrySetCompleted();
            } catch (Exception ex) {
                KillBuffer();
                closed = true;

                WebConnection.Debug($"{ME} WRITE ASYNC EX: {ex.Message}");

                if (ex is SocketException)
                {
                    ex = new IOException("Error writing request", ex);
                }

                Operation.CompleteRequestWritten(this, ex);

                pendingWrite = null;
                completion.TrySetException(ex);
                throw;
            }
        }
예제 #2
0
        public override async Task <int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            WebConnection.Debug($"{ME} READ ASYNC");

            cancellationToken.ThrowIfCancellationRequested();

            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            int length = buffer.Length;

            if (offset < 0 || length < offset)
            {
                throw new ArgumentOutOfRangeException(nameof(offset));
            }
            if (count < 0 || (length - offset) < count)
            {
                throw new ArgumentOutOfRangeException(nameof(count));
            }

            if (Interlocked.CompareExchange(ref nestedRead, 1, 0) != 0)
            {
                throw new InvalidOperationException("Invalid nested call.");
            }

            var completion = new WebCompletionSource();

            while (!cancellationToken.IsCancellationRequested)
            {
                /*
                 * 'currentRead' is set by ReadAllAsync().
                 */
                var oldCompletion = Interlocked.CompareExchange(ref pendingRead, completion, null);
                WebConnection.Debug($"{ME} READ ASYNC #1: {oldCompletion != null}");
                if (oldCompletion == null)
                {
                    break;
                }
                await oldCompletion.WaitForCompletion().ConfigureAwait(false);
            }

            WebConnection.Debug($"{ME} READ ASYNC #2");

            int       nbytes  = 0;
            Exception throwMe = null;

            try {
                nbytes = await ProcessRead(buffer, offset, count, cancellationToken).ConfigureAwait(false);
            } catch (Exception e) {
                throwMe = GetReadException(WebExceptionStatus.ReceiveFailure, e, "ReadAsync");
            }

            WebConnection.Debug($"{ME} READ ASYNC #3: {nbytes} {throwMe?.Message}");

            if (throwMe != null)
            {
                lock (locker) {
                    completion.TrySetException(throwMe);
                    pendingRead = null;
                    nestedRead  = 0;
                }

                closed = true;
                Operation.Finish(false, throwMe);
                throw throwMe;
            }

            lock (locker) {
                completion.TrySetCompleted();
                pendingRead = null;
                nestedRead  = 0;
            }

            if (nbytes <= 0 && !read_eof)
            {
                read_eof = true;

                if (!nextReadCalled)
                {
                    WebConnection.Debug($"{ME} READ ASYNC - READ COMPLETE: {nbytes} - {nextReadCalled}");
                    if (!nextReadCalled)
                    {
                        nextReadCalled = true;
                        Operation.Finish(true);
                    }
                }
            }

            return(nbytes);
        }
예제 #3
0
        internal async Task ReadAllAsync(bool resending, CancellationToken cancellationToken)
        {
            WebConnection.Debug($"{ME} READ ALL ASYNC: resending={resending} eof={read_eof} " +
                                "nextReadCalled={nextReadCalled}");
            if (read_eof || bufferedEntireContent || nextReadCalled)
            {
                if (!nextReadCalled)
                {
                    nextReadCalled = true;
                    Operation.Finish(true);
                }
                return;
            }

            var completion = new WebCompletionSource();
            var timeoutCts = new CancellationTokenSource();

            try {
                var timeoutTask = Task.Delay(ReadTimeout, timeoutCts.Token);
                while (true)
                {
                    /*
                     * 'currentRead' is set by ReadAsync().
                     */
                    cancellationToken.ThrowIfCancellationRequested();
                    var oldCompletion = Interlocked.CompareExchange(ref pendingRead, completion, null);
                    if (oldCompletion == null)
                    {
                        break;
                    }

                    // ReadAsync() is in progress.
                    var oldReadTask = oldCompletion.WaitForCompletion();
                    var anyTask     = await Task.WhenAny(oldReadTask, timeoutTask).ConfigureAwait(false);

                    if (anyTask == timeoutTask)
                    {
                        throw new WebException("The operation has timed out.", WebExceptionStatus.Timeout);
                    }
                }
            } finally {
                timeoutCts.Cancel();
                timeoutCts.Dispose();
            }

            WebConnection.Debug($"{ME} READ ALL ASYNC #1");

            try {
                cancellationToken.ThrowIfCancellationRequested();

                /*
                 * We may have awaited on the 'readTcs', so check
                 * for eof again as ReadAsync() may have set it.
                 */
                if (read_eof || bufferedEntireContent)
                {
                    return;
                }

                /*
                 * Simplify: if we're resending on a new connection,
                 * then we can simply close the connection here.
                 */
                if (resending && !KeepAlive)
                {
                    Close();
                    return;
                }

                var buffer = await ReadAllAsyncInner(cancellationToken).ConfigureAwait(false);

                var bos = new BufferOffsetSize(buffer, 0, buffer.Length, false);
                innerStream = new BufferedReadStream(Operation, null, bos);

                nextReadCalled = true;
                completion.TrySetCompleted();
            } catch (Exception ex) {
                WebConnection.Debug($"{ME} READ ALL ASYNC EX: {ex.Message}");
                completion.TrySetException(ex);
                throw;
            } finally {
                WebConnection.Debug($"{ME} READ ALL ASYNC #2");
                pendingRead = null;
            }

            Operation.Finish(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.Finish(true);
                }
                return;
            }

            var completion = new WebCompletionSource();
            var timeoutCts = new CancellationTokenSource();

            try {
                var timeoutTask = Task.Delay(ReadTimeout, timeoutCts.Token);
                while (true)
                {
                    /*
                     * 'currentRead' is set by ReadAsync().
                     */
                    cancellationToken.ThrowIfCancellationRequested();
                    var oldCompletion = Interlocked.CompareExchange(ref pendingRead, completion, null);
                    if (oldCompletion == null)
                    {
                        break;
                    }

                    // ReadAsync() is in progress.
                    var oldReadTask = oldCompletion.WaitForCompletion();
                    var anyTask     = await Task.WhenAny(oldReadTask, timeoutTask).ConfigureAwait(false);

                    if (anyTask == timeoutTask)
                    {
                        throw new WebException("The operation has timed out.", WebExceptionStatus.Timeout);
                    }
                }
            } finally {
                timeoutCts.Cancel();
                timeoutCts.Dispose();
            }

            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;
                completion.TrySetCompleted();
            } catch (Exception ex) {
                WebConnection.Debug($"{ME} READ ALL ASYNC EX: {ex.Message}");
                completion.TrySetException(ex);
                throw;
            } finally {
                WebConnection.Debug($"{ME} READ ALL ASYNC #2");
                pendingRead = null;
            }

            Operation.Finish(true);
        }
        public override async Task <int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            WebConnection.Debug($"{ME} READ ASYNC");

            cancellationToken.ThrowIfCancellationRequested();

            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            int length = buffer.Length;

            if (offset < 0 || length < offset)
            {
                throw new ArgumentOutOfRangeException(nameof(offset));
            }
            if (count < 0 || (length - offset) < count)
            {
                throw new ArgumentOutOfRangeException(nameof(count));
            }

            if (Interlocked.CompareExchange(ref nestedRead, 1, 0) != 0)
            {
                throw new InvalidOperationException("Invalid nested call.");
            }

            var completion = new WebCompletionSource();

            while (!cancellationToken.IsCancellationRequested)
            {
                /*
                 * 'currentRead' is set by ReadAllAsync().
                 */
                var oldCompletion = Interlocked.CompareExchange(ref pendingRead, completion, null);
                WebConnection.Debug($"{ME} READ ASYNC #1: {oldCompletion != null}");
                if (oldCompletion == null)
                {
                    break;
                }
                await oldCompletion.WaitForCompletion().ConfigureAwait(false);
            }

            WebConnection.Debug($"{ME} READ ASYNC #2: {totalRead} {contentLength}");

            int       oldBytes = 0, nbytes = 0;
            Exception throwMe = null;

            try {
                // FIXME: NetworkStream.ReadAsync() does not support cancellation.
                (oldBytes, nbytes) = await HttpWebRequest.RunWithTimeout(
                    ct => ProcessRead(buffer, offset, count, ct),
                    ReadTimeout, () => {
                    Operation.Abort();
                    InnerStream.Dispose();
                }, () => Operation.Aborted, cancellationToken).ConfigureAwait(false);
            } catch (Exception e) {
                throwMe = GetReadException(WebExceptionStatus.ReceiveFailure, e, "ReadAsync");
            }

            WebConnection.Debug($"{ME} READ ASYNC #3: {totalRead} {contentLength} - {oldBytes} {nbytes} {throwMe?.Message}");

            if (throwMe != null)
            {
                lock (locker) {
                    completion.TrySetException(throwMe);
                    pendingRead = null;
                    nestedRead  = 0;
                }

                closed = true;
                Operation.Finish(false, throwMe);
                throw throwMe;
            }

            lock (locker) {
                pendingRead.TrySetCompleted();
                pendingRead = null;
                nestedRead  = 0;
            }

            if (totalRead >= contentLength && !nextReadCalled)
            {
                WebConnection.Debug($"{ME} READ ASYNC - READ COMPLETE: {oldBytes} {nbytes} - {totalRead} {contentLength} {nextReadCalled}");
                if (!nextReadCalled)
                {
                    nextReadCalled = true;
                    Operation.Finish(true);
                }
            }

            return(oldBytes + nbytes);
        }