예제 #1
0
        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);
        }
예제 #2
0
        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");
            }
        }
예제 #3
0
        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;
                }
            }
        }
예제 #4
0
        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);
        }
예제 #5
0
        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);
        }
예제 #6
0
        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);
        }
예제 #7
0
        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);
        }
예제 #8
0
        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();
        }