Example #1
0
 public function canCacheRequest(RequestInterface $request)
 {
     // Only GET and HEAD requests can be cached
     if ($request->getMethod() != RequestInterface::GET && $request->getMethod() != RequestInterface::HEAD) {
         return false;
     }
     // Never cache requests when using no-store
     if ($request->hasHeader('Cache-Control') && $request->getHeader('Cache-Control')->hasDirective('no-store')) {
         return false;
     }
     return true;
 }
Example #2
0
 public function shouldRevalidate(RequestInterface $request, Response $response)
 {
     if ($request->getMethod() != RequestInterface::GET) {
         return false;
     }
     $reqCache = $request->getHeader('Cache-Control');
     $resCache = $response->getHeader('Cache-Control');
     $revalidate = $request->getHeader('Pragma') == 'no-cache' || $reqCache && ($reqCache->hasDirective('no-cache') || $reqCache->hasDirective('must-revalidate')) || $resCache && ($resCache->hasDirective('no-cache') || $resCache->hasDirective('must-revalidate'));
     // Use the strong ETag validator if available and the response contains no Cache-Control directive
     if (!$revalidate && !$resCache && $response->hasHeader('ETag')) {
         $revalidate = true;
     }
     return $revalidate;
 }
Example #3
0
 /**
  * Get the canonicalized query/parameter string for a request
  *
  * @param RequestInterface $request Request used to build canonicalized string
  *
  * @return string
  */
 private function getCanonicalizedParameterString(RequestInterface $request)
 {
     if ($request->getMethod() == 'POST') {
         $params = $request->getPostFields()->toArray();
     } else {
         $params = $request->getQuery()->toArray();
     }
     // Don't resign a previous signature value
     unset($params['Signature']);
     uksort($params, 'strcmp');
     $str = '';
     foreach ($params as $key => $val) {
         $str .= rawurlencode($key) . '=' . rawurlencode($val) . '&';
     }
     return substr($str, 0, -1);
 }
Example #4
0
 /**
  * Create the canonical representation of a request
  *
  * @param RequestInterface $request Request to canonicalize
  * @param string           $payload Request payload (typically the value
  *                                  of the x-amz-content-sha256 header.
  *
  * @return array Returns an array of context information including:
  *               - canonical_request
  *               - signed_headers
  */
 private function createSigningContext(RequestInterface $request, $payload)
 {
     // Normalize the path as required by SigV4 and ensure it's absolute
     $canon = $request->getMethod() . "\n" . $this->createCanonicalizedPath($request) . "\n" . $this->getCanonicalizedQueryString($request) . "\n";
     $canonHeaders = array();
     foreach ($request->getHeaders()->getAll() as $key => $values) {
         $key = strtolower($key);
         if ($key == 'host' || $key == 'content-md5' || $key == 'date' || substr($key, 0, 6) === 'x-amz-') {
             $values = $values->toArray();
             if (count($values) == 1) {
                 $values = $values[0];
             } else {
                 sort($values);
                 $values = implode(',', $values);
             }
             $canonHeaders[$key] = $key . ':' . preg_replace('/\\s+/', ' ', $values);
         }
     }
     ksort($canonHeaders);
     $signedHeadersString = implode(';', array_keys($canonHeaders));
     $canon .= implode("\n", $canonHeaders) . "\n\n" . $signedHeadersString . "\n" . $payload;
     return array('canonical_request' => $canon, 'signed_headers' => $signedHeadersString);
 }
