예제 #1
0
        public static async Task <ApiResponseData> HandleRequestAsync(ContentType?contentType, CQRSRequestData data)
        {
            if (!String.IsNullOrWhiteSpace(data.ProviderType))
            {
                if (contentType == null)
                {
                    return(null);
                }

                var response = await Call(data);

                if (response.Stream != null)
                {
                    return(new ApiResponseData(response.Stream));
                }
                else if (response.Model != null)
                {
                    var bytes = ContentTypeSerializer.Serialize(contentType.Value, response.Model);
                    return(new ApiResponseData(bytes));
                }
                else
                {
                    return(new ApiResponseData(Array.Empty <byte>()));
                }
            }
            else if (!String.IsNullOrWhiteSpace(data.MessageType))
            {
                await Dispatch(data);

                return(new ApiResponseData());
            }

            return(null);
        }
예제 #2
0
        protected override async void Handle(TcpClient client, CancellationToken cancellationToken)
        {
            HttpRequestHeader requestHeader = null;
            bool responseStarted            = false;

            var bufferOwner = BufferArrayPool <byte> .Rent(HttpCommon.BufferLength);

            var    buffer             = bufferOwner.AsMemory();
            Stream stream             = null;
            Stream requestBodyStream  = null;
            Stream responseBodyStream = null;

            try
            {
                stream = client.GetStream();

                //Read Request Header
                //------------------------------------------------------------------------------------------------------------
                var headerPosition = 0;
                var headerLength   = 0;
                var headerEnd      = false;
                while (!headerEnd)
                {
                    if (headerLength == buffer.Length)
                    {
                        throw new Exception($"{nameof(HttpCQRSServer)} Header Too Long");
                    }

#if NETSTANDARD2_0
                    var bytesRead = await stream.ReadAsync(bufferOwner, headerLength, buffer.Length - headerLength, cancellationToken);
#else
                    var bytesRead = await stream.ReadAsync(buffer.Slice(headerLength, buffer.Length - headerLength), cancellationToken);
#endif

                    if (bytesRead == 0)
                    {
                        throw new EndOfStreamException();
                    }
                    headerLength += bytesRead;

                    headerEnd = HttpCommon.ReadToHeaderEnd(buffer, ref headerPosition, headerLength);
                }
                requestHeader = HttpCommon.ReadHeader(buffer, headerPosition, headerLength);

                if (contentType.HasValue && requestHeader.ContentType.HasValue && requestHeader.ContentType != contentType)
                {
                    _ = Log.ErrorAsync($"{nameof(HttpCQRSServer)} Received Invalid Content Type {requestHeader.ContentType}");
                    throw new Exception("Invalid Content Type");
                }

                if (requestHeader.Preflight)
                {
                    _ = Log.TraceAsync($"{nameof(HttpCQRSServer)} Received Preflight {client.Client.RemoteEndPoint}");

                    var preflightLength = HttpCommon.BufferPreflight(buffer, requestHeader.Origin);
#if NETSTANDARD2_0
                    await stream.WriteAsync(bufferOwner, 0, preflightLength, cancellationToken);
#else
                    await stream.WriteAsync(buffer.Slice(0, preflightLength), cancellationToken);
#endif

                    await stream.FlushAsync(cancellationToken);

                    return;
                }

                if (!requestHeader.ContentType.HasValue)
                {
                    _ = Log.ErrorAsync($"{nameof(HttpCQRSServer)} Received Invalid Content Type {requestHeader.ContentType}");
                    throw new Exception("Invalid Content Type");
                }

                _ = Log.TraceAsync($"{nameof(HttpCQRSServer)} Received On {client.Client.LocalEndPoint} From {client.Client.RemoteEndPoint} {requestHeader.ProviderType}");
                if (allowOrigins != null && allowOrigins.Length > 0)
                {
                    if (allowOrigins.Contains(requestHeader.Origin))
                    {
                        throw new Exception($"Origin Not Allowed {requestHeader.Origin}");
                    }
                }

                //Read Request Body
                //------------------------------------------------------------------------------------------------------------

                requestBodyStream = new HttpProtocolBodyStream(requestHeader.ContentLength, stream, requestHeader.BodyStartBuffer, true);

                var data = await ContentTypeSerializer.DeserializeAsync <CQRSRequestData>(requestHeader.ContentType.Value, requestBodyStream);

                if (data == null)
                {
                    throw new Exception("Empty request body");
                }

#if NETSTANDARD2_0
                requestBodyStream.Dispose();
#else
                await requestBodyStream.DisposeAsync();
#endif
                requestBodyStream = null;

                //Authroize
                //------------------------------------------------------------------------------------------------------------
                switch (networkType)
                {
                case NetworkType.Internal:
                    if (data.Claims != null)
                    {
                        var claimsIdentity = new ClaimsIdentity(data.Claims.Select(x => new Claim(x.Type, x.Value)), "CQRS");
                        Thread.CurrentPrincipal = new ClaimsPrincipal(claimsIdentity);
                    }
                    else
                    {
                        Thread.CurrentPrincipal = null;
                    }
                    break;

                case NetworkType.Api:
                    if (this.apiAuthorizer != null)
                    {
                        this.apiAuthorizer.Authorize(requestHeader);
                    }
                    break;

                default:
                    throw new NotImplementedException();
                }

                //Process and Respond
                //----------------------------------------------------------------------------------------------------
                if (!String.IsNullOrWhiteSpace(data.ProviderType))
                {
                    var providerType = Discovery.GetTypeFromName(data.ProviderType);
                    var typeDetail   = TypeAnalyzer.GetTypeDetail(providerType);

                    if (!this.interfaceTypes.Contains(providerType))
                    {
                        throw new Exception($"Unhandled Provider Type {providerType.FullName}");
                    }

                    bool exposed = typeDetail.Attributes.Any(x => x is ServiceExposedAttribute attribute && (!attribute.NetworkType.HasValue || attribute.NetworkType == networkType)) &&
                                   !typeDetail.Attributes.Any(x => x is ServiceBlockedAttribute attribute && (!attribute.NetworkType.HasValue || attribute.NetworkType == networkType));
                    if (!exposed)
                    {
                        throw new Exception($"Provider {data.MessageType} is not exposed to {networkType}");
                    }

                    _ = Log.TraceAsync($"Received Call: {providerType.GetNiceName()}.{data.ProviderMethod}");

                    var result = await this.providerHandlerAsync.Invoke(providerType, data.ProviderMethod, data.ProviderArguments);


                    //Response Header
                    var responseHeaderLength = HttpCommon.BufferHeader(buffer, requestHeader.ProviderType, contentType, requestHeader.Origin, null);
#if NETSTANDARD2_0
                    await stream.WriteAsync(bufferOwner, 0, responseHeaderLength);
#else
                    await stream.WriteAsync(buffer.Slice(0, responseHeaderLength));
#endif

                    //Response Body
                    responseBodyStream = new HttpProtocolBodyStream(null, stream, null, false);

                    int bytesRead;
                    if (result.Stream != null)
                    {
#if NETSTANDARD2_0
                        while ((bytesRead = await result.Stream.ReadAsync(bufferOwner, 0, bufferOwner.Length, cancellationToken)) > 0)
                        {
                            await responseBodyStream.WriteAsync(bufferOwner, 0, bytesRead, cancellationToken);
                        }
#else
                        while ((bytesRead = await result.Stream.ReadAsync(buffer, cancellationToken)) > 0)
                        {
                            await responseBodyStream.WriteAsync(buffer.Slice(0, bytesRead), cancellationToken);
                        }
#endif
                        await responseBodyStream.FlushAsync();

#if NETSTANDARD2_0
                        responseBodyStream.Dispose();
#else
                        await responseBodyStream.DisposeAsync();
#endif
                        client.Dispose();
                        return;
                    }
                    else
                    {
                        await ContentTypeSerializer.SerializeAsync(requestHeader.ContentType.Value, responseBodyStream, result.Model);

                        await responseBodyStream.FlushAsync();

#if NETSTANDARD2_0
                        responseBodyStream.Dispose();
#else
                        await responseBodyStream.DisposeAsync();
#endif
                        client.Dispose();
                        return;
                    }
                }
                else if (!String.IsNullOrWhiteSpace(data.MessageType))
                {
                    var commandType = Discovery.GetTypeFromName(data.MessageType);
                    var typeDetail  = TypeAnalyzer.GetTypeDetail(commandType);

                    if (!typeDetail.Interfaces.Contains(typeof(ICommand)))
                    {
                        throw new Exception($"Type {data.MessageType} is not a command");
                    }

                    bool exposed = typeDetail.Attributes.Any(x => x is ServiceExposedAttribute attribute && (!attribute.NetworkType.HasValue || attribute.NetworkType == networkType)) &&
                                   !typeDetail.Attributes.Any(x => x is ServiceBlockedAttribute attribute && (!attribute.NetworkType.HasValue || attribute.NetworkType == networkType));
                    if (!exposed)
                    {
                        throw new Exception($"Command {data.MessageType} is not exposed to {networkType}");
                    }

                    var command = (ICommand)System.Text.Json.JsonSerializer.Deserialize(data.MessageData, commandType);

                    if (data.MessageAwait)
                    {
                        await handlerAwaitAsync(command);
                    }
                    else
                    {
                        await handlerAsync(command);
                    }

                    //Response Header
                    var responseHeaderLength = HttpCommon.BufferHeader(buffer, requestHeader.ProviderType, contentType, requestHeader.Origin, null);
#if NETSTANDARD2_0
                    await stream.WriteAsync(bufferOwner, 0, responseHeaderLength, cancellationToken);
#else
                    await stream.WriteAsync(buffer.Slice(0, responseHeaderLength), cancellationToken);
#endif

                    //Response Body Empty
                    responseBodyStream = new HttpProtocolBodyStream(null, stream, null, false);
                    await responseBodyStream.FlushAsync(cancellationToken);

#if NETSTANDARD2_0
                    responseBodyStream.Dispose();
#else
                    await responseBodyStream.DisposeAsync();
#endif
                    client.Dispose();
                    return;
                }

                throw new Exception("Invalid Request");
            }
            catch (Exception ex)
            {
                if (ex is IOException ioException)
                {
                    if (ioException.InnerException != null && ioException.InnerException is SocketException socketException)
                    {
                        if (socketException.SocketErrorCode == SocketError.ConnectionAborted)
                        {
                            return;
                        }
                    }
                }

                _ = Log.ErrorAsync(null, ex);

                if (client.Connected && !responseStarted && requestHeader != null && requestHeader.ContentType.HasValue)
                {
                    try
                    {
                        //Response Header
                        var responseHeaderLength = HttpCommon.BufferErrorHeader(buffer, requestHeader.Origin);
#if NETSTANDARD2_0
                        await stream.WriteAsync(bufferOwner, 0, responseHeaderLength, cancellationToken);
#else
                        await stream.WriteAsync(buffer.Slice(0, responseHeaderLength), cancellationToken);
#endif

                        //Response Body
                        responseBodyStream = new HttpProtocolBodyStream(null, stream, null, false);
                        await ContentTypeSerializer.SerializeExceptionAsync(requestHeader.ContentType.Value, responseBodyStream, ex);

#if NETSTANDARD2_0
                        responseBodyStream.Dispose();
#else
                        await responseBodyStream.DisposeAsync();
#endif
                        client.Dispose();
                    }
                    catch (Exception ex2)
                    {
                        if (responseBodyStream != null)
                        {
#if NETSTANDARD2_0
                            responseBodyStream.Dispose();
#else
                            await responseBodyStream.DisposeAsync();
#endif
                        }
                        if (stream != null)
                        {
#if NETSTANDARD2_0
                            stream.Dispose();
#else
                            await stream.DisposeAsync();
#endif
                        }
                        client.Dispose();
                        _ = Log.ErrorAsync($"{nameof(HttpCQRSServer)} Error {client.Client.RemoteEndPoint}", ex2);
                    }
                    return;
                }

                if (responseBodyStream != null)
                {
#if NETSTANDARD2_0
                    responseBodyStream.Dispose();
#else
                    await responseBodyStream.DisposeAsync();
#endif
                }
                if (requestBodyStream != null)
                {
#if NETSTANDARD2_0
                    requestBodyStream.Dispose();
#else
                    await requestBodyStream.DisposeAsync();
#endif
                }
                if (stream != null)
                {
#if NETSTANDARD2_0
                    stream.Dispose();
#else
                    await stream.DisposeAsync();
#endif
                }
                client.Dispose();
            }
            finally
            {
                BufferArrayPool <byte> .Return(bufferOwner);
            }
        }
