public function cloneRequestWithMethod(RequestInterface $request, $method) { if ($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()->toArray()); $cloned->setEventDispatcher(clone $request->getEventDispatcher()); if (!$cloned instanceof EntityEnclosingRequestInterface) { $cloned->removeHeader('Content-Length'); } elseif ($request instanceof EntityEnclosingRequestInterface) { $cloned->setBody($request->getBody()); } $cloned->getParams()->replace($request->getParams()->toArray()); $cloned->dispatch('request.clone', array('request' => $cloned)); return $cloned; }
/** * Create a pre-signed URL for a request * * @param RequestInterface $request Request to generate the URL for. Use the factory methods of the client to * create this request object * @param int|string|\DateTime $expires The time at which the URL should expire. This can be a Unix timestamp, a * PHP DateTime object, or a string that can be evaluated by strtotime * * @return string * @throws InvalidArgumentException if the request is not associated with this client object */ public function createPresignedUrl(RequestInterface $request, $expires) { if ($request->getClient() !== $this) { throw new InvalidArgumentException('The request object must be associated with the client. Use the ' . '$client->get(), $client->head(), $client->post(), $client->put(), etc. methods when passing in a ' . 'request object'); } return $this->signature->createPresignedUrl($request, $this->credentials, $expires); }
/** * Parse the bucket name from a request object * * @param RequestInterface $request Request to parse * * @return string */ private function parseBucketName(RequestInterface $request) { $baseUrl = Url::factory($request->getClient()->getBaseUrl()); $baseHost = $baseUrl->getHost(); $host = $request->getHost(); if (strpos($host, $baseHost) === false) { // Does not contain the base URL, so it's either a redirect, CNAME, or using a different region $baseHost = ''; // For every known S3 host, check if that host is present on the request $regions = $request->getClient()->getDescription()->getData('regions'); foreach ($regions as $region) { if (strpos($host, $region['hostname']) !== false) { // This host matches the request host. Tells use the region and endpoint-- we can derive the bucket $baseHost = $region['hostname']; break; } } // If no matching base URL was found, then assume that this is a CNAME, and the CNAME is the bucket if (!$baseHost) { return $host; } } // Remove the baseURL from the host of the request to attempt to determine the bucket name return trim(str_replace($baseHost, '', $request->getHost()), ' .'); }
/** * Create a pre-signed URL for a request * * @param RequestInterface $request Request to generate the URL for. Use the factory methods of the client to * create this request object * @param int|string|\DateTime $expires The time at which the URL should expire. This can be a Unix timestamp, a * PHP DateTime object, or a string that can be evaluated by strtotime * * @return string * @throws InvalidArgumentException if the request is not associated with this client object */ public function createPresignedUrl(RequestInterface $request, $expires) { if ($request->getClient() !== $this) { throw new InvalidArgumentException('The request object must be associated with the client. Use the ' . '$client->get(), $client->head(), $client->post(), $client->put(), etc. methods when passing in a ' . 'request object'); } if ($expires instanceof \DateTime) { $expires = $expires->getTimestamp(); } elseif (!is_numeric($expires)) { $expires = strtotime($expires); } // Operate on a clone of the request, so the original is not altered $request = clone $request; // URL encoding already occurs in the URI template expansion. Undo that and encode using the same encoding as // GET object, PUT object, etc. $path = $this->encodeKey(rawurldecode($request->getPath())); $request->setPath($path); // Make sure to handle temporary credentials if ($token = $this->credentials->getSecurityToken()) { $request->setHeader('x-amz-security-token', $token); $request->getQuery()->set('x-amz-security-token', $token); } // Set query params required for pre-signed URLs $request->getQuery()->set('AWSAccessKeyId', $this->credentials->getAccessKeyId())->set('Expires', $expires)->set('Signature', $this->signature->signString($this->signature->createCanonicalizedString($request, $expires), $this->credentials)); return $request->getUrl(); }
/** * 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()->toArray()); $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()->toArray()); $cloned->dispatch('request.clone', array('request' => $cloned)); return $cloned; }
/** * Create a pre-signed URL for a request * * @param RequestInterface $request Request to generate the URL for. Use the factory methods of the client to create * this request object. * @param int|string $expires The Unix timestamp to expire at or a string that can be evaluated by strtotime * * @return string * @throws InvalidArgumentException if the request is not associated with this client object */ public function createPresignedUrl(RequestInterface $request, $expires) { if ($request->getClient() !== $this) { throw new InvalidArgumentException('The request object must be associated with the client. Use the ' . '$client->get(), $client->head(), $client->post(), $client->put(), etc. methods when passing in a ' . 'request object'); } if (!is_numeric($expires)) { $expires = strtotime($expires); } $copy = clone $request; // URL encoding already occurs in the URI template expansion. Undo that and encode using the same encoding as // GET object, PUT object, etc. $path = $this->encodeKey(rawurldecode($copy->getPath())); $copy->setPath($path); $copy->getQuery()->set('AWSAccessKeyId', $this->credentials->getAccessKeyId())->set('Expires', $expires)->set('Signature', $this->signature->signString($this->signature->createCanonicalizedString($request, $expires), $this->credentials)); return $copy->getUrl(); }
/** * 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; }
/** * 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); }
/** * 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); }