private void SetHeaders(CurlEasyHandle easyHandle, SList headers)
        {
            foreach (string headerName in Headers.AllKeys)
            {
                headers.Append($"{headerName}: {Headers[headerName]}");
            }

            LibCurl.EasySetOpt(easyHandle, CurlOption.HttpHeader, headers.Handle);
        }
        private void SetHeaders(CurlEasyHandle easyHandle, SList headersList)
        {
            IEnumerable <IHttpHeader> headers = Headers.GetHeaders();

            foreach (IHttpHeader header in headers)
            {
                headersList.Append($"{header.Name}: {header.Value}");
            }

            // Adding the Accept-Encoding header manually ensures that it's below the Accept header.
            // See See https://sansec.io/research/http-header-order-is-important

            string acceptEncoding = GetAcceptEncoding();

            if (!string.IsNullOrWhiteSpace(acceptEncoding))
            {
                headersList.Append($"Accept-Encoding: {acceptEncoding}");
            }

            LibCurl.EasySetOpt(easyHandle, CurlOption.HttpHeader, headersList.Handle);
        }
        // Methods overidden from WebRequest

        public override WebResponse GetResponse()
        {
            Stream responseStream = new ProducerConsumerStream(8192)
            {
                Blocking     = true,
                ReadTimeout  = Timeout,
                WriteTimeout = Timeout,
            };

            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
            CancellationToken       cancellationToken       = cancellationTokenSource.Token;

            Task curlTask = Task.Factory.StartNew(() => {
                try {
                    GlobalInit();

                    using (CurlEasyHandle easyHandle = LibCurl.EasyInit())
                        using (SList headers = new SList())
                            using (MemoryStream requestStream = new MemoryStream(GetRequestStream(validateMethod: false).ToArray())) {
                                LibCurl.EasySetOpt(easyHandle, CurlOption.Url, base.RequestUri.AbsoluteUri);
                                LibCurl.EasySetOpt(easyHandle, CurlOption.FollowLocation, AllowAutoRedirect ? 1 : 0);
                                LibCurl.EasySetOpt(easyHandle, CurlOption.MaxRedirs, MaximumAutomaticRedirections);
                                LibCurl.EasySetOpt(easyHandle, CurlOption.Timeout, base.Timeout);

                                if (AutomaticDecompression != DecompressionMethods.None)
                                {
                                    LibCurl.EasySetOpt(easyHandle, CurlOption.AcceptEncoding, GetAcceptEncoding());
                                }

                                if (File.Exists(options.CABundlePath))
                                {
                                    LibCurl.EasySetOpt(easyHandle, CurlOption.CAInfo, options.CABundlePath);
                                }

                                SetCertificateValidationEnabled(easyHandle);
                                SetCookies(easyHandle);
                                SetCredentials(easyHandle);
                                SetHeaders(easyHandle, headers);
                                SetHttpVersion(easyHandle);
                                SetKeepAlive(easyHandle);
                                SetMethod(easyHandle);
                                SetProxy(easyHandle);

                                // Execute the request.

                                using (ICurlDataCopier dataCopier = new CurlDataCopier(requestStream, responseStream, cancellationToken)) {
                                    LibCurl.EasySetOpt(easyHandle, CurlOption.HeaderFunction, dataCopier.Header);
                                    LibCurl.EasySetOpt(easyHandle, CurlOption.ReadFunction, dataCopier.Read);
                                    LibCurl.EasySetOpt(easyHandle, CurlOption.WriteFunction, dataCopier.Write);
                                    LibCurl.EasySetOpt(easyHandle, CurlOption.ProgessFunction, dataCopier.Progress);

                                    CurlCode resultCode = LibCurl.EasyPerform(easyHandle);

                                    if (resultCode != CurlCode.OK)
                                    {
                                        throw new CurlException(resultCode);
                                    }
                                }
                            }
                }
                finally {
                    // Close the stream to indicate that we're done writing to it, unblocking readers.

                    responseStream.Close();

                    GlobalCleanup();
                }
            }, cancellationToken);

            HaveResponse = true;

            return(new CurlHttpWebResponse(this, responseStream, curlTask, cancellationTokenSource));
        }
        // Methods overidden from WebRequest

        public override WebResponse GetResponse()
        {
            ConcurrentMemoryStream stream = new ConcurrentMemoryStream()
            {
                Blocking    = true,
                ReadTimeout = Timeout
            };

            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
            CancellationToken       cancellationToken       = cancellationTokenSource.Token;

            Task curlTask = Task.Factory.StartNew(() => {
                // We shouldn't need to check if libcurl is initialized, because it's reference counted and can be called multiple times.
                // However, repeated calls to curl_global_cleanup are crashing my program, so for the time being I'm not pairing it with this.
                // It's up the user to call curl_global_cleanup once when they're done using it.

                if (!LibCurl.IsInitialized) // Check so ref count will not be increased (only one call to curl_global_cleanup required after multiple requests)
                {
                    LibCurl.GlobalInit();
                }

                try {
                    using (CurlEasyHandle easyHandle = LibCurl.EasyInit())
                        using (SList headers = new SList())
                            using (MemoryStream postDataStream = new MemoryStream(GetRequestStream(validateMethod: false).ToArray())) {
                                CurlDataCopier dataCopier = new CurlDataCopier(stream, postDataStream, cancellationToken);

                                dataCopier.SetCallbacks(easyHandle);

                                LibCurl.EasySetOpt(easyHandle, CurlOption.Url, RequestUri.AbsoluteUri);
                                LibCurl.EasySetOpt(easyHandle, CurlOption.FollowLocation, AllowAutoRedirect ? 1 : 0);
                                LibCurl.EasySetOpt(easyHandle, CurlOption.MaxRedirs, MaximumAutomaticRedirections);
                                LibCurl.EasySetOpt(easyHandle, CurlOption.Timeout, Timeout);
                                LibCurl.EasySetOpt(easyHandle, CurlOption.HttpVersion, (int)GetHttpVersion());

                                if (AutomaticDecompression != DecompressionMethods.None)
                                {
                                    LibCurl.EasySetOpt(easyHandle, CurlOption.AcceptEncoding, GetAcceptEncoding());
                                }

                                LibCurl.EasySetOpt(easyHandle, CurlOption.TcpKeepAlive, KeepAlive ? 1 : 0);

                                if (File.Exists(LibCurl.CABundlePath))
                                {
                                    LibCurl.EasySetOpt(easyHandle, CurlOption.CaInfo, LibCurl.CABundlePath);
                                }

                                SetCookies(easyHandle);
                                SetCredentials(easyHandle);
                                SetHeaders(easyHandle, headers);
                                SetMethod(easyHandle);
                                SetProxy(easyHandle);

                                // Execute the request.

                                LibCurl.EasyPerform(easyHandle);
                            }
                }
                finally {
                    // Close the stream to indicate that we're done writing to it, unblocking readers.

                    stream.Close();

                    //LibCurl.GlobalCleanup();
                }
            }, cancellationToken);

            HaveResponse = true;

            return(new LibCurlHttpWebResponse(this, stream, cancellationTokenSource));
        }