Add cURL options related to the request body

@param \Psr\Http\Message\RequestInterface  $request  Request object
@param array  $options  cURL options
11129
protected function addRequestBodyOptions(RequestInterface $request, array $options)
	{
		/*
		 * HTTP methods that cannot have payload:
		 * - GET   => cURL will automatically change method to PUT or POST if we
		 *            set CURLOPT_UPLOAD or CURLOPT_POSTFIELDS.
		 * - HEAD  => cURL treats HEAD as GET request with a same restrictions.
		 * - TRACE => According to RFC7231: a client MUST NOT send a message body
		 *            in a TRACE request.
		 */
		$http_methods = [
			'GET',
			'HEAD',
			'TRACE',
		];

		if (!in_array($request->getMethod(), $http_methods, true)) {
			$body      = $request->getBody();
			$body_size = $body->getSize();

			if ($body_size !== 0) {
				if ($body->isSeekable()) {
					$body->rewind();
				}

				if ($body_size === null || $body_size > self::$MAX_BODY_SIZE) {
					$options[CURLOPT_UPLOAD] = true;

					if ($body_size !== null) {
						$options[CURLOPT_INFILESIZE] = $body_size;
					}

					$options[CURLOPT_READFUNCTION] = function ($ch, $fd, $len) use ($body) {
						return $body->read($len);
					};
				}
				else {
					$options[CURLOPT_POSTFIELDS] = (string)$body;
				}
			}
		}

		if ($request->getMethod() === 'HEAD') {
			$options[CURLOPT_NOBODY] = true;
		}
		else if ($request->getMethod() !== 'GET') {
			$options[CURLOPT_CUSTOMREQUEST] = $request->getMethod();
		}

		return $options;
	}