예제 #3
0
        protected override async Task DispatchInternal(ICommand command, bool messageAwait)
        {
            var messageType     = command.GetType();
            var messageTypeName = messageType.GetNiceName();

            var messageData = System.Text.Json.JsonSerializer.Serialize(command, messageType);

            var data = new CQRSRequestData()
            {
                MessageType  = messageTypeName,
                MessageData  = messageData,
                MessageAwait = messageAwait
            };

            switch (networkType)
            {
            case NetworkType.Internal:
                data.AddClaims();
                break;

            case NetworkType.Api:
                break;

            default:
                throw new NotImplementedException();
            }

            var stopwatch = Stopwatch.StartNew();

            var client = new TcpClient(endpoint.AddressFamily);

            client.NoDelay = true;

            var bufferOwner = BufferArrayPool <byte> .Rent(TcpRawCommon.BufferLength);

            var              buffer                  = bufferOwner.AsMemory();
            Stream           stream                  = null;
            Stream           requestBodyStream       = null;
            FinalBlockStream requestBodyCryptoStream = null;
            Stream           responseBodyStream      = null;

            try
            {
                await client.ConnectAsync(endpoint.Address, endpoint.Port);

                stream = client.GetStream();

                //Request Header
                var requestHeaderLength = TcpRawCommon.BufferHeader(buffer, data.MessageType, contentType);
#if NETSTANDARD2_0
                await stream.WriteAsync(bufferOwner, 0, requestHeaderLength);
#else
                await stream.WriteAsync(buffer.Slice(0, requestHeaderLength));
#endif

                requestBodyStream = new TcpRawProtocolBodyStream(stream, null, true);

                if (encryptionKey != null)
                {
                    requestBodyCryptoStream = SymmetricEncryptor.Encrypt(encryptionAlgorithm, encryptionKey, requestBodyStream, true, true);
                    await ContentTypeSerializer.SerializeAsync(contentType, requestBodyCryptoStream, data);

#if NET5_0_OR_GREATER
                    await requestBodyCryptoStream.FlushFinalBlockAsync();
#else
                    requestBodyCryptoStream.FlushFinalBlock();
#endif
#if NETSTANDARD2_0
                    requestBodyCryptoStream.Dispose();
#else
                    await requestBodyCryptoStream.DisposeAsync();
#endif
                    requestBodyCryptoStream = null;
                }
                else
                {
                    await ContentTypeSerializer.SerializeAsync(contentType, requestBodyStream, data);

                    await requestBodyStream.FlushAsync();
                }

#if NETSTANDARD2_0
                requestBodyStream.Dispose();
#else
                await requestBodyStream.DisposeAsync();
#endif
                requestBodyStream = null;

                //Response Header
                var headerPosition   = 0;
                var headerLength     = 0;
                var requestHeaderEnd = false;
                while (!requestHeaderEnd)
                {
                    if (headerLength == buffer.Length)
                    {
                        throw new Exception($"{nameof(TcpRawCQRSClient)} Header Too Long");
                    }

#if NETSTANDARD2_0
                    var bytesRead = await stream.ReadAsync(bufferOwner, headerPosition, buffer.Length - headerPosition);
#else
                    var bytesRead = await stream.ReadAsync(buffer.Slice(headerPosition, buffer.Length - headerPosition));
#endif

                    if (bytesRead == 0)
                    {
                        throw new EndOfStreamException();
                    }
                    headerLength += bytesRead;

                    requestHeaderEnd = TcpRawCommon.ReadToHeaderEnd(buffer, ref headerPosition, headerLength);
                }
                var responseHeader = TcpRawCommon.ReadHeader(buffer, headerPosition, headerLength);

                //Response Body
                responseBodyStream = new TcpRawProtocolBodyStream(stream, responseHeader.BodyStartBuffer, false);

                if (responseHeader.IsError)
                {
                    if (encryptionKey != null)
                    {
                        responseBodyStream = SymmetricEncryptor.Decrypt(encryptionAlgorithm, encryptionKey, responseBodyStream, false, false);
                    }

                    var responseException = await ContentTypeSerializer.DeserializeExceptionAsync(contentType, responseBodyStream);

                    throw responseException;
                }

                if (responseBodyStream != null)
                {
#if NETSTANDARD2_0
                    responseBodyStream.Dispose();
#else
                    await responseBodyStream.DisposeAsync();
#endif
                }
                client.Dispose();

                stopwatch.Stop();
                _ = Log.TraceAsync($"{nameof(TcpRawCQRSClient)}Sent: {messageTypeName} {stopwatch.ElapsedMilliseconds}");
            }
            catch
            {
                if (responseBodyStream != null)
                {
#if NETSTANDARD2_0
                    responseBodyStream.Dispose();
#else
                    await responseBodyStream.DisposeAsync();
#endif
                }
                if (requestBodyStream != null)
                {
#if NETSTANDARD2_0
                    requestBodyStream.Dispose();
#else
                    await requestBodyStream.DisposeAsync();
#endif
                }
                if (stream != null)
                {
#if NETSTANDARD2_0
                    stream.Dispose();
#else
                    await stream.DisposeAsync();
#endif
                }
                client.Dispose();
                throw;
            }
            finally
            {
                BufferArrayPool <byte> .Return(bufferOwner);
            }
        }
