private static async Task SendDownstreamResponse(UpstreamResponse upstreamResponse, HttpResponse response, int?contentBytes = null)
        {
            Log("Sending downstream response...");

            response.StatusCode = upstreamResponse.StatusCode;

            Log($"StatusCode: {upstreamResponse.StatusCode}");

            Log("Headers:");
            foreach (var header in upstreamResponse.Headers)
            {
                Log($"  {header.Key}:{header.Value}");
                response.Headers.Add(header.Key, header.Value);
            }

            var count = contentBytes ?? upstreamResponse.Content.Length;

            Log($"Writing response body of {count} bytes...");
            await response.Body.WriteAsync(upstreamResponse.Content, 0, count);

            Log($"Finished writing response body");
        }
        private static async Task <UpstreamResponse> SendUpstreamRequest(HttpRequest request)
        {
            var upstreamUriBuilder = new UriBuilder()
            {
                Scheme = request.Scheme,
                Host   = request.Host.Host,
                Path   = request.Path.Value,
                Query  = request.QueryString.Value,
            };

            if (request.Host.Port.HasValue)
            {
                upstreamUriBuilder.Port = request.Host.Port.Value;
            }

            var upstreamUri = upstreamUriBuilder.Uri;

            Log("Upstream Request");
            Log($"URL: {upstreamUri}");

            using (var upstreamRequest = new HttpRequestMessage(new HttpMethod(request.Method), upstreamUri))
            {
                Log("Headers:");

                if (request.ContentLength > 0)
                {
                    upstreamRequest.Content = new StreamContent(request.Body);

                    foreach (var header in request.Headers.Where(h => _contentRequestHeaders.Contains(h.Key)))
                    {
                        Log($"  {header.Key}:{header.Value.First()}");
                        upstreamRequest.Content.Headers.Add(header.Key, values: header.Value);
                    }
                }

                foreach (var header in request.Headers.Where(h => !_excludedRequestHeaders.Contains(h.Key) && !_contentRequestHeaders.Contains(h.Key)))
                {
                    Log($"  {header.Key}:{header.Value.First()}");
                    if (!upstreamRequest.Headers.TryAddWithoutValidation(header.Key, values: header.Value))
                    {
                        throw new InvalidOperationException($"Could not add header {header.Key} with value {header.Value}");
                    }
                }

                Log("Sending request to upstream server...");
                using (var upstreamResponseMessage = await _httpClient.SendAsync(upstreamRequest))
                {
                    Log("Upstream Response");
                    var headers = new List <KeyValuePair <string, StringValues> >();

                    Log($"StatusCode: {upstreamResponseMessage.StatusCode}");

                    Log("Headers:");
                    foreach (var header in upstreamResponseMessage.Headers)
                    {
                        Log($"  {header.Key}:{header.Value.First()}");

                        // Must skip "Transfer-Encoding" header, since if it's set manually Kestrel requires you to implement
                        // your own chunking.
                        if (string.Equals(header.Key, "Transfer-Encoding", StringComparison.OrdinalIgnoreCase))
                        {
                            continue;
                        }

                        headers.Add(new KeyValuePair <string, StringValues>(header.Key, header.Value.ToArray()));
                    }

                    foreach (var header in upstreamResponseMessage.Content.Headers)
                    {
                        Log($"  {header.Key}:{header.Value.First()}");
                        headers.Add(new KeyValuePair <string, StringValues>(header.Key, header.Value.ToArray()));
                    }

                    Log("Reading upstream response body...");

                    var upstreamResponse = new UpstreamResponse()
                    {
                        StatusCode = (int)upstreamResponseMessage.StatusCode,
                        Headers    = headers.ToArray(),
                        Content    = await upstreamResponseMessage.Content.ReadAsByteArrayAsync()
                    };

                    Log($"ContentLength: {upstreamResponse.Content.Length}");

                    return(upstreamResponse);
                }
            }
        }