Example #5
0
 /**
  * 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.
  *
  * @param RequestInterface $request Request
  *
  * @return CurlHandle
  * @throws RuntimeException
  */
 public static function factory(RequestInterface $request)
 {
     $requestCurlOptions = $request->getCurlOptions();
     $mediator = new RequestMediator($request, $requestCurlOptions->get('emit_io'));
     $tempContentLength = null;
     $method = $request->getMethod();
     $bodyAsString = $requestCurlOptions->get(self::BODY_AS_STRING);
     // Prepare url
     $url = (string) $request->getUrl();
     if (($pos = strpos($url, '#')) !== false) {
         // strip fragment from url
         $url = substr($url, 0, $pos);
     }
     // Array of default cURL options.
     $curlOptions = array(CURLOPT_URL => $url, CURLOPT_CONNECTTIMEOUT => 150, CURLOPT_RETURNTRANSFER => false, CURLOPT_HEADER => false, CURLOPT_PORT => $request->getPort(), CURLOPT_HTTPHEADER => array(), CURLOPT_WRITEFUNCTION => array($mediator, 'writeResponseBody'), CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader'), CURLOPT_HTTP_VERSION => $request->getProtocolVersion() === '1.0' ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1, CURLOPT_SSL_VERIFYPEER => 1, CURLOPT_SSL_VERIFYHOST => 2);
     if (defined('CURLOPT_PROTOCOLS')) {
         // Allow only HTTP and HTTPS protocols
         $curlOptions[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
     }
     // 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 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;
     }
     // Specify settings according to the HTTP method
     if ($method == 'GET') {
         $curlOptions[CURLOPT_HTTPGET] = true;
     } elseif ($method == 'HEAD') {
         $curlOptions[CURLOPT_NOBODY] = true;
         // HEAD requests do not use a write function
         unset($curlOptions[CURLOPT_WRITEFUNCTION]);
     } elseif (!$request instanceof EntityEnclosingRequest) {
         $curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
     } else {
         $curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
         // Handle sending raw bodies in a request
         if ($request->getBody()) {
             // You can send the body as a string using curl's CURLOPT_POSTFIELDS
             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;
                 }
                 // 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;
                 }
                 // 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);
             }
         } else {
             // Special handling for POST specific fields and files
             $postFields = false;
             if (count($request->getPostFiles())) {
                 $postFields = $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;
                         $postFields[$fieldKey] = $file->getCurlValue();
                     }
                 }
             } elseif (count($request->getPostFields())) {
                 $postFields = (string) $request->getPostFields()->useUrlEncoding(true);
             }
             if ($postFields !== false) {
                 if ($method == 'POST') {
                     unset($curlOptions[CURLOPT_CUSTOMREQUEST]);
                     $curlOptions[CURLOPT_POST] = true;
                 }
                 $curlOptions[CURLOPT_POSTFIELDS] = $postFields;
                 $request->removeHeader('Content-Length');
             }
         }
         // 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->toArray() 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:';
     }
     // 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;
     }
     // Add the content-length header back if it was temporarily removed
     if ($tempContentLength) {
         $request->setHeader('Content-Length', $tempContentLength);
     }
     // Apply the options to a new cURL handle.
     $handle = curl_init();
     // Enable the progress function if the 'progress' param was set
     if ($requestCurlOptions->get('progress')) {
         // Wrap the function in a function that provides the curl handle to the mediator's progress function
         // Using this rather than injecting the handle into the mediator prevents a circular reference
         $curlOptions[CURLOPT_PROGRESSFUNCTION] = function () use($mediator, $handle) {
             $args = func_get_args();
             $args[] = $handle;
             // PHP 5.5 pushed the handle onto the start of the args
             if (is_resource($args[0])) {
                 array_shift($args);
             }
             call_user_func_array(array($mediator, 'progress'), $args);
         };
         $curlOptions[CURLOPT_NOPROGRESS] = false;
     }
     curl_setopt_array($handle, $curlOptions);
     return new static($handle, $curlOptions);
 }
Example #6
0
 /**
  * Adds the default context options to the stream context options
  *
  * @param RequestInterface $request Request
  */
 protected function addDefaultContextOptions(RequestInterface $request)
 {
     $this->setContextValue('http', 'method', $request->getMethod());
     $headers = $request->getHeaderLines();
     // "Connection: close" is required to get streams to work in HTTP 1.1
     if (!$request->hasHeader('Connection')) {
         $headers[] = 'Connection: close';
     }
     $this->setContextValue('http', 'header', $headers);
     $this->setContextValue('http', 'protocol_version', $request->getProtocolVersion());
     $this->setContextValue('http', 'ignore_errors', true);
 }
