コード例 #1
0
 /**
  * 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;
 }
コード例 #2
0
 /**
  * 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;
 }
コード例 #3
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);
 }
コード例 #4
0
 /**
  * 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);
     }
 }
コード例 #5
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);
 }
コード例 #6
0
 /**
  * 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 ($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;
 }
コード例 #7
0
 /**
  * @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;
 }