public function signRequest(RequestInterface $request, CredentialsInterface $credentials) { if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()) { $request->setHeader('X-Amz-Content-Sha256', EntityBody::getHash($request->getBody(), 'sha256')); } else { $request->setHeader('X-Amz-Content-Sha256', hash('sha256', '')); } parent::signRequest($request, $credentials); }
/** * Read data from the request body and send it to curl * * @param resource $ch Curl handle * @param resource $fd File descriptor * @param int $length Amount of data to read * * @return string */ public function readRequestBody($ch, $fd, $length) { $read = ''; if ($this->request->getBody()) { $read = $this->request->getBody()->read($length); if ($this->emitIo) { $this->request->dispatch('curl.callback.read', array('request' => $this->request, 'read' => $read)); } } return !$read ? '' : $read; }
/** * Always add a x-amz-content-sha-256 for data integrity. */ public function signRequest(RequestInterface $request, CredentialsInterface $credentials) { if ($request instanceof EntityEnclosingRequestInterface && $request->getBody() && !$request->hasHeader('x-amz-content-sha256')) { $request->setHeader('X-Amz-Content-Sha256', $this->getPresignedPayload($request)); } parent::signRequest($request, $credentials); }
/** * @param RequestInterface|EntityEnclosingRequestInterface $request * @param Credentials $credentials */ public function signRequest($request, $credentials) { $request->setHeader('X-HMB-Signature-Method', self::DEFAULT_METHOD); $request->setHeader('X-HMB-Signature-Version', self::DEFAULT_SIGN_VERSION); $request->setHeader('X-HMB-TimeStamp', time()); $contentMd5 = $request instanceof EntityEnclosingRequestInterface ? md5($request->getBody()) : ''; if ($contentMd5) { $request->setHeader('Content-MD5', $contentMd5); } $sign = array(); $sign[] = strtoupper($request->getMethod()); $sign[] = $request->getHost(); if ($request->getHeader('Content-MD5')) { $sign[] = $request->getHeader('Content-MD5'); } if ($request->getHeader('Content-Type')) { $sign[] = $request->getHeader('Content-Type'); } $sign[] = $request->getHeader('X-HMB-Signature-Method'); $sign[] = $request->getHeader('X-HMB-Signature-Version'); $sign[] = $request->getHeader('X-HMB-TimeStamp'); if ($request->getHeader('X-HMB-User-Session-Token')) { $sign[] = $request->getHeader('X-HMB-User-Session-Token'); } $sign[] = $request->getQuery(true) ? $request->getPath() . '?' . $request->getQuery(true) : $request->getPath(); $signature = base64_encode(hash_hmac(strtolower($request->getHeader('X-HMB-Signature-Method')), implode("\n", $sign), $credentials->getSecret())); $request->setHeader('Authorization', sprintf('%s %s:%s', self::AUTHORIZATION_SCHME, $credentials->getKey(), $signature)); }
/** * @param Guzzle\Http\Message\RequestInterface * @return Guzzle\Http\Message\Response */ public function handshake(RequestInterface $request) { $body = $this->sign($request->getHeader('Sec-WebSocket-Key1', true), $request->getHeader('Sec-WebSocket-Key2', true), (string) $request->getBody()); $headers = array('Upgrade' => 'WebSocket', 'Connection' => 'Upgrade', 'Sec-WebSocket-Origin' => $request->getHeader('Origin', true), 'Sec-WebSocket-Location' => 'ws://' . $request->getHeader('Host', true) . $request->getPath()); $response = new Response(101, $headers, $body); $response->setStatus(101, 'WebSocket Protocol Handshake'); return $response; }
/** * {@inheritdoc} */ protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) { if ($response && $response->getStatusCode() == 400 && strpos($response->getBody(), self::ERR)) { // Check if the request is sending a local file, and if so, clear the stat cache and recalculate the size. if ($request instanceof EntityEnclosingRequestInterface) { if ($request->getBody()->getWrapper() == 'plainfile') { $filename = $request->getBody()->getUri(); // Clear the cache so that we send accurate file sizes clearstatcache(true, $filename); $length = filesize($filename); $request->getBody()->setSize($length); $request->setHeader('Content-Length', $length); } } return true; } }
/** * Read data from the request body and send it to curl * * @param resource $ch Curl handle * @param resource $fd File descriptor * @param int $length Amount of data to read * * @return string */ public function readRequestBody($ch, $fd, $length) { if (!($body = $this->request->getBody())) { return ''; } $read = (string) $body->read($length); if ($this->emitIo) { $this->request->dispatch('curl.callback.read', array('request' => $this->request, 'read' => $read)); } return $read; }
/** * @param \Guzzle\Http\Message\RequestInterface $request * @return \Guzzle\Http\Message\Response * @throws \UnderflowException If there hasn't been enough data received */ public function handshake(RequestInterface $request) { $body = substr($request->getBody(), 0, 8); if (8 !== strlen($body)) { throw new \UnderflowException("Not enough data received to issue challenge response"); } $challenge = $this->sign((string) $request->getHeader('Sec-WebSocket-Key1'), (string) $request->getHeader('Sec-WebSocket-Key2'), $body); $headers = array('Upgrade' => 'WebSocket', 'Connection' => 'Upgrade', 'Sec-WebSocket-Origin' => (string) $request->getHeader('Origin'), 'Sec-WebSocket-Location' => 'ws://' . (string) $request->getHeader('Host') . $request->getPath()); $response = new Response(101, $headers, $challenge); $response->setStatus(101, 'WebSocket Protocol Handshake'); return $response; }
public function __construct(ConnectionInterface $conn, RequestInterface $req) { $body = (string) $req->getBody(); if (!empty($body)) { $query = QueryString::fromString($body); $fields = $query->getAll(); foreach ($fields as $field => $value) { $req->setPostField($field, $value); } } $this->conn = $conn; $this->session = $conn->Session; $this->req = $req; }
/** * Sign the Pusher request * * @link http://pusher.com/docs/rest_api#authentication * @param RequestInterface $request * @param Credentials $credentials */ public function signRequest(RequestInterface $request, Credentials $credentials) { $queryParameters = array('auth_key' => $credentials->getKey(), 'auth_timestamp' => time(), 'auth_version' => self::AUTH_VERSION); if ($request instanceof EntityEnclosingRequestInterface) { $body = $request->getBody(); $queryParameters['body_md5'] = $body->getContentLength() ? $body->getContentMd5() : ''; } // The signature algorithm asks that keys are all lowercased $queryParameters = array_change_key_case($request->getQuery()->toArray()) + $queryParameters; $queryParameters = array_filter($queryParameters); ksort($queryParameters); $method = strtoupper($request->getMethod()); $requestPath = $request->getPath(); $query = urldecode(http_build_query($queryParameters)); $signature = $this->signString(implode("\n", array($method, $requestPath, $query)), $credentials); $queryParameters['auth_signature'] = $signature; $request->getQuery()->replace($queryParameters); }
/** * Returns an HTML-formatted string representation of the exception. * * @return string * @internal */ public function __toString() { $msg = $this->getMessage(); if (is_object($this->requestObj) && $this->requestObj instanceof \Guzzle\Http\Message\Request) { $request = array('url' => $this->requestObj->getUrl(), 'host' => $this->requestObj->getHost(), 'headers' => $this->requestObj->getRawHeaders(), 'query' => (string) $this->requestObj->getQuery()); if ($this->requestObj instanceof \Guzzle\Http\Message\EntityEnclosingRequestInterface) { $request_body = $this->requestObj->getBody(); $request['content-type'] = $request_body->getContentType(); $request['content-length'] = $request_body->getContentLength(); $request['body'] = $request_body->__toString(); } $msg .= "\n\nRequest: <pre>" . htmlspecialchars(print_r($request, true)) . '</pre>'; } if (is_object($this->responseObj) && $this->responseObj instanceof \Guzzle\Http\Message\Response) { $response = array('status' => $this->responseObj->getStatusCode(), 'headers' => $this->responseObj->getRawHeaders(), 'body' => $this->responseBody); $msg .= "\n\nResponse: <pre>" . htmlspecialchars(print_r($response, true)) . '</pre>'; } return $msg; }
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; }
/** * Collect & sanitize data about a Guzzle request * * @param Guzzle\Http\Message\RequestInterface $request * * @return array */ private function collectRequest(GuzzleRequestInterface $request) { $body = null; if ($request instanceof EntityEnclosingRequestInterface) { $body = (string) $request->getBody(); } return array('headers' => $request->getHeaders(), 'method' => $request->getMethod(), 'scheme' => $request->getScheme(), 'host' => $request->getHost(), 'port' => $request->getPort(), 'path' => $request->getPath(), 'query' => $request->getQuery(), 'body' => $body); }
/** * 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); // Array of default cURL options. $curlOptions = array(CURLOPT_URL => $request->getUrl(), CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_RETURNTRANSFER => false, CURLOPT_HEADER => false, 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, 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 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) { $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; } } elseif (!$request->hasHeader('Content-Type')) { // Remove the curl generated Content-Type header if none was set manually $curlOptions[CURLOPT_HTTPHEADER][] = 'Content-Type:'; } } // 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->getAll() 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; }
/** * Perform a http request and return the response * * @param \Guzzle\Http\Message\RequestInterface $request the request to preform * @param bool $async whether or not to perform an async request * * @return \Guzzle\Http\Message\Response|mixed the response from elastic search * @throws \Exception */ public function perform(\Guzzle\Http\Message\RequestInterface $request, $async = false) { try { $profileKey = null; if ($this->enableProfiling) { $profileKey = __METHOD__ . '(' . $request->getUrl() . ')'; if ($request instanceof \Guzzle\Http\Message\EntityEnclosingRequest) { $profileKey .= " " . $request->getBody(); } Yii::beginProfile($profileKey); } $response = $async ? $request->send() : json_decode($request->send()->getBody(true), true); Yii::trace("Sent request to '{$request->getUrl()}'", 'application.elastic.connection'); if ($this->enableProfiling) { Yii::endProfile($profileKey); } return $response; } catch (\Guzzle\Http\Exception\BadResponseException $e) { $body = $e->getResponse()->getBody(true); if (($msg = json_decode($body)) !== null && isset($msg->error)) { throw new \CException($msg->error); } else { throw new \CException($e); } } catch (\Guzzle\Http\Exception\ClientErrorResponseException $e) { throw new \CException($e->getResponse()->getBody(true)); } }
/** * @link https://github.com/guzzle/guzzle/issues/710 */ private function validateResponseWasSet(RequestInterface $request) { if ($request->getResponse()) { return true; } $body = $request instanceof EntityEnclosingRequestInterface ? $request->getBody() : null; if (!$body) { $rex = new RequestException('No response was received for a request with no body. This' . ' could mean that you are saturating your network.'); $rex->setRequest($request); $this->removeErroredRequest($request, $rex); } elseif (!$body->isSeekable() || !$body->seek(0)) { // Nothing we can do with this. Sorry! $rex = new RequestException('The connection was unexpectedly closed. The request would' . ' have been retried, but attempting to rewind the' . ' request body failed.'); $rex->setRequest($request); $this->removeErroredRequest($request, $rex); } else { $this->remove($request); // Add the request back to the batch to retry automatically. $this->requests[] = $request; $this->addHandle($request); } return false; }
/** * 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; }
/** * Add body (content) specific options to the context options * * @param RequestInterface $request */ protected function addBodyOptions(RequestInterface $request) { // Add the content for the request if needed if (!$request instanceof EntityEnclosingRequestInterface) { return; } if (count($request->getPostFields())) { $this->setContextValue('http', 'content', (string) $request->getPostFields(), true); } elseif ($request->getBody()) { $this->setContextValue('http', 'content', (string) $request->getBody(), true); } // Always ensure a content-length header is sent if (isset($this->contextOptions['http']['content'])) { $headers = isset($this->contextOptions['http']['header']) ? $this->contextOptions['http']['header'] : array(); $headers[] = 'Content-Length: ' . strlen($this->contextOptions['http']['content']); $this->setContextValue('http', 'header', $headers, true); } }
/** * Get the payload part of a signature from a request. * * @param RequestInterface $request * * @return string */ protected function getPayload(RequestInterface $request) { // Calculate the request signature payload if ($request->hasHeader('x-amz-content-sha256')) { // Handle streaming operations (e.g. Glacier.UploadArchive) return (string) $request->getHeader('x-amz-content-sha256'); } if ($request instanceof EntityEnclosingRequestInterface) { return hash('sha256', $request->getMethod() == 'POST' && count($request->getPostFields()) ? (string) $request->getPostFields() : (string) $request->getBody()); } return self::DEFAULT_PAYLOAD; }
/** * 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); }
protected function onRequest(ConnectionInterface $from, RequestInterface $request) { $requestPath = $request->getPath(); $body = (string) $request->getBody(); if (!empty($body)) { $query = QueryString::fromString($body); $fields = $query->getAll(); $request->addPostFields($fields); } // TODO: use only $req->acceptLanguage() in Managers $_SERVER['HTTP_ACCEPT_LANGUAGE'] = (string) $request->getHeaders()->get('accept-language'); $routes = array('/' => 'executeUiBooter', '/api' => 'executeApiCall', '/api/group' => 'executeApiCallGroup', '/sbin/apicall.php' => 'executeApiCall', '/sbin/apicallgroup.php' => 'executeApiCallGroup', '/sbin/rawdatacall.php' => 'executeRawDataCall', '#^/([a-zA-Z0-9-_.]+)\\.html$#' => 'executeUiBooter', '#^/(bin|boot|etc|home|tmp|usr|var)/(.*)$#' => 'executeReadFile', '/webos.webapp' => 'executeReadManifest', '/hello' => 'executeSayHello'); foreach ($routes as $path => $method) { $matched = false; if (substr($path, 0, 1) == '#') { // Regex if (preg_match($path, $requestPath, $matches)) { $result = $this->{$method}($from, $request, $matches); $matched = true; } } else { if ($path == $requestPath) { $result = $this->{$method}($from, $request); $matched = true; } } if ($matched) { if (empty($result)) { $result = ''; } if ($result instanceof ResponseContent) { $result = $result->generate(); } if ($result instanceof HTTPServerResponse) { if ($result->headersSent()) { // Implicit mode, content already sent if ($result->getHeader('Connection') != 'keep-alive') { $from->close(); } } else { $result->send(); } return; } $response = null; if (is_string($result)) { $response = new Response(200, array(), (string) $result); } else { $response = $result; } $from->send((string) $response); $from->close(); return; } } $resp = new Response(404, array('Content-Type' => 'text/plain'), '404 Not Found'); $from->send((string) $resp); $from->close(); }
public function signRequest(RequestInterface $request, CredentialsInterface $credentials) { $timestamp = $this->getTimestamp(); $longDate = gmdate(DateFormat::ISO8601, $timestamp); $shortDate = substr($longDate, 0, 8); // Remove any previously set Authorization headers so that retries work $request->removeHeader('Authorization'); // Requires a x-amz-date header or Date if ($request->hasHeader('x-amz-date') || !$request->hasHeader('Date')) { $request->setHeader('x-amz-date', $longDate); } else { $request->setHeader('Date', gmdate(DateFormat::RFC1123, $timestamp)); } // Add the security token if one is present if ($credentials->getSecurityToken()) { $request->setHeader('x-amz-security-token', $credentials->getSecurityToken()); } // Parse the service and region or use one that is explicitly set $region = $this->regionName; $service = $this->serviceName; if (!$region || !$service) { $url = Url::factory($request->getUrl()); $region = $region ?: HostNameUtils::parseRegionName($url); $service = $service ?: HostNameUtils::parseServiceName($url); } $credentialScope = "{$shortDate}/{$region}/{$service}/aws4_request"; // Calculate the request signature payload if ($request->hasHeader('x-amz-content-sha256')) { // Handle streaming operations (e.g. Glacier.UploadArchive) $payload = $request->getHeader('x-amz-content-sha256'); } elseif ($request instanceof EntityEnclosingRequestInterface) { $payload = hash('sha256', $request->getMethod() == 'POST' && count($request->getPostFields()) ? (string) $request->getPostFields() : (string) $request->getBody()); } else { // Use the default payload if there is no body $payload = self::DEFAULT_PAYLOAD; } $signingContext = $this->createSigningContext($request, $payload); $signingContext['string_to_sign'] = "AWS4-HMAC-SHA256\n{$longDate}\n{$credentialScope}\n" . hash('sha256', $signingContext['canonical_request']); // Calculate the signing key using a series of derived keys $signingKey = $this->getSigningKey($shortDate, $region, $service, $credentials->getSecretKey()); $signature = hash_hmac('sha256', $signingContext['string_to_sign'], $signingKey); $request->setHeader('Authorization', "AWS4-HMAC-SHA256 " . "Credential={$credentials->getAccessKeyId()}/{$credentialScope}, " . "SignedHeaders={$signingContext['signed_headers']}, Signature={$signature}"); // Add debug information to the request $request->getParams()->set('aws.signature', $signingContext); }
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); $url = (string) $request->getUrl(); if (($pos = strpos($url, '#')) !== false) { $url = substr($url, 0, $pos); } $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')) { $curlOptions[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; } if ($acceptEncodingHeader = $request->getHeader('Accept-Encoding')) { $curlOptions[CURLOPT_ENCODING] = (string) $acceptEncodingHeader; $request->removeHeader('Accept-Encoding'); } if ($requestCurlOptions->get('debug')) { $curlOptions[CURLOPT_STDERR] = fopen('php://temp', 'r+'); if (false === $curlOptions[CURLOPT_STDERR]) { throw new RuntimeException('Unable to create a stream for CURLOPT_STDERR'); } $curlOptions[CURLOPT_VERBOSE] = true; } if ($method == 'GET') { $curlOptions[CURLOPT_HTTPGET] = true; } elseif ($method == 'HEAD') { $curlOptions[CURLOPT_NOBODY] = true; unset($curlOptions[CURLOPT_WRITEFUNCTION]); } elseif (!$request instanceof EntityEnclosingRequest) { $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; } else { $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; if ($request->getBody()) { if ($bodyAsString) { $curlOptions[CURLOPT_POSTFIELDS] = (string) $request->getBody(); if ($tempContentLength = $request->getHeader('Content-Length')) { $tempContentLength = (int) (string) $tempContentLength; } if (!$request->hasHeader('Content-Type')) { $curlOptions[CURLOPT_HTTPHEADER][] = 'Content-Type:'; } } else { $curlOptions[CURLOPT_UPLOAD] = true; if ($tempContentLength = $request->getHeader('Content-Length')) { $tempContentLength = (int) (string) $tempContentLength; $curlOptions[CURLOPT_INFILESIZE] = $tempContentLength; } $curlOptions[CURLOPT_READFUNCTION] = array($mediator, 'readRequestBody'); $request->getBody()->seek(0); } } else { $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) { $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 (!$request->hasHeader('Expect')) { $curlOptions[CURLOPT_HTTPHEADER][] = 'Expect:'; } } if (null !== $tempContentLength) { $request->removeHeader('Content-Length'); } foreach ($requestCurlOptions->toArray() as $key => $value) { if (is_numeric($key)) { $curlOptions[$key] = $value; } } if (!isset($curlOptions[CURLOPT_ENCODING])) { $curlOptions[CURLOPT_HTTPHEADER][] = 'Accept:'; } foreach ($request->getHeaderLines() as $line) { $curlOptions[CURLOPT_HTTPHEADER][] = $line; } if ($tempContentLength) { $request->setHeader('Content-Length', $tempContentLength); } $handle = curl_init(); if ($requestCurlOptions->get('progress')) { $curlOptions[CURLOPT_PROGRESSFUNCTION] = function () use($mediator, $handle) { $args = func_get_args(); $args[] = $handle; 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); }
private function exec(\Guzzle\Http\Message\RequestInterface $request) { $start = microtime(true); $this->responseCode = 0; // Get snapshot of request headers. $request_headers = $request->getRawHeaders(); // Mask authorization for logs. $request_headers = preg_replace('!\\nAuthorization: (Basic|Digest) [^\\r\\n]+\\r!i', "\nAuthorization: \$1 [**masked**]\r", $request_headers); try { $response = $request->send(); } catch (\Guzzle\Http\Exception\BadResponseException $e) { $response = $e->getResponse(); } catch (\Guzzle\Http\Exception\CurlException $e) { // Timeouts etc. DebugData::$raw = ''; DebugData::$code = $e->getErrorNo(); DebugData::$code_status = $e->getError(); DebugData::$code_class = 0; DebugData::$exception = $e->getMessage(); DebugData::$opts = array('request_headers' => $request_headers); DebugData::$data = null; $exception = new ResponseException($e->getError(), $e->getErrorNo(), $request->getUrl(), DebugData::$opts); $exception->requestObj = $request; // Log Exception $headers_array = $request->getHeaders(); unset($headers_array['Authorization']); $headerString = ''; foreach ($headers_array as $value) { $headerString .= $value->getName() . ': ' . $value . " "; } $log_message = '{code_status} ({code}) Request Details:[ {r_method} {r_resource} {r_scheme} {r_headers} ]'; $httpScheme = strtoupper(str_replace('https', 'http', $request->getScheme())) . $request->getProtocolVersion(); $log_params = array('code' => $e->getErrorNo(), 'code_status' => $e->getError(), 'r_method' => $request->getUrl(), 'r_resource' => $request->getRawHeaders(), 'r_scheme' => $httpScheme, 'r_headers' => $headerString); self::$logger->emergency($log_message, $log_params); throw $exception; } $this->responseCode = $response->getStatusCode(); $this->responseText = trim($response->getBody(true)); $this->responseLength = $response->getContentLength(); $this->responseMimeType = $response->getContentType(); $this->responseObj = array(); $content_type = $response->getContentType(); $firstChar = substr($this->responseText, 0, 1); if (strpos($content_type, '/json') !== false && ($firstChar == '{' || $firstChar == '[')) { $response_obj = @json_decode($this->responseText, true); if (is_array($response_obj)) { $this->responseObj = $response_obj; } } $status = self::getStatusMessage($this->responseCode); $code_class = floor($this->responseCode / 100); DebugData::$raw = $this->responseText; DebugData::$opts = array('request_headers' => $request_headers, 'response_headers' => $response->getRawHeaders()); if ($request instanceof \Guzzle\Http\Message\EntityEnclosingRequestInterface) { DebugData::$opts['request_body'] = (string) $request->getBody(); } DebugData::$opts['request_type'] = class_implements($request); DebugData::$data = $this->responseObj; DebugData::$code = $this->responseCode; DebugData::$code_status = $status; DebugData::$code_class = $code_class; DebugData::$exception = null; DebugData::$time_elapsed = microtime(true) - $start; if ($code_class != 2) { $uri = $request->getUrl(); if (!empty($this->responseCode) && isset($this->responseObj['message'])) { $message = 'Code: ' . $this->responseCode . '; Message: ' . $this->responseObj['message']; } else { $message = 'API returned HTTP code of ' . $this->responseCode . ' when fetching from ' . $uri; } DebugData::$exception = $message; $this->debugCallback(DebugData::toArray()); self::$logger->error($this->responseText); // Create better status to show up in logs $status .= ': ' . $request->getMethod() . ' ' . $uri; if ($request instanceof \Guzzle\Http\Message\EntityEnclosingRequestInterface) { $body = $request->getBody(); if ($body instanceof \Guzzle\Http\EntityBodyInterface) { $status .= ' with Content-Length of ' . $body->getContentLength() . ' and Content-Type of ' . $body->getContentType(); } } $exception = new ResponseException($status, $this->responseCode, $uri, DebugData::$opts, $this->responseText); $exception->requestObj = $request; $exception->responseObj = $response; throw $exception; } $this->debugCallback(DebugData::toArray()); }
/** * 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': if ($handle) { $result = $handle->getInfo(CURLINFO_CONNECT_TIME); } elseif ($response) { $result = $response->getInfo('connect_time'); } break; case 'total_time': if ($handle) { $result = $handle->getInfo(CURLINFO_TOTAL_TIME); } elseif ($response) { $result = $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 (strpos($matches[1], 'res_header_') === 0) { $result = $response->getHeader(substr($matches[1], 11)); } } $cache[$matches[1]] = $result; return $result; }, $this->template); }
/** * 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); }
protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) { if ($response && $request->getMethod() === 'POST' && $request instanceof EntityEnclosingRequestInterface && $response->getStatusCode() == 200 && strpos($request->getBody(), '<CompleteMultipartUpload xmlns') !== false && strpos($response->getBody(), '<CompleteMultipartUploadResult xmlns') === false) { return true; } }
/** * 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; }
/** * 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); }
/** * 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; }