Example #7
0
 public function createCanonicalizedString(RequestInterface $request, $expires = null)
 {
     $buffer = $request->getMethod() . "\n";
     // Add the interesting headers
     foreach ($this->signableHeaders as $header) {
         $buffer .= (string) $request->getHeader($header) . "\n";
     }
     // Choose dates from left to right based on what's set
     $date = $expires ?: (string) $request->getHeader('date');
     $buffer .= "{$date}\n" . $this->createCanonicalizedAmzHeaders($request) . $this->createCanonicalizedResource($request);
     return $buffer;
 }
Example #8
0
 /**
  * Returns a formatted message
  *
  * @param RequestInterface $request    Request that was sent
  * @param Response         $response   Response that was received
  * @param CurlHandle       $handle     Curl handle associated with the message
  * @param array            $customData Associative array of custom template data
  *
  * @return string
  */
 public function format(RequestInterface $request, Response $response = null, CurlHandle $handle = null, array $customData = array())
 {
     $cache = $customData;
     return preg_replace_callback('/{\\s*([A-Za-z_\\-\\.0-9]+)\\s*}/', function (array $matches) use($request, $response, $handle, &$cache) {
         if (array_key_exists($matches[1], $cache)) {
             return $cache[$matches[1]];
         }
         $result = '';
         switch ($matches[1]) {
             case 'request':
                 $result = (string) $request;
                 break;
             case 'response':
                 $result = (string) $response;
                 break;
             case 'req_body':
                 $result = $request instanceof EntityEnclosingRequestInterface ? (string) $request->getBody() : '';
                 break;
             case 'res_body':
                 $result = $response ? $response->getBody(true) : '';
                 break;
             case 'ts':
                 $result = gmdate('c');
                 break;
             case 'method':
                 $result = $request->getMethod();
                 break;
             case 'url':
                 $result = (string) $request->getUrl();
                 break;
             case 'resource':
                 $result = $request->getResource();
                 break;
             case 'protocol':
                 $result = 'HTTP';
                 break;
             case 'version':
                 $result = $request->getProtocolVersion();
                 break;
             case 'host':
                 $result = $request->getHost();
                 break;
             case 'hostname':
                 $result = gethostname();
                 break;
             case 'port':
                 $result = $request->getPort();
                 break;
             case 'code':
                 $result = $response ? $response->getStatusCode() : '';
                 break;
             case 'phrase':
                 $result = $response ? $response->getReasonPhrase() : '';
                 break;
             case 'connect_time':
                 $result = $handle && $handle->getInfo(CURLINFO_CONNECT_TIME) ? $handle->getInfo(CURLINFO_CONNECT_TIME) : ($response ? $response->getInfo('connect_time') : '');
                 break;
             case 'total_time':
                 $result = $handle && $handle->getInfo(CURLINFO_TOTAL_TIME) ? $handle->getInfo(CURLINFO_TOTAL_TIME) : ($response ? $response->getInfo('total_time') : '');
                 break;
             case 'curl_error':
                 $result = $handle ? $handle->getError() : '';
                 break;
             case 'curl_code':
                 $result = $handle ? $handle->getErrorNo() : '';
                 break;
             case 'curl_stderr':
                 $result = $handle ? $handle->getStderr() : '';
                 break;
             default:
                 if (strpos($matches[1], 'req_header_') === 0) {
                     $result = $request->getHeader(substr($matches[1], 11));
                 } elseif ($response && strpos($matches[1], 'res_header_') === 0) {
                     $result = $response->getHeader(substr($matches[1], 11));
                 }
         }
         $cache[$matches[1]] = $result;
         return $result;
     }, $this->template);
 }
Example #9
0
 /**
  * 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);
 }