예제 #1
0
        /// <summary>
        ///     relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers
        ///     as prefix
        ///     Usefull for websocket requests
        ///     Asynchronous Programming Model, which does not throw exceptions when the socket is closed
        /// </summary>
        /// <param name="clientStream"></param>
        /// <param name="serverStream"></param>
        /// <param name="bufferSize"></param>
        /// <param name="onDataSend"></param>
        /// <param name="onDataReceive"></param>
        /// <param name="cancellationTokenSource"></param>
        /// <param name="exceptionFunc"></param>
        /// <returns></returns>
        internal static async Task SendRawApm(Stream clientStream, Stream serverStream,
                                              IBufferPool bufferPool, int bufferSize,
                                              Action <byte[], int, int> onDataSend, Action <byte[], int, int> onDataReceive,
                                              CancellationTokenSource cancellationTokenSource,
                                              ExceptionHandler exceptionFunc)
        {
            var taskCompletionSource = new TaskCompletionSource <bool>();

            cancellationTokenSource.Token.Register(() => taskCompletionSource.TrySetResult(true));

            // Now async relay all server=>client & client=>server data
            var clientBuffer = bufferPool.GetBuffer(bufferSize);
            var serverBuffer = bufferPool.GetBuffer(bufferSize);

            try
            {
                beginRead(clientStream, serverStream, clientBuffer, onDataSend, cancellationTokenSource, exceptionFunc);
                beginRead(serverStream, clientStream, serverBuffer, onDataReceive, cancellationTokenSource,
                          exceptionFunc);
                await taskCompletionSource.Task;
            }
            finally
            {
                bufferPool.ReturnBuffer(clientBuffer);
                bufferPool.ReturnBuffer(serverBuffer);
            }
        }
예제 #2
0
        /// <summary>
        /// Writes a byte to the current position in the stream and advances the position within the stream by one byte.
        /// </summary>
        /// <param name="value">The byte to write to the stream.</param>
        public override void WriteByte(byte value)
        {
            if (closedWrite)
            {
                return;
            }

            var buffer = bufferPool.GetBuffer();

            try
            {
                buffer[0] = value;
                OnDataWrite(buffer, 0, 1);
                baseStream.Write(buffer, 0, 1);
            }
            catch
            {
                closedWrite = true;
                if (!isNetworkStream)
                {
                    throw;
                }
            }
            finally
            {
                bufferPool.ReturnBuffer(buffer);
            }
        }
예제 #3
0
        /// <summary>
        /// Writes a byte to the current position in the stream and advances the position within the stream by one byte.
        /// </summary>
        /// <param name="value">The byte to write to the stream.</param>
        public override void WriteByte(byte value)
        {
            var buffer = bufferPool.GetBuffer(BufferSize);

            try
            {
                buffer[0] = value;
                OnDataWrite(buffer, 0, 1);
                baseStream.Write(buffer, 0, 1);
            }
            finally
            {
                bufferPool.ReturnBuffer(buffer);
            }
        }
        /// <summary>
        ///     Copy streams asynchronously
        /// </summary>
        /// <param name="input"></param>
        /// <param name="output"></param>
        /// <param name="onCopy"></param>
        /// <param name="bufferPool"></param>
        /// <param name="bufferSize"></param>
        /// <param name="cancellationToken"></param>
        internal static async Task CopyToAsync(this Stream input, Stream output, Action <byte[], int, int> onCopy,
                                               IBufferPool bufferPool, int bufferSize, CancellationToken cancellationToken)
        {
            var buffer = bufferPool.GetBuffer(bufferSize);

            try
            {
                while (!cancellationToken.IsCancellationRequested)
                {
                    // cancellation is not working on Socket ReadAsync
                    // https://github.com/dotnet/corefx/issues/15033
                    int num = await input.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None)
                              .withCancellation(cancellationToken);

                    int bytesRead;
                    if ((bytesRead = num) != 0 && !cancellationToken.IsCancellationRequested)
                    {
                        await output.WriteAsync(buffer, 0, bytesRead, CancellationToken.None);

                        onCopy?.Invoke(buffer, 0, bytesRead);
                    }
                    else
                    {
                        break;
                    }
                }
            }
            finally
            {
                bufferPool.ReturnBuffer(buffer);
            }
        }