571703
private function setRequestOptions($url, $vars, $options = array())
    {
        curl_setopt($this->request, CURLOPT_URL, $url);
        if (!empty($vars)) {
            curl_setopt($this->request, CURLOPT_POSTFIELDS, $vars);
        }

        # Set some default CURL options
        curl_setopt($this->request, CURLOPT_HEADER, true);
        curl_setopt($this->request, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($this->request, CURLOPT_USERAGENT, $this->userAgent);
        curl_setopt($this->request, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
        if ($this->cookieFile) {
            curl_setopt($this->request, CURLOPT_COOKIEFILE, $this->cookieFile);
            curl_setopt($this->request, CURLOPT_COOKIEJAR, $this->cookieFile);
        }
        if ($this->followRedirects) {
            curl_setopt($this->request, CURLOPT_FOLLOWLOCATION, true);
        }
        if ($this->referrer) {
            curl_setopt($this->request, CURLOPT_REFERER, $this->referrer);
        }

        # Set any custom CURL options
        foreach (array_merge($options, $this->options) as $option => $value) {
            curl_setopt($this->request, constant('CURLOPT_' . str_replace('CURLOPT_', '', strtoupper($option))), $value);
        }
    }
799003
private function getCurloptHttpHeader(RequestInterface $Request) : array
    {
        $options    = [];
        $headers    = $Request->getHeaders();
        foreach ($headers as $name => $values) {
            $header = strtolower($name);
            if ('expect' === $header) {
                // curl-client does not support "Expect-Continue", so dropping "expect" headers
                continue;
            }
            foreach ($values as $value) {
                $options[] = $name . ': ' . $value;
            }
        }
        /*
         * curl-client does not support "Expect-Continue", but cURL adds "Expect" header by default.
         * We can not suppress it, but we can set it to empty.
         */
        $options[] = 'Expect:';

        return $options;
    }
939699
public function setBody(RequestInterface $request, $body, $options)
    {
        return RequestHandler::setBody($request, $body, $options);
    }
782473
protected function _buildContext(Request $request, $options)
    {
        $this->_buildContent($request, $options);
        $this->_buildHeaders($request, $options);
        $this->_buildOptions($request, $options);

        $url = $request->getUri();
        $scheme = parse_url($url, PHP_URL_SCHEME);
        if ($scheme === 'https') {
            $this->_buildSslContext($request, $options);
        }
        $this->_context = stream_context_create([
            'http' => $this->_contextOptions,
            'ssl' => $this->_sslContextOptions,
        ]);
    }
567200
private function composeCurlOptions(array $options)
    {
        static $optionMap = [
            'protocolVersion' => CURLOPT_HTTP_VERSION,
            'maxRedirects' => CURLOPT_MAXREDIRS,
            'sslCapath' => CURLOPT_CAPATH,
            'sslCafile' => CURLOPT_CAINFO,
            'sslLocalCert' => CURLOPT_SSLCERT,
            'sslLocalPk' => CURLOPT_SSLKEY,
            'sslPassphrase' => CURLOPT_SSLCERTPASSWD,
        ];

        $curlOptions = [];
        foreach ($options as $key => $value) {
            if (is_int($key)) {
                $curlOptions[$key] = $value;
            } else {
                if (isset($optionMap[$key])) {
                    $curlOptions[$optionMap[$key]] = $value;
                } else {
                    $key = strtoupper($key);
                    if (strpos($key, 'SSL') === 0) {
                        $key = substr($key, 3);
                        $constantName = 'CURLOPT_SSL_' . $key;
                        if (!defined($constantName)) {
                            $constantName = 'CURLOPT_SSL' . $key;
                        }
                    } else {
                        $constantName = 'CURLOPT_' . strtoupper($key);
                    }
                    $curlOptions[constant($constantName)] = $value;
                }
            }
        }
        return $curlOptions;
    }
1121914
protected function doRequest(RequestInterface $request)
    {
        // Create the stream context options array with the required method offset.
        $options = ['method' => $request->getMethod()];

        // Set HTTP Version
        $options['protocol_version'] = $request->getProtocolVersion();

        // If data exists let's encode it and make sure our Content-Type header is set.
        $data = (string) $request->getBody();

        if (isset($data)) {
            // If the data is a scalar value simply add it to the stream context options.
            if (is_scalar($data)) {
                $options['content'] = $data;
            } else // Otherwise we need to encode the value first.
            {
                $options['content'] = http_build_query($data);
            }

            if (!$request->getHeader('Content-Type')) {
                $request = $request->withHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
            }

            // Add the relevant headers.
            $request = $request->withHeader('Content-Length', (string) strlen($options['content']));
        }

        // Speed up stream get URL
        // @see http://stackoverflow.com/questions/3629504/php-file-get-contents-very-slow-when-using-full-url
        // @see http://stackoverflow.com/questions/13679976/how-to-speed-up-file-get-contents
        $request = $request->withHeader('Connection', 'Close');

        // Build the headers string for the request.
        if ($headers = $request->getHeaders()) {
            // Add the headers string into the stream context options array.
            $options['header'] = HeaderHelper::toHeaderLine($headers, true);
        }

        // If an explicit timeout is given user it.
        if ($this->getOption('timeout')) {
            $options['timeout'] = (int) $this->getOption('timeout');
        }

        // If an explicit user agent is given use it.
        if ($this->getOption('userAgent')) {
            $options['user_agent'] = $this->getOption('userAgent');
        }

        // Ignore HTTP errors so that we can capture them.
        $options['ignore_errors'] = 1;

        // Follow redirects.
        $options['follow_location'] = (int) $this->getOption('follow_location', 1);

        foreach ((array) $this->getOption('options') as $key => $value) {
            $options[$key] = $value;
        }

        // Create the stream context for the request.
        $context = stream_context_create(['http' => $options]);

        // Capture PHP errors
        $php_errormsg = '';
        $track_errors = ini_get('track_errors');
        ini_set('track_errors', '1');

        $connection = @fopen($request->getRequestTarget(), Stream::MODE_READ_ONLY_FROM_BEGIN, false, $context);

        if (!$connection) {
            if (!$php_errormsg) {
                // Error but nothing from php? Create our own
                $php_errormsg = sprintf('Could not connect to resource: %s', $request->getRequestTarget());
            }

            // Restore error tracking to give control to the exception handler
            ini_set('track_errors', $track_errors);

            throw new \RuntimeException($php_errormsg);
        }

        $stream = new Stream($connection);

        if ($dest = $this->getOption('target_file')) {
            $content = '';
            StreamHelper::copyTo($stream, $dest);
        } else {
            $content = $stream->getContents();
        }

        $metadata = $stream->getMetadata();

        $stream->close();

        if (isset($metadata['wrapper_data']['headers'])) {
            $headers = $metadata['wrapper_data']['headers'];
        } elseif (isset($metadata['wrapper_data'])) {
            $headers = $metadata['wrapper_data'];
        } else {
            $headers = [];
        }

        return $this->getResponse($headers, $content);
    }
823797
protected function curlOptions($action, $request)
    {
        $options = $this->options['curlopts'] + array(
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => $this->buildHeaders($action),
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_HTTPAUTH => CURLAUTH_BASIC | CURLAUTH_NTLM,
            CURLOPT_USERPWD => $this->options['user'] . ':'
                               . $this->options['password'],
        );

        // We shouldn't allow these options to be overridden.
        $options[CURLOPT_HEADER] = true;
        $options[CURLOPT_POST] = true;
        $options[CURLOPT_POSTFIELDS] = $request;

        return $options;
    }
354356
protected function addDefaultContextOptions(RequestInterface $request)
    {
        $this->setContextValue('http', 'method', $request->getMethod());
        $this->setContextValue('http', 'header', $request->getHeaderLines());
        // Force 1.0 for now until PHP fully support chunked transfer-encoding decoding
        $this->setContextValue('http', 'protocol_version', '1.0');
        $this->setContextValue('http', 'ignore_errors', true);
    }
192710
public function post($params = array(), $options = array())
        {
            // If its an array (instead of a query string) then format it correctly
            if (Array::isArray($params)) {
                $params = http_build_query($params, null, '&');
            }

            // Add in the specific options provided
            $this->options($options);

            $this->http_method('post');

            $this->option(CURLOPT_POST, true);
            $this->option(CURLOPT_POSTFIELDS, $params);
        }
1269512
private function createHeaders(RequestInterface $request, array $curlOptions): array
    {
        $curlHeaders = [];
        $headers = $request->getHeaders();
        foreach ($headers as $name => $values) {
            $header = strtolower($name);
            if ('expect' === $header) {
                // curl-client does not support "Expect-Continue", so dropping "expect" headers
                continue;
            }
            if ('content-length' === $header) {
                if (array_key_exists(CURLOPT_POSTFIELDS, $curlOptions)) {
                    // Small body content length can be calculated here.
                    $values = [strlen($curlOptions[CURLOPT_POSTFIELDS])];
                } elseif (!array_key_exists(CURLOPT_READFUNCTION, $curlOptions)) {
                    // Else if there is no body, forcing "Content-length" to 0
                    $values = [0];
                }
            }
            foreach ($values as $value) {
                $curlHeaders[] = $name . ': ' . $value;
            }
        }
        /*
         * curl-client does not support "Expect-Continue", but cURL adds "Expect" header by default.
         * We can not suppress it, but we can set it to empty.
         */
        $curlHeaders[] = 'Expect:';

        return $curlHeaders;
    }
1235110
public function getOptions(ServerRequestInterface $request): void
    {
        parent::getOptions($request);

        $this->search  = $request->get('search', '');
        $this->replace = $request->get('replace', '');
        $this->method  = $request->get('method', 'exact');
        $this->case    = $request->get('case', '');

        $this->error = '';
        switch ($this->method) {
            case 'exact':
                $this->regex = preg_quote($this->search, '/');
                break;
            case 'words':
                $this->regex = '\b' . preg_quote($this->search, '/') . '\b';
                break;
            case 'wildcards':
                $this->regex = '\b' . str_replace([
                        '\*',
                        '\?',
                    ], [
                        '.*',
                        '.',
                    ], preg_quote($this->search, '/')) . '\b';
                break;
            case 'regex':
                $this->regex = $this->search;
                // Check for invalid regular expressions.
                // A valid regex on an empty string returns zero.
                // An invalid regex on an empty string returns false and throws a warning.
                try {
                    preg_match('/' . $this->search . '/', '');
                } catch (Throwable $ex) {
                    /* I18N: http://en.wikipedia.org/wiki/Regular_expression */
                    $this->error = '<div class="alert alert-danger">' . I18N::translate('The regular expression appears to contain an error. It can’t be used.') . '</div>';
                }
                break;
        }
    }
451091
public function doRequest($request, $options = null)
    {
        $request = array_merge($request, array(
            'Version'        => $this->_config->version,
            'ProcessorId'    => $this->_config->processorId,
            'MerchantId'     => $this->_config->merchantId,
            'TransactionPwd' => $this->_config->transactionPwd,
        ));

        $xml = new DOMDocument('1.0', 'utf-8');
        $envelope = $xml->createElement('CardinalMPI');
        foreach ($request as $k => $v) {
            $envelope->appendChild($xml->createElement($k, $v));
        }
        $xml->appendChild($envelope);

        $url = $this->_config->serviceUri();
        $xmlData = $xml->saveXML();
        $data = 'cmpi_msg=' . urlencode($xmlData);
        $header = array(
            'Content-type: application/x-www-form-urlencoded;charset="utf-8"',
            'Accept: text/xml',
            'Content-length: '.strlen($data),
        );
        // error_log($xmlData);

        return $this->submitRequest($url, $header, $data);
    }
1270239
private static function init(RequestOptions $options)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $options->Url);
        curl_setopt_array($ch, self::$defaultOptions);  //default options
        //include headers in response
        curl_setopt($ch, CURLOPT_HEADER, $options->IncludeHeaders);
        //include body in response
        curl_setopt($ch, CURLOPT_NOBODY, !$options->IncludeBody);
        //Set method
        if($options->Method == HttpMethod::Post)
            curl_setopt($ch, CURLOPT_POST, 1);
        else if($options->Method == HttpMethod::Patch)
           curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $options->Method);
        else if($options->Method == HttpMethod::Delete)
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $options->Method);
        //set Post Body
        if(isset($options->Data))
            curl_setopt($ch, CURLOPT_POSTFIELDS, $options->Data);
        if(is_resource($options->StreamHandle)) {
            $opt = $options->Method === HttpMethod::Get ? CURLOPT_FILE : CURLOPT_INFILE;
            curl_setopt($ch, $opt, $options->StreamHandle);
        }
        $options->addCustomHeader("content-length",strlen($options->Data));
        //custom HTTP headers
        if($options->Headers)
            curl_setopt($ch, CURLOPT_HTTPHEADER, $options->getRawHeaders());
        //debugging mode
        curl_setopt($ch,CURLOPT_VERBOSE, $options->Verbose);
        //SSL Version
        if(!is_null($options->SSLVersion)) {
            curl_setopt($ch,CURLOPT_SSLVERSION, $options->SSLVersion);
        }
        //authentication
        if(!is_null($options->AuthType))
            curl_setopt($ch,CURLOPT_HTTPAUTH, $options->AuthType);
        if(!is_null($options->UserCredentials))
            curl_setopt($ch,CURLOPT_USERPWD, $options->UserCredentials->toString());

        return $ch;
    }