예제 #4
0
        protected override TReturn CallInternal <TReturn>(bool isStream, Type interfaceType, string methodName, object[] arguments)
        {
            var data = new CQRSRequestData()
            {
                ProviderType   = interfaceType.Name,
                ProviderMethod = methodName
            };

            data.AddProviderArguments(arguments);

            switch (networkType)
            {
            case NetworkType.Internal:
                data.AddClaims();
                break;

            case NetworkType.Api:
                break;

            default: throw new NotImplementedException();
            }

            var stopwatch = Stopwatch.StartNew();

            var client = new TcpClient(endpoint.AddressFamily);

            var bufferOwner = BufferArrayPool <byte> .Rent(TcpRawCommon.BufferLength);

            var              buffer                  = bufferOwner.AsMemory();
            Stream           stream                  = null;
            Stream           requestBodyStream       = null;
            FinalBlockStream requestBodyCryptoStream = null;
            Stream           responseBodyStream      = null;

            try
            {
                client.Connect(endpoint.Address, endpoint.Port);

                stream = client.GetStream();

                //Request Header
                var requestHeaderLength = TcpRawCommon.BufferHeader(buffer, data.ProviderType, contentType);
#if NETSTANDARD2_0
                stream.Write(bufferOwner, 0, requestHeaderLength);
#else
                stream.Write(buffer.Span.Slice(0, requestHeaderLength));
#endif

                requestBodyStream = new TcpRawProtocolBodyStream(stream, null, true);

                if (encryptionKey != null)
                {
                    requestBodyCryptoStream = SymmetricEncryptor.Encrypt(encryptionAlgorithm, encryptionKey, requestBodyStream, true, true);
                    ContentTypeSerializer.Serialize(contentType, requestBodyCryptoStream, data);
                    requestBodyCryptoStream.FlushFinalBlock();
                    requestBodyCryptoStream.Dispose();
                    requestBodyCryptoStream = null;
                }
                else
                {
                    ContentTypeSerializer.Serialize(contentType, requestBodyStream, data);
                    requestBodyStream.Flush();
                }

                requestBodyStream.Dispose();
                requestBodyStream = null;

                //Response Header
                var headerPosition   = 0;
                var headerLength     = 0;
                var requestHeaderEnd = false;
                while (!requestHeaderEnd)
                {
                    if (headerLength == buffer.Length)
                    {
                        throw new Exception($"{nameof(TcpRawCQRSClient)} Header Too Long");
                    }

#if NETSTANDARD2_0
                    var bytesRead = stream.Read(bufferOwner, headerPosition, buffer.Length - headerPosition);
#else
                    var bytesRead = stream.Read(buffer.Span.Slice(headerPosition, buffer.Length - headerPosition));
#endif

                    if (bytesRead == 0)
                    {
                        throw new EndOfStreamException();
                    }
                    headerLength += bytesRead;

                    requestHeaderEnd = TcpRawCommon.ReadToHeaderEnd(buffer, ref headerPosition, headerLength);
                }
                var responseHeader = TcpRawCommon.ReadHeader(buffer, headerPosition, headerLength);

                responseBodyStream = new TcpRawProtocolBodyStream(stream, responseHeader.BodyStartBuffer, false);

                if (encryptionKey != null)
                {
                    responseBodyStream = SymmetricEncryptor.Decrypt(encryptionAlgorithm, encryptionKey, responseBodyStream, false, false);
                }

                if (responseHeader.IsError)
                {
                    var responseException = ContentTypeSerializer.DeserializeException(contentType, responseBodyStream);
                    throw responseException;
                }

                stopwatch.Stop();
                _ = Log.TraceAsync($"{nameof(TcpRawCQRSClient)} Query: {interfaceType.GetNiceName()}.{data.ProviderMethod} {stopwatch.ElapsedMilliseconds}");

                if (isStream)
                {
                    return((TReturn)(object)responseBodyStream); //TODO better way to convert type???
                }
                else
                {
                    var model = ContentTypeSerializer.Deserialize <TReturn>(contentType, responseBodyStream);
                    responseBodyStream.Dispose();
                    client.Dispose();
                    return(model);
                }
            }
            catch
            {
                try
                {
                    //crypto stream can error, we want to throw the actual error
                    if (responseBodyStream != null)
                    {
                        responseBodyStream.Dispose();
                    }
                }
                catch { }
                if (requestBodyStream != null)
                {
                    requestBodyStream.Dispose();
                }
                if (requestBodyCryptoStream != null)
                {
                    requestBodyCryptoStream.Dispose();
                }
                if (stream != null)
                {
                    stream.Dispose();
                }
                client.Dispose();
                throw;
            }
            finally
            {
                BufferArrayPool <byte> .Return(bufferOwner);
            }
        }