예제 #5
0
        /// <summary>
        ///     Determines whether the stream starts with the given string.
        /// </summary>
        /// <returns>
        ///     1: when starts with the given string, 0: when valid HTTP method, -1: otherwise
        /// </returns>
        private static async Task <int> startsWith(ICustomStreamReader clientStreamReader, IBufferPool bufferPool, string expectedStart, CancellationToken cancellationToken = default)
        {
            const int lengthToCheck = 10;

            byte[] buffer = null;
            try
            {
                if (bufferPool.BufferSize < lengthToCheck)
                {
                    throw new Exception($"Buffer is too small. Minimum size is {lengthToCheck} bytes");
                }

                buffer = bufferPool.GetBuffer(bufferPool.BufferSize);

                bool isExpected = true;
                int  i          = 0;
                while (i < lengthToCheck)
                {
                    int peeked = await clientStreamReader.PeekBytesAsync(buffer, i, i, lengthToCheck - i, cancellationToken);

                    if (peeked <= 0)
                    {
                        return(-1);
                    }

                    peeked += i;

                    while (i < peeked)
                    {
                        int b = buffer[i];

                        if (b == ' ' && i > 2)
                        {
                            return(isExpected ? 1 : 0);
                        }
                        else
                        {
                            char ch = (char)b;
                            if (ch < 'A' || ch > 'z' || (ch > 'Z' && ch < 'a')) // ASCII letter
                            {
                                return(-1);
                            }
                            else if (i >= expectedStart.Length || ch != expectedStart[i])
                            {
                                isExpected = false;
                            }
                        }

                        i++;
                    }
                }

                // only letters
                return(0);
            }
            finally
            {
                bufferPool.ReturnBuffer(buffer);
            }
        }
예제 #6
0
 public void Dispose()
 {
     if (!disposed)
     {
         disposed = true;
         bufferPool.ReturnBuffer(buffer);
     }
 }
예제 #7
0
        private void AdjustBufferSize()
        {
            if (timeInSize++ < steps)
            {
                return;
            }

            if (internalBuffer.Length >= MaxBufferSize)
            {
                return;
            }

            timeInSize = 0;
            steps     += 2;
            var newSize = Math.Min(internalBuffer.Length * 2, MaxBufferSize);

            bufferPool.ReturnBuffer(internalBuffer);
            internalBuffer = bufferPool.TakeBuffer(newSize);
        }
예제 #8
0
 public void Dispose()
 {
     if (!disposed)
     {
         disposed = true;
         var b = buffer;
         buffer = null;
         bufferPool.ReturnBuffer(b);
     }
 }
예제 #9
0
        protected virtual void Dispose(bool disposing)
        {
            if (disposed)
            {
                return;
            }

            disposed = true;
            bufferPool.ReturnBuffer(buffer);
        }
예제 #10
0
        private Task writeAsyncInternal(string value, bool addNewLine, CancellationToken cancellationToken)
        {
            int newLineChars = addNewLine ? newLine.Length : 0;
            int charCount    = value.Length;

            if (charCount < BufferSize - newLineChars)
            {
                value.CopyTo(0, charBuffer, 0, charCount);

                var buffer = bufferPool.GetBuffer(BufferSize);
                try
                {
                    int idx = encoder.GetBytes(charBuffer, 0, charCount, buffer, 0, true);
                    if (newLineChars > 0)
                    {
                        Buffer.BlockCopy(newLine, 0, buffer, idx, newLineChars);
                        idx += newLineChars;
                    }

                    return(stream.WriteAsync(buffer, 0, idx, cancellationToken));
                }
                finally
                {
                    bufferPool.ReturnBuffer(buffer);
                }
            }
            else
            {
                var charBuffer = new char[charCount];
                value.CopyTo(0, charBuffer, 0, charCount);

                var buffer = new byte[charCount + newLineChars + 1];
                int idx    = encoder.GetBytes(charBuffer, 0, charCount, buffer, 0, true);
                if (newLineChars > 0)
                {
                    Buffer.BlockCopy(newLine, 0, buffer, idx, newLineChars);
                    idx += newLineChars;
                }

                return(stream.WriteAsync(buffer, 0, idx, cancellationToken));
            }
        }
