public static function CurlHandle::factory

Factory method to create a new curl handle based on an HTTP request.

There are some helpful options you can set to enable specific behavior:

  • debug: Set to true to enable cURL debug functionality to track the actual headers sent over the wire.
  • progress: Set to true to enable progress function callbacks.

Parameters

RequestInterface $request Request:

Return value

CurlHandle

Throws

RuntimeException

1 call to CurlHandle::factory()
CurlMulti::createCurlHandle in drupal/core/vendor/guzzle/http/Guzzle/Http/Curl/CurlMulti.php
Create a curl handle for a request

File

drupal/core/vendor/guzzle/http/Guzzle/Http/Curl/CurlHandle.php, line 49

Class

CurlHandle
Immutable wrapper for a cURL handle

Namespace

Guzzle\Http\Curl

Code

public static function factory(RequestInterface $request) {
  $mediator = new RequestMediator($request);
  $requestCurlOptions = $request
    ->getCurlOptions();
  $tempContentLength = null;
  $method = $request
    ->getMethod();
  $bodyAsString = $requestCurlOptions
    ->get(self::BODY_AS_STRING);

  // Array of default cURL options.
  $curlOptions = array(
    CURLOPT_URL => $request
      ->getUrl(),
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_RETURNTRANSFER => false,
    CURLOPT_HEADER => false,
    CURLOPT_USERAGENT => (string) $request
      ->getHeader('User-Agent'),
    CURLOPT_PORT => $request
      ->getPort(),
    CURLOPT_HTTPHEADER => array(),
    CURLOPT_HEADERFUNCTION => array(
      $mediator,
      'receiveResponseHeader',
    ),
    CURLOPT_HTTP_VERSION => $request
      ->getProtocolVersion() === '1.0' ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1,
    // Verifies the authenticity of the peer's certificate
    CURLOPT_SSL_VERIFYPEER => 1,
    // Certificate must indicate that the server is the server to which you meant to connect
    CURLOPT_SSL_VERIFYHOST => 2,
  );

  // Add CURLOPT_ENCODING if Accept-Encoding header is provided
  if ($acceptEncodingHeader = $request
    ->getHeader('Accept-Encoding')) {
    $curlOptions[CURLOPT_ENCODING] = (string) $acceptEncodingHeader;

    // Let cURL set the Accept-Encoding header, prevents duplicate values
    $request
      ->removeHeader('Accept-Encoding');
  }

  // Enable the progress function if the 'progress' param was set
  if ($requestCurlOptions
    ->get('progress')) {
    $curlOptions[CURLOPT_PROGRESSFUNCTION] = array(
      $mediator,
      'progress',
    );
    $curlOptions[CURLOPT_NOPROGRESS] = false;
  }

  // Enable curl debug information if the 'debug' param was set
  if ($requestCurlOptions
    ->get('debug')) {
    $curlOptions[CURLOPT_STDERR] = fopen('php://temp', 'r+');

    // @codeCoverageIgnoreStart
    if (false === $curlOptions[CURLOPT_STDERR]) {
      throw new RuntimeException('Unable to create a stream for CURLOPT_STDERR');
    }

    // @codeCoverageIgnoreEnd
    $curlOptions[CURLOPT_VERBOSE] = true;
  }

  // HEAD requests need no response body, everything else might
  if ($method != 'HEAD') {
    $curlOptions[CURLOPT_WRITEFUNCTION] = array(
      $mediator,
      'writeResponseBody',
    );
  }

  // Specify settings according to the HTTP method
  switch ($method) {
    case 'GET':
      $curlOptions[CURLOPT_HTTPGET] = true;
      break;
    case 'HEAD':
      $curlOptions[CURLOPT_NOBODY] = true;
      break;
    case 'POST':
      $curlOptions[CURLOPT_POST] = true;

      // Special handling for POST specific fields and files
      if (count($request
        ->getPostFiles())) {
        $fields = $request
          ->getPostFields()
          ->useUrlEncoding(false)
          ->urlEncode();
        foreach ($request
          ->getPostFiles() as $key => $data) {
          $prefixKeys = count($data) > 1;
          foreach ($data as $index => $file) {

            // Allow multiple files in the same key
            $fieldKey = $prefixKeys ? "{$key}[{$index}]" : $key;
            $fields[$fieldKey] = $file
              ->getCurlString();
          }
        }
        $curlOptions[CURLOPT_POSTFIELDS] = $fields;
        $request
          ->removeHeader('Content-Length');
      }
      elseif (count($request
        ->getPostFields())) {
        $curlOptions[CURLOPT_POSTFIELDS] = (string) $request
          ->getPostFields()
          ->useUrlEncoding(true);
        $request
          ->removeHeader('Content-Length');
      }
      elseif (!$request
        ->getBody()) {

        // Need to remove CURLOPT_POST to prevent chunked encoding for an empty POST
        unset($curlOptions[CURLOPT_POST]);
        $curlOptions[CURLOPT_CUSTOMREQUEST] = 'POST';
      }
      break;
    case 'PUT':
    case 'PATCH':
    case 'DELETE':
    default:
      $curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
      if ($bodyAsString) {

        // Remove the curl generated Content-Type header if none was set manually
        if (!$request
          ->hasHeader('Content-Type')) {
          $curlOptions[CURLOPT_HTTPHEADER][] = 'Content-Type:';
        }
      }
      else {
        $curlOptions[CURLOPT_UPLOAD] = true;

        // Let cURL handle setting the Content-Length header
        if ($tempContentLength = $request
          ->getHeader('Content-Length')) {
          $tempContentLength = (int) (string) $tempContentLength;
          $curlOptions[CURLOPT_INFILESIZE] = $tempContentLength;
        }
      }
  }

  // Special handling for requests sending raw data
  if ($request instanceof EntityEnclosingRequestInterface) {
    if ($request
      ->getBody()) {
      if ($bodyAsString) {
        $curlOptions[CURLOPT_POSTFIELDS] = (string) $request
          ->getBody();

        // Allow curl to add the Content-Length for us to account for the times when
        // POST redirects are followed by GET requests
        if ($tempContentLength = $request
          ->getHeader('Content-Length')) {
          $tempContentLength = (int) (string) $tempContentLength;
        }
      }
      else {

        // Add a callback for curl to read data to send with the request only if a body was specified
        $curlOptions[CURLOPT_READFUNCTION] = array(
          $mediator,
          'readRequestBody',
        );

        // Attempt to seek to the start of the stream
        $request
          ->getBody()
          ->seek(0);
      }
    }

    // If the Expect header is not present, prevent curl from adding it
    if (!$request
      ->hasHeader('Expect')) {
      $curlOptions[CURLOPT_HTTPHEADER][] = 'Expect:';
    }
  }

  // If a Content-Length header was specified but we want to allow curl to set one for us
  if (null !== $tempContentLength) {
    $request
      ->removeHeader('Content-Length');
  }

  // Set custom cURL options
  foreach ($requestCurlOptions as $key => $value) {
    if (is_numeric($key)) {
      $curlOptions[$key] = $value;
    }
  }

  // Do not set an Accept header by default
  if (!isset($curlOptions[CURLOPT_ENCODING])) {
    $curlOptions[CURLOPT_HTTPHEADER][] = 'Accept:';
  }

  // Check if any headers or cURL options are blacklisted
  if ($blacklist = $requestCurlOptions
    ->get('blacklist')) {
    foreach ($blacklist as $value) {
      if (strpos($value, 'header.') !== 0) {
        unset($curlOptions[$value]);
      }
      else {

        // Remove headers that may have previously been set but are supposed to be blacklisted
        $key = substr($value, 7);
        $request
          ->removeHeader($key);
        $curlOptions[CURLOPT_HTTPHEADER][] = $key . ':';
      }
    }
  }

  // Add any custom headers to the request. Empty headers will cause curl to not send the header at all.
  foreach ($request
    ->getHeaderLines() as $line) {
    $curlOptions[CURLOPT_HTTPHEADER][] = $line;
  }

  // Apply the options to a new cURL handle.
  $handle = curl_init();
  curl_setopt_array($handle, $curlOptions);
  if ($tempContentLength) {
    $request
      ->setHeader('Content-Length', $tempContentLength);
  }
  $handle = new static($handle, $curlOptions);
  $mediator
    ->setCurlHandle($handle);
  return $handle;
}