예제 #5
0
        private TReturn Request <TReturn>(string address, string providerType, ContentType contentType, object data, bool getResponseData)
        {
            HttpWebRequest request = WebRequest.CreateHttp(address);

            request.Method = "POST";
            switch (contentType)
            {
            case ContentType.Bytes:
                request.ContentType = "application/octet-stream";
                break;

            case ContentType.Json:
                request.ContentType = "application/json; charset=utf-8";
                break;

            case ContentType.JsonNameless:
                request.ContentType = "application/jsonnameless; charset=utf-8";
                break;
            }

            request.Accept  = request.ContentType;
            request.Timeout = Timeout.Infinite;

            if (!String.IsNullOrWhiteSpace(providerType))
            {
                request.Headers.Add("Provider-Type", providerType);
            }

            CookieContainer cookieContainer = new CookieContainer();

            if (cookies != null)
            {
                cookieContainer.Add(cookies);
            }
            request.CookieContainer = cookieContainer;

            request.SendChunked = true;
            using (var postStream = request.GetRequestStream())
            {
                ContentTypeSerializer.Serialize(contentType, postStream, data);
            }

            try
            {
                var response = (HttpWebResponse)request.GetResponse();

                if (getResponseData)
                {
                    using (Stream stream = response.GetResponseStream())
                    {
                        var result = ContentTypeSerializer.Deserialize <TReturn>(contentType, stream);
                        return(result);
                    }
                }

                response.Close();
                response.Dispose();
            }
            catch (WebException ex)
            {
                if (ex.Response != null)
                {
                    Exception innerException;
                    using (Stream exStream = ex.Response.GetResponseStream())
                    {
                        innerException = ContentTypeSerializer.DeserializeException(contentType, exStream);
                    }
                    ex.Response.Close();
                    ex.Response.Dispose();

                    throw innerException;
                }
                throw;
            }

            this.cookies = GetCookiesFromContainer(cookieContainer);

            return(default);
예제 #6
0
        protected override TReturn CallInternal <TReturn>(bool isStream, Type interfaceType, string methodName, object[] arguments)
        {
            var data = new CQRSRequestData()
            {
                ProviderType   = interfaceType.Name,
                ProviderMethod = methodName
            };

            data.AddProviderArguments(arguments);

            HttpAuthHeaders authHeaders = null;

            switch (networkType)
            {
            case NetworkType.Internal:
                data.AddClaims();
                break;

            case NetworkType.Api:
                if (apiAuthorizer != null)
                {
                    authHeaders = apiAuthorizer.BuildAuthHeaders();
                }
                break;

            default:
                throw new NotImplementedException();
            }

            var stopwatch = Stopwatch.StartNew();

            var client = new TcpClient(endpoint.AddressFamily);

            client.NoDelay = true;

            var bufferOwner = BufferArrayPool <byte> .Rent(HttpCommon.BufferLength);

            var    buffer             = bufferOwner.AsMemory();
            Stream stream             = null;
            Stream requestBodyStream  = null;
            Stream responseBodyStream = null;

            try
            {
                client.Connect(endpoint.Address, endpoint.Port);

                stream = client.GetStream();

                //Request Header
                var requestHeaderLength = HttpCommon.BufferHeader(buffer, data.ProviderType, contentType, serviceUrl, authHeaders);
#if NETSTANDARD2_0
                stream.Write(bufferOwner, 0, requestHeaderLength);
#else
                stream.Write(buffer.Span.Slice(0, requestHeaderLength));
#endif

                requestBodyStream = new HttpProtocolBodyStream(null, stream, null, true);

                ContentTypeSerializer.Serialize(contentType, requestBodyStream, data);
                requestBodyStream.Flush();

                requestBodyStream.Dispose();
                requestBodyStream = null;

                //Response Header
                var headerPosition = 0;
                var headerLength   = 0;
                var headerEnd      = false;
                while (!headerEnd)
                {
                    if (headerLength == buffer.Length)
                    {
                        throw new Exception($"{nameof(HttpCQRSClient)} Header Too Long");
                    }

#if NETSTANDARD2_0
                    var bytesRead = stream.Read(bufferOwner, headerPosition, buffer.Length - headerPosition);
#else
                    var bytesRead = stream.Read(buffer.Span.Slice(headerPosition, buffer.Length - headerPosition));
#endif

                    if (bytesRead == 0)
                    {
                        throw new EndOfStreamException();
                    }
                    headerLength += bytesRead;

                    headerEnd = HttpCommon.ReadToHeaderEnd(buffer, ref headerPosition, headerLength);
                }
                var responseHeader = HttpCommon.ReadHeader(buffer, headerPosition, headerLength);

                //Response Body
                responseBodyStream = new HttpProtocolBodyStream(null, stream, responseHeader.BodyStartBuffer, false);

                if (responseHeader.IsError)
                {
                    var responseException = ContentTypeSerializer.DeserializeException(contentType, responseBodyStream);
                    throw responseException;
                }

                stopwatch.Stop();
                _ = Log.TraceAsync($"{nameof(HttpCQRSClient)} Query: {interfaceType.GetNiceName()}.{data.ProviderMethod} {stopwatch.ElapsedMilliseconds}");

                if (isStream)
                {
                    return((TReturn)(object)responseBodyStream);
                }
                else
                {
                    var model = ContentTypeSerializer.Deserialize <TReturn>(contentType, responseBodyStream);
                    responseBodyStream.Dispose();
                    client.Dispose();
                    return(model);
                }
            }
            catch
            {
                if (responseBodyStream != null)
                {
                    responseBodyStream.Dispose();
                }
                if (requestBodyStream != null)
                {
                    requestBodyStream.Dispose();
                }
                if (stream != null)
                {
                    stream.Dispose();
                }
                client.Dispose();
                throw;
            }
            finally
            {
                BufferArrayPool <byte> .Return(bufferOwner);
            }
        }