private HttpRequestMessage CreateRequestMessage(OpenIdConnectRequest request, HttpMethod method, Uri uri)
        {
            // Note: a dictionary is deliberately not used here to allow multiple parameters with the
            // same name to be specified. While initially not allowed by the core OAuth2 specification,
            // this is required for derived drafts like the OAuth2 token exchange specification.
            var parameters = new List <KeyValuePair <string, string> >();

            foreach (var parameter in request.GetParameters())
            {
                var values = (string[])parameter.Value;
                if (values == null)
                {
                    continue;
                }

                foreach (var value in values)
                {
                    parameters.Add(new KeyValuePair <string, string>(parameter.Key, value));
                }
            }

            if (method == HttpMethod.Get && parameters.Count != 0)
            {
                var builder = new StringBuilder();

                foreach (var parameter in parameters)
                {
                    if (builder.Length != 0)
                    {
                        builder.Append('&');
                    }

                    builder.Append(UrlEncoder.Default.Encode(parameter.Key));
                    builder.Append('=');
                    builder.Append(UrlEncoder.Default.Encode(parameter.Value));
                }

                if (!uri.IsAbsoluteUri)
                {
                    uri = new Uri(HttpClient.BaseAddress, uri);
                }

                uri = new UriBuilder(uri)
                {
                    Query = builder.ToString()
                }.Uri;
            }

            var message = new HttpRequestMessage(method, uri);

            if (method != HttpMethod.Get)
            {
                message.Content = new FormUrlEncodedContent(parameters);
            }

            return(message);
        }
        /// <summary>
        /// Sends a generic OpenID Connect request to the given endpoint and
        /// converts the returned response to an OpenID Connect response.
        /// </summary>
        /// <param name="method">The HTTP method used to send the OpenID Connect request.</param>
        /// <param name="uri">The endpoint to which the request is sent.</param>
        /// <param name="request">The OpenID Connect request to send.</param>
        /// <returns>The OpenID Connect response returned by the server.</returns>
        public virtual async Task <OpenIdConnectResponse> SendAsync(
            [NotNull] HttpMethod method, [NotNull] Uri uri,
            [NotNull] OpenIdConnectRequest request)
        {
            if (method == null)
            {
                throw new ArgumentNullException(nameof(method));
            }

            if (uri == null)
            {
                throw new ArgumentNullException(nameof(uri));
            }

            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (HttpClient.BaseAddress == null && !uri.IsAbsoluteUri)
            {
                throw new ArgumentException("The address cannot be a relative URI when no base address " +
                                            "is associated with the HTTP client.", nameof(uri));
            }

            var parameters = new Dictionary <string, string>();

            foreach (var parameter in request.GetParameters())
            {
                var value = (string)parameter.Value;
                if (string.IsNullOrEmpty(value))
                {
                    continue;
                }

                parameters.Add(parameter.Key, value);
            }

            if (method == HttpMethod.Get && parameters.Count != 0)
            {
                var builder = new StringBuilder();

                foreach (var parameter in parameters)
                {
                    if (builder.Length != 0)
                    {
                        builder.Append('&');
                    }

                    builder.Append(UrlEncoder.Default.Encode(parameter.Key));
                    builder.Append('=');
                    builder.Append(UrlEncoder.Default.Encode(parameter.Value));
                }

                if (!uri.IsAbsoluteUri)
                {
                    uri = new Uri(HttpClient.BaseAddress, uri);
                }

                uri = new UriBuilder(uri)
                {
                    Query = builder.ToString()
                }.Uri;
            }

            var message = new HttpRequestMessage(method, uri);

            if (method != HttpMethod.Get)
            {
                message.Content = new FormUrlEncodedContent(parameters);
            }

            var response = await HttpClient.SendAsync(message, HttpCompletionOption.ResponseHeadersRead);

            if (response.Headers.Location != null)
            {
                var payload = response.Headers.Location.Fragment;
                if (string.IsNullOrEmpty(payload))
                {
                    payload = response.Headers.Location.Query;
                }

                if (string.IsNullOrEmpty(payload))
                {
                    return(new OpenIdConnectResponse());
                }

                var result = new OpenIdConnectResponse();

                using (var tokenizer = new StringTokenizer(payload, OpenIdConnectConstants.Separators.Ampersand).GetEnumerator())
                {
                    while (tokenizer.MoveNext())
                    {
                        var parameter = tokenizer.Current;
                        if (parameter.Length == 0)
                        {
                            continue;
                        }

                        // Always skip the first char (# or ?).
                        if (parameter.Offset == 0)
                        {
                            parameter = parameter.Subsegment(1, parameter.Length - 1);
                        }

                        var index = parameter.IndexOf('=');
                        if (index == -1)
                        {
                            continue;
                        }

                        var name = parameter.Substring(0, index);
                        if (string.IsNullOrEmpty(name))
                        {
                            continue;
                        }

                        var value = parameter.Substring(index + 1, parameter.Length - (index + 1));
                        if (string.IsNullOrEmpty(value))
                        {
                            continue;
                        }

                        result.AddParameter(
                            Uri.UnescapeDataString(name.Replace('+', ' ')),
                            Uri.UnescapeDataString(value.Replace('+', ' ')));
                    }
                }

                return(result);
            }

            else if (string.Equals(response.Content?.Headers?.ContentType?.MediaType, "application/json", StringComparison.OrdinalIgnoreCase))
            {
                using (var stream = await response.Content.ReadAsStreamAsync())
                    using (var reader = new JsonTextReader(new StreamReader(stream)))
                    {
                        var serializer = JsonSerializer.CreateDefault();

                        return(serializer.Deserialize <OpenIdConnectResponse>(reader));
                    }
            }

            else if (string.Equals(response.Content?.Headers?.ContentType?.MediaType, "text/html", StringComparison.OrdinalIgnoreCase))
            {
                using (var stream = await response.Content.ReadAsStreamAsync())
                {
                    var result = new OpenIdConnectResponse();

                    var document = await new HtmlParser().ParseAsync(stream);

                    foreach (var element in document.Body.GetElementsByTagName("input"))
                    {
                        var name = element.GetAttribute("name");
                        if (string.IsNullOrEmpty(name))
                        {
                            continue;
                        }

                        var value = element.GetAttribute("value");
                        if (string.IsNullOrEmpty(value))
                        {
                            continue;
                        }

                        result.AddParameter(name, value);
                    }

                    return(result);
                }
            }

            else if (string.Equals(response.Content?.Headers?.ContentType?.MediaType, "text/plain", StringComparison.OrdinalIgnoreCase))
            {
                using (var stream = await response.Content.ReadAsStreamAsync())
                    using (var reader = new StreamReader(stream))
                    {
                        var result = new OpenIdConnectResponse();

                        for (var line = await reader.ReadLineAsync(); line != null; line = await reader.ReadLineAsync())
                        {
                            var index = line.IndexOf(':');
                            if (index == -1)
                            {
                                continue;
                            }

                            result.AddParameter(line.Substring(0, index), line.Substring(index + 1));
                        }

                        return(result);
                    }
            }

            return(new OpenIdConnectResponse());
        }