예제 #11
0
        /// <summary>
        ///     Gets the HTTP method from the stream.
        /// </summary>
        public static async ValueTask <KnownMethod> GetMethod(IPeekStream httpReader, IBufferPool bufferPool, CancellationToken cancellationToken = default)
        {
            const int lengthToCheck = 20;

            if (bufferPool.BufferSize < lengthToCheck)
            {
                throw new Exception($"Buffer is too small. Minimum size is {lengthToCheck} bytes");
            }

            byte[] buffer = bufferPool.GetBuffer(bufferPool.BufferSize);
            try
            {
                int i = 0;
                while (i < lengthToCheck)
                {
                    int peeked = await httpReader.PeekBytesAsync(buffer, i, i, lengthToCheck - i, cancellationToken);

                    if (peeked <= 0)
                    {
                        return(KnownMethod.Invalid);
                    }

                    peeked += i;

                    while (i < peeked)
                    {
                        int b = buffer[i];

                        if (b == ' ' && i > 2)
                        {
                            return(getKnownMethod(buffer.AsSpan(0, i)));
                        }

                        char ch = (char)b;
                        if ((ch < 'A' || ch > 'z' || (ch > 'Z' && ch < 'a')) && (ch != '-')) // ASCII letter
                        {
                            return(KnownMethod.Invalid);
                        }

                        i++;
                    }
                }

                // only letters, but no space (or shorter than 3 characters)
                return(KnownMethod.Invalid);
            }
            finally
            {
                bufferPool.ReturnBuffer(buffer);
            }
        }
예제 #12
0
        private async Task writeAsyncInternal(string value, bool addNewLine, CancellationToken cancellationToken)
        {
            int newLineChars = addNewLine ? newLine.Length : 0;
            int charCount    = value.Length;

            if (charCount < bufferPool.BufferSize - newLineChars)
            {
                var buffer = bufferPool.GetBuffer();
                try
                {
                    int idx = encoding.GetBytes(value, 0, charCount, buffer, 0);
                    if (newLineChars > 0)
                    {
                        Buffer.BlockCopy(newLine, 0, buffer, idx, newLineChars);
                        idx += newLineChars;
                    }

                    await stream.WriteAsync(buffer, 0, idx, cancellationToken);
                }
                finally
                {
                    bufferPool.ReturnBuffer(buffer);
                }
            }
            else
            {
                var buffer = new byte[charCount + newLineChars + 1];
                int idx    = encoding.GetBytes(value, 0, charCount, buffer, 0);
                if (newLineChars > 0)
                {
                    Buffer.BlockCopy(newLine, 0, buffer, idx, newLineChars);
                    idx += newLineChars;
                }

                await stream.WriteAsync(buffer, 0, idx, cancellationToken);
            }
        }
예제 #13
0
        /// <summary>
        ///     Determines whether the stream starts with the given string.
        /// </summary>
        /// <param name="clientStreamReader">The client stream reader.</param>
        /// <param name="expectedStart">The expected start.</param>
        /// <returns>
        ///     1: when starts with the given string, 0: when valid HTTP method, -1: otherwise
        /// </returns>
        private static async Task <int> startsWith(ICustomStreamReader clientStreamReader, IBufferPool bufferPool, int bufferSize, string expectedStart, CancellationToken cancellationToken = default)
        {
            int       iRet          = -1;
            const int lengthToCheck = 10;

            byte[] buffer = null;
            try
            {
                buffer = bufferPool.GetBuffer(Math.Max(bufferSize, lengthToCheck));

                int peeked = await clientStreamReader.PeekBytesAsync(buffer, 0, 0, lengthToCheck, cancellationToken);

                if (peeked > 0)
                {
                    bool isExpected = true;

                    for (int i = 0; i < lengthToCheck; i++)
                    {
                        int b = buffer[i];

                        if (b == ' ' && i > 2)
                        {
                            return(isExpected ? 1 : 0);
                        }
                        else
                        {
                            char ch = (char)b;
                            if (!char.IsLetter(ch))
                            {
                                return(-1);
                            }
                            else if (i >= expectedStart.Length || ch != expectedStart[i])
                            {
                                isExpected = false;
                            }
                        }
                    }

                    // only letters
                    iRet = isExpected ? 1 : 0;
                }
            }
            finally
            {
                bufferPool.ReturnBuffer(buffer);
                buffer = null;
            }
            return(iRet);
        }
        public async Task Finish()
        {
            if (bytesRemaining != -1)
            {
                var buffer = bufferPool.GetBuffer(baseStream.BufferSize);
                try
                {
                    int res = await ReadAsync(buffer, 0, buffer.Length);

                    if (res != 0)
                    {
                        throw new Exception("Data received after stream end");
                    }
                }
                finally
                {
                    bufferPool.ReturnBuffer(buffer);
                }
            }
        }
예제 #15
0
 protected override void Dispose(bool disposing)
 {
     _bufferPool.ReturnBuffer(_buffer);
     base.Dispose(disposing);
 }