/** * {@inheritdoc} */ public function getCacheKey(RequestInterface $request) { // See if the key has already been calculated $key = $request->getParams()->get(self::CACHE_KEY); if (!$key) { $cloned = clone $request; $cloned->removeHeader('Cache-Control'); // Check to see how and if the key should be filtered foreach (explode(';', $request->getParams()->get(self::CACHE_KEY_FILTER)) as $part) { $pieces = array_map('trim', explode('=', $part)); if (isset($pieces[1])) { foreach (array_map('trim', explode(',', $pieces[1])) as $remove) { if ($pieces[0] == 'header') { $cloned->removeHeader($remove); } elseif ($pieces[0] == 'query') { $cloned->getQuery()->remove($remove); } } } } $raw = (string) $cloned; $key = 'GZ' . md5($raw); $request->getParams()->set(self::CACHE_KEY, $key)->set(self::CACHE_KEY_RAW, $raw); } return $key; }
/** * {@inheritdoc} */ public function signRequest(RequestInterface $request, CredentialsInterface $credentials) { // Add a date header if one is not set if (!$request->hasHeader('date') && !$request->hasHeader('x-amz-date')) { $request->setHeader('Date', gmdate(DateFormat::RFC2822)); } $stringToSign = (string) $request->getHeader('Date') ?: (string) $request->getHeader('x-amz-date'); $request->getParams()->set('aws.string_to_sign', $stringToSign); $request->setHeader('Authorization', 'AWS ' . $credentials->getAccessKeyId() . ':' . $this->signString($stringToSign, $credentials)); }
/** * {@inheritdoc} */ public function signRequest(RequestInterface $request, CredentialsInterface $credentials) { // Add a date header if one is not set if (!$request->hasHeader('date') && !$request->hasHeader('x-amz-date')) { $request->setHeader('Date', $this->getDateTime(DateFormat::RFC1123)); } // Add the security token if one is present if ($credentials->getSecurityToken()) { $request->setHeader('x-amz-security-token', $credentials->getSecurityToken()); } // Determine the string to sign $stringToSign = $request->getHeader('Date', true) ?: $request->getHeader('x-amz-date', true); $request->getParams()->set('aws.string_to_sign', $stringToSign); // Calculate the signature $signature = base64_encode(hash_hmac('sha256', $stringToSign, $credentials->getSecretKey(), true)); // Add the authorization header to the request $headerFormat = 'AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=HmacSHA256,Signature=%s'; $request->setHeader('X-Amzn-Authorization', sprintf($headerFormat, $credentials->getAccessKeyId(), $signature)); }
/** * 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: * - disabled_wire: This is a performance improvement that will disable * some debugging functionality with cURL. The functionality * it disabled allows you to see the exact HTTP request sent over * the wire. * - progress: Set to true to enable progress function callbacks. Most * People don't need this, so it has been disabled by default. * * @param RequestInterface $request Request * * @return CurlHandle */ public static function factory(RequestInterface $request) { $handle = curl_init(); $mediator = new RequestMediator($request); $requestCurlOptions = $request->getCurlOptions(); // Array of default cURL options. $curlOptions = array(CURLOPT_URL => $request->getUrl(), CURLOPT_CUSTOMREQUEST => $request->getMethod(), CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_RETURNTRANSFER => false, CURLOPT_HEADER => false, CURLOPT_USERAGENT => (string) $request->getHeader('User-Agent'), CURLOPT_ENCODING => '', CURLOPT_PORT => $request->getPort(), CURLOPT_HTTP_VERSION => $request->getProtocolVersion() === '1.0' ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1, CURLOPT_HTTPHEADER => array(), CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader')); // 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('disable_wire')) { $curlOptions[CURLOPT_STDERR] = fopen('php://temp', 'r+'); $curlOptions[CURLOPT_VERBOSE] = true; } // HEAD requests need no response body, everything else might if ($request->getMethod() != 'HEAD') { $curlOptions[CURLOPT_WRITEFUNCTION] = array($mediator, 'writeResponseBody'); } // Account for PHP installations with safe_mode or open_basedir enabled // @codeCoverageIgnoreStart if (Guzzle::getCurlInfo('follow_location')) { $curlOptions[CURLOPT_FOLLOWLOCATION] = true; $curlOptions[CURLOPT_MAXREDIRS] = 5; } // @codeCoverageIgnoreEnd $headers = $request->getHeaders()->getAll(); // Specify settings according to the HTTP method switch ($request->getMethod()) { case 'GET': $curlOptions[CURLOPT_HTTPGET] = true; break; case 'HEAD': $curlOptions[CURLOPT_NOBODY] = true; unset($curlOptions[CURLOPT_WRITEFUNCTION]); break; case 'POST': $curlOptions[CURLOPT_POST] = true; break; case 'PUT': case 'PATCH': $curlOptions[CURLOPT_UPLOAD] = true; if ($request->hasHeader('Content-Length')) { unset($headers['Content-Length']); $curlOptions[CURLOPT_INFILESIZE] = (int) (string) $request->getHeader('Content-Length'); } break; } if ($request instanceof EntityEnclosingRequestInterface) { // If no body is being sent, always send Content-Length of 0 if (!$request->getBody() && !count($request->getPostFields())) { $headers['Content-Length'] = 0; unset($headers['Transfer-Encoding']); // Need to remove CURLOPT_UPLOAD to prevent chunked encoding unset($curlOptions[CURLOPT_UPLOAD]); unset($curlOptions[CURLOPT_POST]); // Not reading from a callback when using empty body unset($curlOptions[CURLOPT_READFUNCTION]); } else { // Add a callback for curl to read data to send with the request $curlOptions[CURLOPT_READFUNCTION] = array($mediator, 'readRequestBody'); } // If the Expect header is not present, prevent curl from adding it if (!$request->hasHeader('Expect')) { $curlOptions[CURLOPT_HTTPHEADER][] = 'Expect:'; } } // Set custom cURL options foreach ($requestCurlOptions as $key => $value) { if (is_numeric($key)) { $curlOptions[$key] = $value; } } // Check if any headers or cURL options are blacklisted $client = $request->getClient(); if ($client) { $blacklist = $client->getConfig('curl.blacklist'); if ($blacklist) { foreach ($blacklist as $value) { if (strpos($value, 'header.') === 0) { $blacklistHeader = substr($value, 7); // Remove headers that may have previously been set // but are supposed to be blacklisted unset($headers[$blacklistHeader]); $headers[$blacklistHeader] = ''; } else { unset($curlOptions[$value]); } } } } // Add any custom headers to the request. Emtpy headers will cause curl to // not send the header at all. foreach ($headers as $key => $value) { foreach ((array) $value as $val) { $curlOptions[CURLOPT_HTTPHEADER][] = trim("{$key}: {$val}"); } } // Apply the options to the cURL handle. curl_setopt_array($handle, $curlOptions); $request->getParams()->set('curl.last_options', $curlOptions); return new static($handle, $curlOptions); }
/** * Create a canonicalized resource for a request * * @param RequestInterface $request Request for the resource * * @return string */ private function createCanonicalizedResource(RequestInterface $request) { $buffer = $request->getParams()->get('s3.resource'); // When sending a raw HTTP request (e.g. $client->get()) if (null === $buffer) { $bucket = $request->getParams()->get('bucket') ?: $this->parseBucketName($request); // Use any specified bucket name, the parsed bucket name, or no bucket name when interacting with GetService $buffer = $bucket ? "/{$bucket}" : ''; // Remove encoding from the path and use the S3 specific encoding $path = ObsClient::encodeKey(rawurldecode($request->getPath())); // if the bucket was path style, then ensure that the bucket wasn't duplicated in the resource $buffer .= preg_replace("#^/{$bucket}/{$bucket}#", "/{$bucket}", $path); } // Remove double slashes $buffer = str_replace('//', '/', $buffer); // Add sub resource parameters $query = $request->getQuery(); $first = true; foreach ($this->signableQueryString as $key) { if ($query->hasKey($key)) { $value = $query[$key]; $buffer .= $first ? '?' : '&'; $first = false; $buffer .= $key; // Don't add values for empty sub-resources if ($value !== '' && $value !== false && $value !== null && $value !== QueryString::BLANK) { $buffer .= "={$value}"; } } } return $buffer; }
/** * Add the plugin's headers to a response * * @param RequestInterface $request Request * @param Response $response Response to add headers to */ protected function addResponseHeaders(RequestInterface $request, Response $response) { $params = $request->getParams(); $response->setHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION)); $lookup = ($params['cache.lookup'] === true ? 'HIT' : 'MISS') . ' from GuzzleCache'; if ($header = $response->getHeader('X-Cache-Lookup')) { // Don't add duplicates $values = $header->toArray(); $values[] = $lookup; $response->setHeader('X-Cache-Lookup', array_unique($values)); } else { $response->setHeader('X-Cache-Lookup', $lookup); } if ($params['cache.hit'] === true) { $xcache = 'HIT from GuzzleCache'; } elseif ($params['cache.hit'] == 'error') { $xcache = 'HIT_ERROR from GuzzleCache'; } else { $xcache = 'MISS from GuzzleCache'; } if ($header = $response->getHeader('X-Cache')) { // Don't add duplicates $values = $header->toArray(); $values[] = $xcache; $response->setHeader('X-Cache', array_unique($values)); } else { $response->setHeader('X-Cache', $xcache); } if ($response->isFresh() === false) { $response->addHeader('Warning', sprintf('110 GuzzleCache/%s "Response is stale"', Version::VERSION)); if ($params['cache.hit'] === 'error') { $response->addHeader('Warning', sprintf('111 GuzzleCache/%s "Revalidation failed"', Version::VERSION)); } } }
protected function visit_params(RequestInterface $request, $value, $flags) { if (!is_array($value)) { throw new InvalidArgumentException('params value must be an array'); } $request->getParams()->overwriteWith($value); }
/** * Trigger a request to retry * * @param RequestInterface $request Request to retry */ protected function retryRequest(RequestInterface $request) { $params = $request->getParams(); $retries = (int) $params->get(self::RETRY_PARAM) + 1; $params->set(self::RETRY_PARAM, $retries); // If this request has been retried too many times, then throw an exception if ($retries <= $this->maxRetries) { // Calculate how long to wait until the request should be retried $delay = call_user_func($this->delayClosure, $retries); $delayTime = microtime(true) + $delay; // Send the request again $request->setState(RequestInterface::STATE_TRANSFER); $params->set(self::DELAY_PARAM, $delay); } }
/** * Hash a request URL into a string that returns cache metadata * * @param RequestInterface $request * * @return string */ protected function getCacheKey(RequestInterface $request) { // Allow cache.key_filter to trim down the URL cache key by removing generate query string values (e.g. auth) if ($filter = $request->getParams()->get('cache.key_filter')) { $url = $request->getUrl(true); foreach (explode(',', $filter) as $remove) { $url->getQuery()->remove(trim($remove)); } } else { $url = $request->getUrl(); } return $this->keyPrefix . md5($request->getMethod() . ' ' . $url); }
/** * Prepare the request for redirection and enforce the maximum number of allowed redirects per client * * @param RequestInterface $request Request to prepare and validate * * @return RequestInterface Returns the original request */ protected function prepareRedirection(RequestInterface $request) { $original = $request; // The number of redirects is held on the original request, so determine which request that is while ($parent = $original->getParams()->get(self::PARENT_REQUEST)) { $original = $parent; } // Always associate the parent response with the current response so that a chain can be established if ($parent = $request->getParams()->get(self::PARENT_REQUEST)) { $request->getResponse()->setPreviousResponse($parent->getResponse()); } $params = $original->getParams(); // This is a new redirect, so increment the redirect counter $current = $params->get(self::REDIRECT_COUNT) + 1; $params->set(self::REDIRECT_COUNT, $current); // Use a provided maximum value or default to a max redirect count of 5 $max = $params->hasKey(self::MAX_REDIRECTS) ? $params->get(self::MAX_REDIRECTS) : $this->defaultMaxRedirects; // Throw an exception if the redirect count is exceeded if ($current > $max) { return $this->throwTooManyRedirectsException($request); } return $original; }
/** * Factory method to create a new curl handle based on an HTTP request. * * Note that the HTTP request may be modified by this method * * There are some helpful options you can set to enable specific behavior: * * - disabled_wire: This is a performance improvement that will disable * some debugging functionality with cURL. The * functionality it disabled allows you to see the * exact HTTP request sent over the wire. * - progress: Set to true to enable progress function callbacks. * Most people don't need this, so it has been disabled * by default. * * @param RequestInterface $request Request * * @return CurlHandle */ public static function factory(RequestInterface $request) { $mediator = new RequestMediator($request); $requestCurlOptions = $request->getCurlOptions(); $tempHeaders = array(); // Array of default cURL options. $curlOptions = array(CURLOPT_URL => $request->getUrl(), CURLOPT_CUSTOMREQUEST => $request->getMethod(), CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_RETURNTRANSFER => false, CURLOPT_HEADER => false, CURLOPT_USERAGENT => (string) $request->getHeader('User-Agent'), CURLOPT_ENCODING => '', CURLOPT_PORT => $request->getPort(), CURLOPT_HTTP_VERSION => $request->getProtocolVersion() === '1.0' ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1, CURLOPT_HTTPHEADER => array(), CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader')); // 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('disable_wire')) { $curlOptions[CURLOPT_STDERR] = fopen('php://temp', 'r+'); $curlOptions[CURLOPT_VERBOSE] = true; } // HEAD requests need no response body, everything else might if ($request->getMethod() != 'HEAD') { $curlOptions[CURLOPT_WRITEFUNCTION] = array($mediator, 'writeResponseBody'); } // Account for PHP installations with safe_mode or open_basedir enabled // @codeCoverageIgnoreStart if (CurlVersion::getInstance()->get('follow_location')) { $curlOptions[CURLOPT_FOLLOWLOCATION] = true; $curlOptions[CURLOPT_MAXREDIRS] = 5; } // @codeCoverageIgnoreEnd // Specify settings according to the HTTP method switch ($request->getMethod()) { 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())) { $curlOptions[CURLOPT_POSTFIELDS] = $request->getPostFields()->getAll(); $request->removeHeader('Content-Length'); } elseif (count($request->getPostFields())) { $curlOptions[CURLOPT_POSTFIELDS] = (string) $request->getPostFields(); $request->removeHeader('Content-Length'); } break; case 'PUT': case 'PATCH': $curlOptions[CURLOPT_UPLOAD] = true; // Let cURL handle setting the Content-Length header $contentLength = $request->getHeader('Content-Length'); if ($contentLength != null) { $contentLength = (int) (string) $contentLength; $curlOptions[CURLOPT_INFILESIZE] = $contentLength; $tempHeaders['Content-Length'] = $contentLength; $request->removeHeader('Content-Length'); } break; } // Special handling for requests sending raw data if ($request instanceof EntityEnclosingRequestInterface) { // Don't modify POST requests using POST fields and files via cURL if (!isset($curlOptions[CURLOPT_POSTFIELDS])) { if ($request->getBody()) { // 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'); } else { // If no body is being sent, always send Content-Length of 0 $request->setHeader('Content-Length', 0); $request->removeHeader('Transfer-Encoding'); // Need to remove CURLOPT_UPLOAD to prevent chunked encoding unset($curlOptions[CURLOPT_UPLOAD]); unset($curlOptions[CURLOPT_POST]); } } // If the Expect header is not present, prevent curl from adding it if (!$request->hasHeader('Expect')) { $curlOptions[CURLOPT_HTTPHEADER][] = 'Expect:'; } } // Set custom cURL options foreach ($requestCurlOptions as $key => $value) { if (is_numeric($key)) { $curlOptions[$key] = $value; } } // Check if any headers or cURL options are blacklisted $client = $request->getClient(); if ($client) { $blacklist = $client->getConfig('curl.blacklist'); if ($blacklist) { foreach ($blacklist as $value) { if (strpos($value, 'header.') === 0) { // 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 . ':'; } else { unset($curlOptions[$value]); } } } } // Add any custom headers to the request. Emtpy headers will cause curl to // not send the header at all. foreach ($request->getHeaders() as $headerName => $values) { foreach ($values as $value) { $curlOptions[CURLOPT_HTTPHEADER][] = trim("{$headerName}: {$value}"); } } // Apply the options to a new cURL handle. $handle = curl_init(); curl_setopt_array($handle, $curlOptions); $request->getParams()->set('curl.last_options', $curlOptions); // Some fields need to be removed from the request in order to properly // send a cURL request message. The fields that were removed for this // purpose (e.g. Content-Length) should be aggregated in this array and // added back to the request. Does not apply to blacklisted headers. foreach ($tempHeaders as $key => $value) { $request->setHeader($key, $value); } return new static($handle, $curlOptions); }
/** * Get application and plugin specific parameters set on the message. * * @return Collection */ public function getParams() { return $this->wrapped->getParams(); }
/** * Prepare a request to be sent from the Client by adding client specific behaviors and properties to the request. * * @param RequestInterface $request Request to prepare for the client * * @return RequestInterface */ protected function prepareRequest(RequestInterface $request) { $request->setClient($this); // Add any curl options to the request if ($options = $this->config->get(self::CURL_OPTIONS)) { $request->getCurlOptions()->merge(CurlHandle::parseCurlConfig($options)); } // Add request parameters to the request if ($options = $this->config->get(self::REQUEST_PARAMS)) { $request->getParams()->merge($options); } // Attach client observers to the request $request->setEventDispatcher(clone $this->getEventDispatcher()); // Set the User-Agent if one is specified on the client but not explicitly on the request if ($this->userAgent && !$request->hasHeader('User-Agent')) { $request->setHeader('User-Agent', $this->userAgent); } $this->dispatch('client.create_request', array('client' => $this, 'request' => $request)); return $request; }
/** * Sends a request. * * @param \Guzzle\Http\Message\RequestInterface $request The request. * * @throws \Widop\HttpAdapter\HttpAdapterException If an error occured. * * @return \Widop\HttpAdapter\HttpResponse The response. */ private function sendRequest(RequestInterface $request) { $request->getParams()->set('redirect.max', $this->getMaxRedirects()); try { $response = $request->send(); } catch (\Exception $e) { throw HttpAdapterException::cannotFetchUrl($request->getUrl(), $this->getName(), $e->getMessage()); } return $this->createResponse($response->getStatusCode(), $request->getUrl(), $response->getHeaders()->toArray(), $response->getBody(true), $response->getEffectiveUrl()); }
/** * Prepare a request to be sent from the Client by adding client specific * behaviors and properties to the request. * * @param RequestInterface $request Request to prepare for the client * * @return RequestInterface */ protected function prepareRequest(RequestInterface $request) { $request->setClient($this); foreach ($this->getConfig()->getAll() as $key => $value) { if ($key == 'curl.blacklist') { continue; } // Add any curl options that might in the config to the request if (strpos($key, 'curl.') === 0) { $curlOption = substr($key, 5); // Convert constants represented as string to constant int values if (defined($curlOption)) { $value = is_string($value) && defined($value) ? constant($value) : $value; $curlOption = constant($curlOption); } $request->getCurlOptions()->set($curlOption, $value); } elseif (strpos($key, 'params.') === 0) { // Add request specific parameters to all requests (prefix with 'params.') $request->getParams()->set(substr($key, 7), $value); } } // Attach client observers to the request $request->setEventDispatcher(clone $this->getEventDispatcher()); $this->dispatch('client.create_request', array('client' => $this, 'request' => $request)); return $request; }
/** * Set the associated CurlHandle object * * @param CurlHandle $handle Curl handle * * @return RequestMediator */ public function setCurlHandle(CurlHandle $handle) { $this->curlHandle = $handle; $this->request->getParams()->set('curl_handle', $handle); return $this; }
/** * Prepare a request to be sent from the Client by adding client specific * behaviors and properties to the request. * * @param RequestInterface $request Request to prepare for the client * * @return RequestInterface */ protected function prepareRequest(RequestInterface $request) { $request->setClient($this); // Add any curl options to the request $request->getCurlOptions()->merge(CurlHandle::parseCurlConfig($this->config)); foreach ($this->config as $key => $value) { if (strpos($key, 'params.') === 0) { // Add request specific parameters to all requests (prefix with 'params.') $request->getParams()->set(substr($key, 7), $value); } } // Attach client observers to the request $request->setEventDispatcher(clone $this->getEventDispatcher()); $this->dispatch('client.create_request', array('client' => $this, 'request' => $request)); return $request; }
/** * Add the plugin's headers to a response * * @param string $cacheKey Cache key * @param RequestInterface $request Request * @param Response $response Response to add headers to */ protected function addResponseHeaders($cacheKey, RequestInterface $request, Response $response) { if (!$response->hasHeader('X-Guzzle-Cache')) { $response->setHeader('X-Guzzle-Cache', "key={$cacheKey}"); } $response->addHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION)); if ($this->debugHeaders) { if ($request->getParams()->get('cache.lookup') === true) { $response->addHeader('X-Cache-Lookup', 'HIT from GuzzleCache'); } else { $response->addHeader('X-Cache-Lookup', 'MISS from GuzzleCache'); } if ($request->getParams()->get('cache.hit') === true) { $response->addHeader('X-Cache', 'HIT from GuzzleCache'); } elseif ($request->getParams()->get('cache.hit') === 'error') { $response->addHeader('X-Cache', 'HIT_ERROR from GuzzleCache'); } else { $response->addHeader('X-Cache', 'MISS from GuzzleCache'); } } if ($response->isFresh() === false) { $response->addHeader('Warning', sprintf('110 GuzzleCache/%s "Response is stale"', Version::VERSION)); if ($request->getParams()->get('cache.hit') === 'error') { $response->addHeader('Warning', sprintf('111 GuzzleCache/%s "Revalidation failed"', Version::VERSION)); } } }
/** * 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. The * - progress: Set to true to enable progress function callbacks. Most * users do not need this, so it has been disabled by default. * * @param RequestInterface $request Request * * @return CurlHandle */ public static function factory(RequestInterface $request) { $mediator = new RequestMediator($request); $requestCurlOptions = $request->getCurlOptions(); $tempContentLength = null; $method = $request->getMethod(); $client = $request->getClient(); // 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_ENCODING => '', 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); // 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'); } // Account for PHP installations with safe_mode or open_basedir enabled // @codeCoverageIgnoreStart if (CurlVersion::getInstance()->get('follow_location')) { $curlOptions[CURLOPT_FOLLOWLOCATION] = true; $curlOptions[CURLOPT_MAXREDIRS] = 5; } // @codeCoverageIgnoreEnd // 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': $curlOptions[CURLOPT_UPLOAD] = true; if ($method != 'PUT') { $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; } // Let cURL handle setting the Content-Length header $contentLength = $request->getHeader('Content-Length'); if ($contentLength !== null) { $contentLength = (int) (string) $contentLength; $curlOptions[CURLOPT_INFILESIZE] = $contentLength; $tempContentLength = $contentLength; $request->removeHeader('Content-Length'); } break; default: $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; } // Special handling for requests sending raw data if ($request instanceof EntityEnclosingRequestInterface) { if ($request->getBody()) { // 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:'; } } // Set custom cURL options foreach ($requestCurlOptions as $key => $value) { if (is_numeric($key)) { $curlOptions[$key] = $value; } } // Check if any headers or cURL options are blacklisted if ($client && ($blacklist = $client->getConfig('curl.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); $request->getParams()->set('curl.last_options', $curlOptions); if ($tempContentLength) { $request->setHeader('Content-Length', $tempContentLength); } $handle = new static($handle, $curlOptions); $mediator->setCurlHandle($handle); return $handle; }
/** * Prepare a request to be sent from the Client by adding client specific behaviors and properties to the request. * * @param RequestInterface $request Request to prepare for the client * * @return RequestInterface */ protected function prepareRequest(RequestInterface $request) { $request->setClient($this); // Add any curl options to the request if ($options = $this->config->get(self::CURL_OPTIONS)) { $request->getCurlOptions()->merge(CurlHandle::parseCurlConfig($options)); } // Add request parameters to the request if ($options = $this->config->get(self::REQUEST_PARAMS)) { $request->getParams()->merge($options); } // Attach client observers to the request $request->setEventDispatcher(clone $this->getEventDispatcher()); $this->dispatch('client.create_request', array('client' => $this, 'request' => $request)); return $request; }
/** * Parse cURL log messages * * @param RequestInterface $request Request that has a curl handle * * @return string */ protected function parseCurlLog(RequestInterface $request) { $message = ''; $handle = $request->getParams()->get('curl_handle'); $stderr = $handle->getStderr(true); if ($stderr) { rewind($stderr); while ($line = fgets($stderr)) { // * - Debug | < - Downstream | > - Upstream if ($line[0] == '*') { if ($this->settings & self::LOG_DEBUG) { $message .= $line; } } elseif ($this->settings & self::LOG_HEADERS) { $message .= $line; } // Add the request body if needed if ($this->settings & self::LOG_BODY) { if (trim($line) == '' && $request instanceof EntityEnclosingRequestInterface) { if ($request->getParams()->get('request_wire')) { $message .= (string) $request->getParams()->get('request_wire') . "\r\n"; } else { $message .= (string) $request->getBody() . "\r\n"; } } } } } return $message; }
/** * Prepare the request for redirection and enforce the maximum number of allowed redirects per client * * @param RequestInterface $original Original request * @param RequestInterface $request Request to prepare and validate * @param Response $response The current response * * @return RequestInterface */ protected function prepareRedirection(RequestInterface $original, RequestInterface $request, Response $response) { $params = $original->getParams(); // This is a new redirect, so increment the redirect counter $current = $params[self::REDIRECT_COUNT] + 1; $params[self::REDIRECT_COUNT] = $current; // Use a provided maximum value or default to a max redirect count of 5 $max = isset($params[self::MAX_REDIRECTS]) ? $params[self::MAX_REDIRECTS] : $this->defaultMaxRedirects; // Throw an exception if the redirect count is exceeded if ($current > $max) { $this->throwTooManyRedirectsException($original, $max); return false; } else { // Create a redirect request based on the redirect rules set on the request return $this->createRedirectRequest($request, $response->getStatusCode(), trim($response->getLocation()), $original); } }
/** * @param RequestInterface $request Request to mediate */ public function __construct(RequestInterface $request) { $this->request = $request; $this->emitIo = $request->getParams()->get('curl.emit_io'); }
/** * Clone a request while changing the method. Emulates the behavior of * {@see Guzzle\Http\Message\Request::clone}, but can change the HTTP method. * * @param RequestInterface $request Request to clone * @param string $method Method to set * * @return RequestInterface */ public function cloneRequestWithMethod(RequestInterface $request, $method) { // Create the request with the same client if possible if ($client = $request->getClient()) { $cloned = $request->getClient()->createRequest($method, $request->getUrl(), $request->getHeaders()); } else { $cloned = $this->create($method, $request->getUrl(), $request->getHeaders()); } $cloned->getCurlOptions()->replace($request->getCurlOptions()->getAll()); $cloned->setEventDispatcher(clone $request->getEventDispatcher()); // Ensure that that the Content-Length header is not copied if changing to GET or HEAD if (!$cloned instanceof EntityEnclosingRequestInterface) { $cloned->removeHeader('Content-Length'); } elseif ($request instanceof EntityEnclosingRequestInterface) { $cloned->setBody($request->getBody()); } $cloned->getParams()->replace($request->getParams()->getAll())->remove('curl_handle')->remove('curl_multi'); return $cloned; }
protected function getCacheKey(RequestInterface $request) { if ($filter = $request->getParams()->get('cache.key_filter')) { $url = $request->getUrl(true); foreach (explode(',', $filter) as $remove) { $url->getQuery()->remove(trim($remove)); } } else { $url = $request->getUrl(); } return $this->keyPrefix . md5($request->getMethod() . ' ' . $url); }
/** * Prepare a request to be sent from the Client by adding client specific behaviors and properties to the request. * * @param RequestInterface $request Request to prepare for the client * @param array $options Options to apply to the request * * @return RequestInterface */ protected function prepareRequest(RequestInterface $request, array $options = array()) { $request->setClient($this)->setEventDispatcher(clone $this->getEventDispatcher()); if ($curl = $this->config[self::CURL_OPTIONS]) { $request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($curl)); } if ($params = $this->config[self::REQUEST_PARAMS]) { Version::warn('request.params is deprecated. Use request.options to add default request options.'); $request->getParams()->overwriteWith($params); } if ($this->userAgent && !$request->hasHeader('User-Agent')) { $request->setHeader('User-Agent', $this->userAgent); } if ($defaults = $this->config[self::REQUEST_OPTIONS]) { $this->requestFactory->applyOptions($request, $defaults, RequestFactoryInterface::OPTIONS_AS_DEFAULTS); } if ($options) { $this->requestFactory->applyOptions($request, $options); } $this->dispatch('client.create_request', array('client' => $this, 'request' => $request)); return $request; }
protected function prepareRedirection(RequestInterface $original, RequestInterface $request, Response $response) { $params = $original->getParams(); $current = $params[self::REDIRECT_COUNT] + 1; $params[self::REDIRECT_COUNT] = $current; $max = isset($params[self::MAX_REDIRECTS]) ? $params[self::MAX_REDIRECTS] : $this->defaultMaxRedirects; if ($current > $max) { $this->throwTooManyRedirectsException($original, $max); return false; } else { return $this->createRedirectRequest($request, $response->getStatusCode(), trim($response->getLocation()), $original); } }
/** * Check if a cache response satisfies a request's caching constraints * * @param RequestInterface $request Request to validate * @param Response $response Response to validate * * @return bool */ public function canResponseSatisfyRequest(RequestInterface $request, Response $response) { $responseAge = $response->getAge(); // Check the request's max-age header against the age of the response if ($request->hasCacheControlDirective('max-age') && $responseAge > $request->getCacheControlDirective('max-age')) { return false; } // Check the response's max-age header if ($response->isFresh() === false) { $maxStale = $request->getCacheControlDirective('max-stale'); if (null !== $maxStale) { if ($maxStale !== true && $response->getFreshness() < -1 * $maxStale) { return false; } } elseif ($response->hasCacheControlDirective('max-age') && $responseAge > $response->getCacheControlDirective('max-age')) { return false; } } // Only revalidate GET requests if ($request->getMethod() == RequestInterface::GET) { // Check if the response must be validated against the origin server if ($request->getHeader('Pragma') == 'no-cache' || $request->hasCacheControlDirective('no-cache') || $request->hasCacheControlDirective('must-revalidate') || $response->hasCacheControlDirective('must-revalidate') || $response->hasCacheControlDirective('no-cache')) { // no-cache: When no parameters are present, always revalidate // When parameters are present in no-cache and the request includes those same parameters, then the // response must re-validate. I'll need an example of what fields look like in order to implement a // smarter version of no-cache // Requests can decline to revalidate against the origin server by setting the cache.revalidate param: // - never - To never revalidate and always contact the origin server // - skip - To skip revalidation and just use what is in cache switch ($request->getParams()->get('cache.revalidate')) { case 'never': return false; case 'skip': return true; default: return $this->revalidation->revalidate($request, $response); } } } return true; }
/** * Trigger a request to retry * * @param RequestInterface $request Request to retry * @param Response $response Response received * @param CurlHandle $handle Curl handle */ protected function retryRequest(RequestInterface $request, Response $response = null, CurlHandle $handle = null) { $params = $request->getParams(); $retries = (int) $params->get(self::RETRY_PARAM) + 1; $params->set(self::RETRY_PARAM, $retries); // If this request has been retried too many times, then throw an exception if ($retries <= $this->maxRetries) { // Calculate how long to wait until the request should be retried $delay = call_user_func($this->delayClosure, $retries, $request); $delayTime = microtime(true) + $delay; // Send the request again $request->setState(RequestInterface::STATE_TRANSFER); $params->set(self::DELAY_PARAM, $delayTime); $this->dispatch(self::RETRY_EVENT, array('request' => $request, 'response' => $response, 'handle' => $handle, 'retries' => $retries, 'delay' => $delay)); } }