/// <summary>
 /// Internal method for copying cookies from an HttpRequest to an HttpRequestMessage's headers
 /// NOTE: Assumes that the HttpClientHandler is set as such
 /// <code>
 /// services.AddHttpClient<ColorApiClient>(options => {
 //      options.BaseAddress = new Uri(SOME_URI);
 //  }).ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler {
 //      UseCookies = false
 //  });
 /// </code>
 /// </summary>
 /// <param name="msg"></param>
 /// <param name="req"></param>
 /// <param name="options"></param>
 /// <returns></returns>
 public static HttpRequestMessage CopyCookies(this HttpRequestMessage msg, HttpRequest req, RequestForwardingOptions options = null)
 {
     options ??= new RequestForwardingOptions();
     if (req.Cookies != null && req.Cookies.Count > 0)
     {
         var sb = new StringBuilder();
         foreach (var cookie in req.Cookies.Where(c => options.CookiesToForward.Contains(c.Key)))
         {
             sb.Append(cookie.Key);
             sb.Append("=");
             sb.Append(cookie.Value);
             sb.Append("; ");
         }
         msg.Headers.Add("Cookie", sb.ToString().TrimEnd());
     }
     return(msg);
 }
        /// <summary>
        /// Internal method for copying Headers from an HttpRequest to an HttpRequestMessage
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="req"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        public static HttpRequestMessage CopyHeaders(this HttpRequestMessage msg, HttpRequest req, RequestForwardingOptions options = null)
        {
            options ??= new RequestForwardingOptions();
            var headers = req.Headers.Where(h => options.HeadersToForward.Contains(h.Key));

            foreach (var header in headers)
            {
                if (header.Key.StartsWith(":"))
                {
                    msg.Headers.Add(header.Key.Substring(1), header.Value.AsEnumerable());
                }
                else
                {
                    msg.Headers.Add(header.Key, header.Value.AsEnumerable());
                }
            }
            return(msg);
        }
        /// <summary>
        /// Internal method for copying Headers from an HttpRequest to an HttpRequestMessage
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="req"></param>
        /// <param name="client"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        public static HttpRequestMessage CopyHeaders(this HttpRequestMessage msg, HttpRequest req, HttpClient client, RequestForwardingOptions options = null)
        {
            options ??= new RequestForwardingOptions();
            var currentHeaders = client.DefaultRequestHeaders.Select(x => x.Key);
            var headers        = req.Headers.Where(h => options.HeadersToForward.Contains(h.Key) && !currentHeaders.Contains(h.Key));

            foreach (var header in headers)
            {
                if (header.Key.StartsWith(":"))
                {
                    msg.Headers.Add(header.Key.Substring(1), header.Value.AsEnumerable());
                }
                else
                {
                    msg.Headers.Add(header.Key, header.Value.AsEnumerable());
                }
            }
            msg.Headers.Host = client.BaseAddress.Host;
            return(msg);
        }
        /// <summary>
        /// Internal method for building an HttpRequestMessage from a parent HttpRequest
        /// </summary>
        /// <param name="httpRequest"></param>
        /// <param name="client"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        private static HttpRequestMessage ToHttpRequestMessage <TRequestBody>(this HttpRequest httpRequest, HttpClient client, TRequestBody body, RequestForwardingOptions options = null)
        {
            var msg = new HttpRequestMessage();

            msg
            .CopyMethod(httpRequest)
            .CopyHeaders(httpRequest, client, options)
            .CopyCookies(httpRequest, options);

            if (options?.ForwardQueryString ?? true)
            {
                msg.CopyQueryString(httpRequest);
            }

            msg.AddJsonContent(body);

            return(msg);
        }
        /// <summary>
        /// Forwards a request through the provided HttpClient to another endpoint.
        /// NOTE: This overload of the Forward method takes a TRequestBody object representing a request body
        ///       and returns a TResponseBody object representing the response body.  The method is
        ///       particularly well-suited for PATCH scenarios.
        /// </summary>
        /// <typeparam name="TRequestBody">The type of the request body</typeparam>
        /// <typeparam name="TResponseBody">The type of the response body</typeparam>
        /// <param name="client">The HttpClient making the request</param>
        /// <param name="request">The incoming HttpRequest object</param>
        /// <param name="relativeUrlFromBase">Relative URL from the HttpClient's base url</param>
        /// <param name="body">The request body (prior to serialization)</param>
        /// <param name="options">Optionally specify certain aspects of the request to copy</param>
        /// <returns>A ObjectResult consisting of a status code and a deserialized response body.</returns>
        /// <see cref="Forward{TRequestBody,TResponseBody}(HttpClient, HttpRequest, string, TRequestBody, RequestForwardingOptions)"/>
        public static async Task <ObjectResult <TResponseBody> > ForwardAsync <TRequestBody, TResponseBody>(this HttpClient client, HttpRequest request, string relativeUrlFromBase, TRequestBody body, RequestForwardingOptions options = null)
        {
            var msg = request.ToHttpRequestMessage(client, body, options);
            var url = relativeUrlFromBase + (msg.Properties["QueryString"] ?? "");

            return(await ForwardRequestAsync <TResponseBody>(client, msg, url));
        }
 /// <summary>
 /// Forwards a request through the provided HttpClient to another endpoint.
 /// NOTE: This overload of the Forward method takes a TRequestBody object representing a request body
 ///       and returns a TResponseBody object representing the response body.  The method is
 ///       particularly well-suited for PATCH scenarios.
 /// </summary>
 /// <typeparam name="TRequestBody">The type of the request body</typeparam>
 /// <typeparam name="TResponseBody">The type of the response body</typeparam>
 /// <param name="client">The HttpClient making the request</param>
 /// <param name="request">The incoming HttpRequest object</param>
 /// <param name="relativeUrlFromBase">Relative URL from the HttpClient's base url</param>
 /// <param name="body">The request body (prior to serialization)</param>
 /// <param name="options">Optionally specify certain aspects of the request to copy</param>
 /// <returns>A ObjectResult consisting of a status code and a deserialized response body.</returns>
 /// <see cref="ForwardAsync{TRequestBody,TResponseBody}(HttpClient, HttpRequest, string, TRequestBody, RequestForwardingOptions)"/>
 public static ObjectResult <TResponseBody> Forward <TRequestBody, TResponseBody>(this HttpClient client, HttpRequest request, string relativeUrlFromBase, TRequestBody body, RequestForwardingOptions options = null)
 => ForwardAsync <TRequestBody, TResponseBody>(client, request, relativeUrlFromBase, body, options).Result;