519615
public static function send(RequestInterface $request, ClientInterface $client, array $options = [])
    {
        $response = null;
        try {
            $response = $client->send($request, $options);
            $body = $response->getBody()->getContents();
            $data = [];
            if ($body) {
                $response->getBody()->seek(0);
                $body = $response->getBody()->getContents();
                $data = \GuzzleHttp\json_decode($body, true);
            }

            return (array) $data;
        } catch (BadResponseException $e) {
            throw ApiResponseException::create($e->getRequest(), $e->getResponse());
        } catch (\InvalidArgumentException $e) {
            throw ApiResponseException::create($request, $response);
        }
    }
478307
private function onRejected(RequestInterface $request, array $options)
    {
        return function ($reason) use ($request, $options) {
            if (!$reason instanceof ClientException) {
                return new RejectedPromise($reason);
            }

            $response = $reason->getResponse();

            if ($next = $this->handleUnauthenticatedResponse($request, $response, $options)) {
                return $next;
            }

            return new RejectedPromise($reason);
        };
    }
26392
private function getCurlDefaultOptions(RequestInterface $request)
    {
        return array(
            CURLOPT_URL            => $request->getUri(),
            CURLOPT_PORT           => $request->getPort(),
            CURLOPT_CUSTOMREQUEST  => $request->getMethod(),
            CURLOPT_HTTPHEADER     => $request->getHeaderFields(),
            CURLOPT_TIMEOUT        => 10,
            CURLOPT_SSL_VERIFYPEER => 1,
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_CAINFO         => __DIR__.'/ca-bundle.crt',
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FORBID_REUSE   => 1,
            CURLOPT_FRESH_CONNECT  => 1,
            CURLOPT_HEADER         => true,
        );
    }