mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-09-03 22:47:03 +03:00
Migrate RainLoop Webmail to use Facebook Graph API instead of FQL
This commit is contained in:
parent
9fcaf00875
commit
d0dbb26548
120 changed files with 18817 additions and 1746 deletions
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Adapter;
|
||||
|
||||
use GuzzleHttp\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Adapter interface used to transfer HTTP requests.
|
||||
*
|
||||
* @link http://docs.guzzlephp.org/en/guzzle4/adapters.html for a full
|
||||
* explanation of adapters and their responsibilities.
|
||||
*/
|
||||
interface AdapterInterface
|
||||
{
|
||||
/**
|
||||
* Transfers an HTTP request and populates a response
|
||||
*
|
||||
* @param TransactionInterface $transaction Transaction abject to populate
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function send(TransactionInterface $transaction);
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Adapter\Curl;
|
||||
|
||||
use GuzzleHttp\Adapter\TransactionInterface;
|
||||
use GuzzleHttp\Exception\AdapterException;
|
||||
|
||||
/**
|
||||
* Provides context for a Curl transaction, including active handles,
|
||||
* pending transactions, and whether or not this is a batch or single
|
||||
* transaction.
|
||||
*/
|
||||
class BatchContext
|
||||
{
|
||||
/** @var resource Curl multi resource */
|
||||
private $multi;
|
||||
|
||||
/** @var \SplObjectStorage Map of transactions to curl resources */
|
||||
private $handles;
|
||||
|
||||
/** @var \Iterator Yields pending transactions */
|
||||
private $pending;
|
||||
|
||||
/** @var bool Whether or not to throw transactions */
|
||||
private $throwsExceptions;
|
||||
|
||||
/**
|
||||
* @param resource $multiHandle Initialized curl_multi resource
|
||||
* @param bool $throwsExceptions Whether or not exceptions are thrown
|
||||
* @param \Iterator $pending Iterator yielding pending transactions
|
||||
*/
|
||||
public function __construct(
|
||||
$multiHandle,
|
||||
$throwsExceptions,
|
||||
\Iterator $pending = null
|
||||
) {
|
||||
$this->multi = $multiHandle;
|
||||
$this->handles = new \SplObjectStorage();
|
||||
$this->throwsExceptions = $throwsExceptions;
|
||||
$this->pending = $pending;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a transaction for a given curl handle
|
||||
*
|
||||
* @param resource $handle Curl handle
|
||||
*
|
||||
* @return TransactionInterface
|
||||
* @throws AdapterException if a transaction is not found
|
||||
*/
|
||||
public function findTransaction($handle)
|
||||
{
|
||||
foreach ($this->handles as $transaction) {
|
||||
if ($this->handles[$transaction] === $handle) {
|
||||
return $transaction;
|
||||
}
|
||||
}
|
||||
|
||||
throw new AdapterException('No curl handle was found');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are any remaining pending transactions
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPending()
|
||||
{
|
||||
return $this->pending && $this->pending->valid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop the next transaction from the transaction queue
|
||||
*
|
||||
* @return TransactionInterface|null
|
||||
*/
|
||||
public function nextPending()
|
||||
{
|
||||
if (!$this->hasPending()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$current = $this->pending->current();
|
||||
$this->pending->next();
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the batch is to throw exceptions on error
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function throwsExceptions()
|
||||
{
|
||||
return $this->throwsExceptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the curl_multi handle
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
public function getMultiHandle()
|
||||
{
|
||||
return $this->multi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a transaction to the multi handle
|
||||
*
|
||||
* @param TransactionInterface $transaction Transaction to add
|
||||
* @param resource $handle Resource to use with the handle
|
||||
*
|
||||
* @throws AdapterException If the handle is already registered
|
||||
*/
|
||||
public function addTransaction(TransactionInterface $transaction, $handle)
|
||||
{
|
||||
if (isset($this->handles[$transaction])) {
|
||||
throw new AdapterException('Transaction already registered');
|
||||
}
|
||||
|
||||
$code = curl_multi_add_handle($this->multi, $handle);
|
||||
if ($code != CURLM_OK) {
|
||||
MultiAdapter::throwMultiError($code);
|
||||
}
|
||||
|
||||
$this->handles[$transaction] = $handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a transaction and associated handle from the context
|
||||
*
|
||||
* @param TransactionInterface $transaction Transaction to remove
|
||||
*
|
||||
* @return array Returns the curl_getinfo array
|
||||
* @throws AdapterException if the transaction is not found
|
||||
*/
|
||||
public function removeTransaction(TransactionInterface $transaction)
|
||||
{
|
||||
if (!isset($this->handles[$transaction])) {
|
||||
throw new AdapterException('Transaction not registered');
|
||||
}
|
||||
|
||||
$handle = $this->handles[$transaction];
|
||||
|
||||
$code = curl_multi_remove_handle($this->multi, $handle);
|
||||
if ($code != CURLM_OK) {
|
||||
MultiAdapter::throwMultiError($code);
|
||||
}
|
||||
|
||||
$info = curl_getinfo($handle);
|
||||
curl_close($handle);
|
||||
unset($this->handles[$transaction]);
|
||||
|
||||
return $info;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Adapter\Curl;
|
||||
|
||||
use GuzzleHttp\Adapter\AdapterInterface;
|
||||
use GuzzleHttp\Adapter\TransactionInterface;
|
||||
use GuzzleHttp\Event\RequestEvents;
|
||||
use GuzzleHttp\Exception\AdapterException;
|
||||
use GuzzleHttp\Message\MessageFactoryInterface;
|
||||
|
||||
/**
|
||||
* HTTP adapter that uses cURL easy handles as a transport layer.
|
||||
*
|
||||
* Requires PHP 5.5+
|
||||
*
|
||||
* When using the CurlAdapter, custom curl options can be specified as an
|
||||
* associative array of curl option constants mapping to values in the
|
||||
* **curl** key of a request's configuration options.
|
||||
*/
|
||||
class CurlAdapter implements AdapterInterface
|
||||
{
|
||||
/** @var CurlFactory */
|
||||
private $curlFactory;
|
||||
|
||||
/** @var MessageFactoryInterface */
|
||||
private $messageFactory;
|
||||
|
||||
/** @var array Array of curl easy handles */
|
||||
private $handles = [];
|
||||
|
||||
/** @var array Array of owned curl easy handles */
|
||||
private $ownedHandles = [];
|
||||
|
||||
/** @var int Total number of idle handles to keep in cache */
|
||||
private $maxHandles;
|
||||
|
||||
/**
|
||||
* Accepts an associative array of options:
|
||||
*
|
||||
* - handle_factory: Optional callable factory used to create cURL handles.
|
||||
* The callable is invoked with the following arguments:
|
||||
* TransactionInterface, MessageFactoryInterface, and an optional cURL
|
||||
* handle to modify. The factory method must then return a cURL resource.
|
||||
* - max_handles: Maximum number of idle handles (defaults to 5).
|
||||
*
|
||||
* @param MessageFactoryInterface $messageFactory
|
||||
* @param array $options Array of options to use with the adapter
|
||||
*/
|
||||
public function __construct(
|
||||
MessageFactoryInterface $messageFactory,
|
||||
array $options = []
|
||||
) {
|
||||
$this->handles = $this->ownedHandles = [];
|
||||
$this->messageFactory = $messageFactory;
|
||||
$this->curlFactory = isset($options['handle_factory'])
|
||||
? $options['handle_factory']
|
||||
: new CurlFactory();
|
||||
$this->maxHandles = isset($options['max_handles'])
|
||||
? $options['max_handles']
|
||||
: 5;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
foreach ($this->handles as $handle) {
|
||||
if (is_resource($handle)) {
|
||||
curl_close($handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function send(TransactionInterface $transaction)
|
||||
{
|
||||
RequestEvents::emitBefore($transaction);
|
||||
if ($response = $transaction->getResponse()) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$factory = $this->curlFactory;
|
||||
$handle = $factory(
|
||||
$transaction,
|
||||
$this->messageFactory,
|
||||
$this->checkoutEasyHandle()
|
||||
);
|
||||
|
||||
curl_exec($handle);
|
||||
$info = curl_getinfo($handle);
|
||||
$info['curl_result'] = curl_errno($handle);
|
||||
|
||||
if ($info['curl_result']) {
|
||||
$this->handleError($transaction, $info, $handle);
|
||||
} else {
|
||||
$this->releaseEasyHandle($handle);
|
||||
RequestEvents::emitComplete($transaction, $info);
|
||||
}
|
||||
|
||||
return $transaction->getResponse();
|
||||
}
|
||||
|
||||
private function handleError(
|
||||
TransactionInterface $transaction,
|
||||
$info,
|
||||
$handle
|
||||
) {
|
||||
$error = curl_error($handle);
|
||||
$this->releaseEasyHandle($handle);
|
||||
RequestEvents::emitError(
|
||||
$transaction,
|
||||
new AdapterException("cURL error {$info['curl_result']}: {$error}"),
|
||||
$info
|
||||
);
|
||||
}
|
||||
|
||||
private function checkoutEasyHandle()
|
||||
{
|
||||
// Find an unused handle in the cache
|
||||
if (false !== ($key = array_search(false, $this->ownedHandles, true))) {
|
||||
$this->ownedHandles[$key] = true;
|
||||
return $this->handles[$key];
|
||||
}
|
||||
|
||||
// Add a new handle
|
||||
$handle = curl_init();
|
||||
$id = (int) $handle;
|
||||
$this->handles[$id] = $handle;
|
||||
$this->ownedHandles[$id] = true;
|
||||
|
||||
return $handle;
|
||||
}
|
||||
|
||||
private function releaseEasyHandle($handle)
|
||||
{
|
||||
$id = (int) $handle;
|
||||
if (count($this->ownedHandles) > $this->maxHandles) {
|
||||
curl_close($this->handles[$id]);
|
||||
unset($this->handles[$id], $this->ownedHandles[$id]);
|
||||
} else {
|
||||
curl_reset($handle);
|
||||
$this->ownedHandles[$id] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,331 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Adapter\Curl;
|
||||
|
||||
use GuzzleHttp\Adapter\TransactionInterface;
|
||||
use GuzzleHttp\Message\MessageFactoryInterface;
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
use GuzzleHttp\Stream;
|
||||
use GuzzleHttp\Exception\AdapterException;
|
||||
|
||||
/**
|
||||
* Creates curl resources from a request and response object
|
||||
*/
|
||||
class CurlFactory
|
||||
{
|
||||
/**
|
||||
* Creates a cURL handle based on a transaction.
|
||||
*
|
||||
* @param TransactionInterface $transaction Holds a request and response
|
||||
* @param MessageFactoryInterface $messageFactory Used to create responses
|
||||
* @param null|resource $handle Optionally provide a curl handle to modify
|
||||
*
|
||||
* @return resource Returns a prepared cURL handle
|
||||
* @throws AdapterException when an option cannot be applied
|
||||
*/
|
||||
public function __invoke(
|
||||
TransactionInterface $transaction,
|
||||
MessageFactoryInterface $messageFactory,
|
||||
$handle = null
|
||||
) {
|
||||
$request = $transaction->getRequest();
|
||||
$mediator = new RequestMediator($transaction, $messageFactory);
|
||||
$options = $this->getDefaultOptions($request, $mediator);
|
||||
$this->applyMethod($request, $options);
|
||||
$this->applyTransferOptions($request, $mediator, $options);
|
||||
$this->applyHeaders($request, $options);
|
||||
unset($options['_headers']);
|
||||
|
||||
// Add adapter options from the request's configuration options
|
||||
if ($config = $request->getConfig()['curl']) {
|
||||
$options = $this->applyCustomCurlOptions($config, $options);
|
||||
}
|
||||
|
||||
if (!$handle) {
|
||||
$handle = curl_init();
|
||||
}
|
||||
|
||||
curl_setopt_array($handle, $options);
|
||||
|
||||
return $handle;
|
||||
}
|
||||
|
||||
protected function getDefaultOptions(
|
||||
RequestInterface $request,
|
||||
RequestMediator $mediator
|
||||
) {
|
||||
$url = $request->getUrl();
|
||||
|
||||
// Strip fragment from URL. See:
|
||||
// https://github.com/guzzle/guzzle/issues/453
|
||||
if (($pos = strpos($url, '#')) !== false) {
|
||||
$url = substr($url, 0, $pos);
|
||||
}
|
||||
|
||||
$config = $request->getConfig();
|
||||
$options = array(
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_CONNECTTIMEOUT => $config['connect_timeout'] ?: 150,
|
||||
CURLOPT_RETURNTRANSFER => false,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_WRITEFUNCTION => array($mediator, 'writeResponseBody'),
|
||||
CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader'),
|
||||
CURLOPT_READFUNCTION => array($mediator, 'readRequestBody'),
|
||||
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,
|
||||
'_headers' => $request->getHeaders()
|
||||
);
|
||||
|
||||
if (defined('CURLOPT_PROTOCOLS')) {
|
||||
// Allow only HTTP and HTTPS protocols
|
||||
$options[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
|
||||
}
|
||||
|
||||
// Add CURLOPT_ENCODING if Accept-Encoding header is provided
|
||||
if ($request->hasHeader('Accept-Encoding')) {
|
||||
$options[CURLOPT_ENCODING] = $request->getHeader('Accept-Encoding');
|
||||
// Let cURL set the Accept-Encoding header. Without this change
|
||||
// curl could add a duplicate value.
|
||||
$this->removeHeader('Accept-Encoding', $options);
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
private function applyMethod(RequestInterface $request, array &$options)
|
||||
{
|
||||
$method = $request->getMethod();
|
||||
if ($method == 'HEAD') {
|
||||
$options[CURLOPT_NOBODY] = true;
|
||||
unset($options[CURLOPT_WRITEFUNCTION], $options[CURLOPT_READFUNCTION]);
|
||||
} else {
|
||||
$options[CURLOPT_CUSTOMREQUEST] = $method;
|
||||
if (!$request->getBody()) {
|
||||
unset($options[CURLOPT_READFUNCTION]);
|
||||
} else {
|
||||
$this->applyBody($request, $options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function applyBody(RequestInterface $request, array &$options)
|
||||
{
|
||||
if ($request->hasHeader('Content-Length')) {
|
||||
$size = (int) $request->getHeader('Content-Length');
|
||||
} else {
|
||||
$size = null;
|
||||
}
|
||||
|
||||
$request->getBody()->seek(0);
|
||||
|
||||
// You can send the body as a string using curl's CURLOPT_POSTFIELDS
|
||||
if (($size !== null && $size < 32768) ||
|
||||
isset($request->getConfig()['curl']['body_as_string'])
|
||||
) {
|
||||
$options[CURLOPT_POSTFIELDS] = $request->getBody()->getContents();
|
||||
// Don't duplicate the Content-Length header
|
||||
$this->removeHeader('Content-Length', $options);
|
||||
$this->removeHeader('Transfer-Encoding', $options);
|
||||
} else {
|
||||
$options[CURLOPT_UPLOAD] = true;
|
||||
// Let cURL handle setting the Content-Length header
|
||||
if ($size !== null) {
|
||||
$options[CURLOPT_INFILESIZE] = $size;
|
||||
$this->removeHeader('Content-Length', $options);
|
||||
}
|
||||
}
|
||||
|
||||
// If the Expect header is not present, prevent curl from adding it
|
||||
if (!$request->hasHeader('Expect')) {
|
||||
$options[CURLOPT_HTTPHEADER][] = 'Expect:';
|
||||
}
|
||||
}
|
||||
|
||||
private function applyHeaders(RequestInterface $request, array &$options)
|
||||
{
|
||||
foreach ($options['_headers'] as $name => $values) {
|
||||
$options[CURLOPT_HTTPHEADER][] = $name . ': ' . implode(', ', $values);
|
||||
}
|
||||
|
||||
// Remove the Expect header if one was not set
|
||||
if (!$request->hasHeader('Accept')) {
|
||||
$options[CURLOPT_HTTPHEADER][] = 'Accept:';
|
||||
}
|
||||
}
|
||||
|
||||
private function applyTransferOptions(
|
||||
RequestInterface $request,
|
||||
RequestMediator $mediator,
|
||||
array &$options
|
||||
) {
|
||||
static $methods;
|
||||
if (!$methods) {
|
||||
$methods = array_flip(get_class_methods(__CLASS__));
|
||||
}
|
||||
|
||||
foreach ($request->getConfig()->toArray() as $key => $value) {
|
||||
$method = "add_{$key}";
|
||||
if (isset($methods[$method])) {
|
||||
$this->{$method}($request, $mediator, $options, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function add_debug(
|
||||
RequestInterface $request,
|
||||
RequestMediator $mediator,
|
||||
&$options,
|
||||
$value
|
||||
) {
|
||||
if ($value) {
|
||||
$options[CURLOPT_STDERR] = is_resource($value) ? $value : STDOUT;
|
||||
$options[CURLOPT_VERBOSE] = true;
|
||||
}
|
||||
}
|
||||
|
||||
private function add_proxy(
|
||||
RequestInterface $request,
|
||||
RequestMediator $mediator,
|
||||
&$options,
|
||||
$value
|
||||
) {
|
||||
if (!is_array($value)) {
|
||||
$options[CURLOPT_PROXY] = $value;
|
||||
} else {
|
||||
$scheme = $request->getScheme();
|
||||
if (isset($value[$scheme])) {
|
||||
$options[CURLOPT_PROXY] = $value[$scheme];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function add_timeout(
|
||||
RequestInterface $request,
|
||||
RequestMediator $mediator,
|
||||
&$options,
|
||||
$value
|
||||
) {
|
||||
$options[CURLOPT_TIMEOUT_MS] = $value * 1000;
|
||||
}
|
||||
|
||||
private function add_connect_timeout(
|
||||
RequestInterface $request,
|
||||
RequestMediator $mediator,
|
||||
&$options,
|
||||
$value
|
||||
) {
|
||||
$options[CURLOPT_CONNECTTIMEOUT_MS] = $value * 1000;
|
||||
}
|
||||
|
||||
private function add_verify(
|
||||
RequestInterface $request,
|
||||
RequestMediator $mediator,
|
||||
&$options,
|
||||
$value
|
||||
) {
|
||||
if ($value === false) {
|
||||
unset($options[CURLOPT_CAINFO]);
|
||||
$options[CURLOPT_SSL_VERIFYHOST] = 0;
|
||||
$options[CURLOPT_SSL_VERIFYPEER] = false;
|
||||
} elseif ($value === true || is_string($value)) {
|
||||
$options[CURLOPT_SSL_VERIFYHOST] = 2;
|
||||
$options[CURLOPT_SSL_VERIFYPEER] = true;
|
||||
if ($value !== true) {
|
||||
if (!file_exists($value)) {
|
||||
throw new AdapterException('SSL certificate authority file'
|
||||
. " not found: {$value}");
|
||||
}
|
||||
$options[CURLOPT_CAINFO] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function add_cert(
|
||||
RequestInterface $request,
|
||||
RequestMediator $mediator,
|
||||
&$options,
|
||||
$value
|
||||
) {
|
||||
if (!file_exists($value)) {
|
||||
throw new AdapterException("SSL certificate not found: {$value}");
|
||||
}
|
||||
|
||||
$options[CURLOPT_SSLCERT] = $value;
|
||||
}
|
||||
|
||||
private function add_ssl_key(
|
||||
RequestInterface $request,
|
||||
RequestMediator $mediator,
|
||||
&$options,
|
||||
$value
|
||||
) {
|
||||
if (is_array($value)) {
|
||||
$options[CURLOPT_SSLKEYPASSWD] = $value[1];
|
||||
$value = $value[0];
|
||||
}
|
||||
|
||||
if (!file_exists($value)) {
|
||||
throw new AdapterException("SSL private key not found: {$value}");
|
||||
}
|
||||
|
||||
$options[CURLOPT_SSLKEY] = $value;
|
||||
}
|
||||
|
||||
private function add_save_to(
|
||||
RequestInterface $request,
|
||||
RequestMediator $mediator,
|
||||
&$options,
|
||||
$value
|
||||
) {
|
||||
$mediator->setResponseBody(is_string($value)
|
||||
? Stream\create(fopen($value, 'w'))
|
||||
: Stream\create($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an array of curl options specified in the 'curl' option of a
|
||||
* request's configuration array and maps them to CURLOPT_* options.
|
||||
*
|
||||
* This method is only called when a request has a 'curl' config setting.
|
||||
* Array key strings that start with CURL that have a matching constant
|
||||
* value will be automatically converted to the matching constant.
|
||||
*
|
||||
* @param array $config Configuration array of custom curl option
|
||||
* @param array $options Array of existing curl options
|
||||
*
|
||||
* @return array Returns a new array of curl options
|
||||
*/
|
||||
private function applyCustomCurlOptions(array $config, array $options)
|
||||
{
|
||||
unset($config['body_as_string']);
|
||||
$curlOptions = [];
|
||||
|
||||
// Map curl constant strings to defined values
|
||||
foreach ($config as $key => $value) {
|
||||
if (defined($key) && substr($key, 0, 4) === 'CURL') {
|
||||
$key = constant($key);
|
||||
}
|
||||
$curlOptions[$key] = $value;
|
||||
}
|
||||
|
||||
return $curlOptions + $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a header from the options array
|
||||
*
|
||||
* @param string $name Case-insensitive header to remove
|
||||
* @param array $options Array of options to modify
|
||||
*/
|
||||
private function removeHeader($name, array &$options)
|
||||
{
|
||||
foreach (array_keys($options['_headers']) as $key) {
|
||||
if (!strcasecmp($key, $name)) {
|
||||
unset($options['_headers'][$key]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Adapter\Curl;
|
||||
|
||||
use GuzzleHttp\Adapter\AdapterInterface;
|
||||
use GuzzleHttp\Adapter\ParallelAdapterInterface;
|
||||
use GuzzleHttp\Adapter\TransactionInterface;
|
||||
use GuzzleHttp\Event\RequestEvents;
|
||||
use GuzzleHttp\Exception\AdapterException;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use GuzzleHttp\Message\MessageFactoryInterface;
|
||||
|
||||
/**
|
||||
* HTTP adapter that uses cURL multi as a transport layer
|
||||
*
|
||||
* When using the CurlAdapter, custom curl options can be specified as an
|
||||
* associative array of curl option constants mapping to values in the
|
||||
* **curl** key of a request's configuration options.
|
||||
*
|
||||
* In addition to being able to supply configuration options via the curl
|
||||
* request config, you can also specify the select_timeout variable using the
|
||||
* `GUZZLE_CURL_SELECT_TIMEOUT` environment variable.
|
||||
*/
|
||||
class MultiAdapter implements AdapterInterface, ParallelAdapterInterface
|
||||
{
|
||||
const ERROR_STR = 'See http://curl.haxx.se/libcurl/c/libcurl-errors.html for an explanation of cURL errors';
|
||||
const ENV_SELECT_TIMEOUT = 'GUZZLE_CURL_SELECT_TIMEOUT';
|
||||
|
||||
/** @var CurlFactory */
|
||||
private $curlFactory;
|
||||
|
||||
/** @var MessageFactoryInterface */
|
||||
private $messageFactory;
|
||||
|
||||
/** @var array Array of curl multi handles */
|
||||
private $multiHandles = [];
|
||||
|
||||
/** @var array Array of curl multi handles */
|
||||
private $multiOwned = [];
|
||||
|
||||
/** @var double */
|
||||
private $selectTimeout;
|
||||
|
||||
/**
|
||||
* Accepts an associative array of options:
|
||||
*
|
||||
* - handle_factory: Optional callable factory used to create cURL handles.
|
||||
* The callable is invoked with the following arguments:
|
||||
* TransactionInterface, MessageFactoryInterface, and an optional cURL
|
||||
* handle to modify. The factory method must then return a cURL resource.
|
||||
* - select_timeout: Specify a float in seconds to use for a
|
||||
* curl_multi_select timeout.
|
||||
*
|
||||
* @param MessageFactoryInterface $messageFactory
|
||||
* @param array $options Array of options to use with the adapter:
|
||||
*/
|
||||
public function __construct(
|
||||
MessageFactoryInterface $messageFactory,
|
||||
array $options = []
|
||||
) {
|
||||
$this->messageFactory = $messageFactory;
|
||||
$this->curlFactory = isset($options['handle_factory'])
|
||||
? $options['handle_factory']
|
||||
: new CurlFactory();
|
||||
|
||||
if (isset($options['select_timeout'])) {
|
||||
$this->selectTimeout = $options['select_timeout'];
|
||||
} elseif (isset($_SERVER[self::ENV_SELECT_TIMEOUT])) {
|
||||
$this->selectTimeout = $_SERVER[self::ENV_SELECT_TIMEOUT];
|
||||
} else {
|
||||
$this->selectTimeout = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
foreach ($this->multiHandles as $handle) {
|
||||
if (is_resource($handle)) {
|
||||
curl_multi_close($handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an exception for a cURL multi response
|
||||
*
|
||||
* @param int $code Curl response code
|
||||
* @throws AdapterException
|
||||
*/
|
||||
public static function throwMultiError($code)
|
||||
{
|
||||
$buffer = function_exists('curl_multi_strerror')
|
||||
? curl_multi_strerror($code)
|
||||
: self::ERROR_STR;
|
||||
|
||||
throw new AdapterException(sprintf('cURL error %s: %s', $code, $buffer));
|
||||
}
|
||||
|
||||
public function send(TransactionInterface $transaction)
|
||||
{
|
||||
$context = new BatchContext($this->checkoutMultiHandle(), true);
|
||||
$this->addHandle($transaction, $context);
|
||||
$this->perform($context);
|
||||
|
||||
return $transaction->getResponse();
|
||||
}
|
||||
|
||||
public function sendAll(\Iterator $transactions, $parallel)
|
||||
{
|
||||
$context = new BatchContext(
|
||||
$this->checkoutMultiHandle(),
|
||||
false,
|
||||
$transactions
|
||||
);
|
||||
|
||||
foreach (new \LimitIterator($transactions, 0, $parallel) as $trans) {
|
||||
$this->addHandle($trans, $context);
|
||||
}
|
||||
|
||||
$this->perform($context);
|
||||
}
|
||||
|
||||
private function perform(BatchContext $context)
|
||||
{
|
||||
// The first curl_multi_select often times out no matter what, but is
|
||||
// usually required for fast transfers.
|
||||
$active = false;
|
||||
$multi = $context->getMultiHandle();
|
||||
|
||||
do {
|
||||
while (($mrc = curl_multi_exec($multi, $active)) == CURLM_CALL_MULTI_PERFORM);
|
||||
if ($mrc != CURLM_OK && $mrc != CURLM_CALL_MULTI_PERFORM) {
|
||||
self::throwMultiError($mrc);
|
||||
}
|
||||
// Need to check if there are pending transactions before processing
|
||||
// them so that we don't bail from the loop too early.
|
||||
$pending = $context->hasPending();
|
||||
$this->processMessages($context);
|
||||
if ($active && curl_multi_select($multi, $this->selectTimeout) === -1) {
|
||||
// Perform a usleep if a select returns -1.
|
||||
// See: https://bugs.php.net/bug.php?id=61141
|
||||
usleep(250);
|
||||
}
|
||||
} while ($active || $pending);
|
||||
|
||||
$this->releaseMultiHandle($multi);
|
||||
}
|
||||
|
||||
private function processMessages(BatchContext $context)
|
||||
{
|
||||
$multi = $context->getMultiHandle();
|
||||
|
||||
while ($done = curl_multi_info_read($multi)) {
|
||||
$transaction = $context->findTransaction($done['handle']);
|
||||
$this->processResponse($transaction, $done, $context);
|
||||
// Add the next transaction if there are more in the queue
|
||||
if ($next = $context->nextPending()) {
|
||||
$this->addHandle($next, $context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function processResponse(
|
||||
TransactionInterface $transaction,
|
||||
array $curl,
|
||||
BatchContext $context
|
||||
) {
|
||||
$info = $context->removeTransaction($transaction);
|
||||
|
||||
try {
|
||||
if (!$this->isCurlException($transaction, $curl, $context, $info)) {
|
||||
RequestEvents::emitComplete($transaction, $info);
|
||||
}
|
||||
} catch (RequestException $e) {
|
||||
$this->throwException($e, $context);
|
||||
}
|
||||
}
|
||||
|
||||
private function addHandle(
|
||||
TransactionInterface $transaction,
|
||||
BatchContext $context
|
||||
) {
|
||||
try {
|
||||
RequestEvents::emitBefore($transaction);
|
||||
// Only transfer if the request was not intercepted
|
||||
if (!$transaction->getResponse()) {
|
||||
$factory = $this->curlFactory;
|
||||
$context->addTransaction(
|
||||
$transaction,
|
||||
$factory($transaction, $this->messageFactory)
|
||||
);
|
||||
}
|
||||
} catch (RequestException $e) {
|
||||
$this->throwException($e, $context);
|
||||
}
|
||||
}
|
||||
|
||||
private function isCurlException(
|
||||
TransactionInterface $transaction,
|
||||
array $curl,
|
||||
BatchContext $context,
|
||||
array $info
|
||||
) {
|
||||
if (CURLM_OK == $curl['result'] ||
|
||||
CURLM_CALL_MULTI_PERFORM == $curl['result']
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$request = $transaction->getRequest();
|
||||
try {
|
||||
// Send curl stats along if they are available
|
||||
$stats = ['curl_result' => $curl['result']] + $info;
|
||||
RequestEvents::emitError(
|
||||
$transaction,
|
||||
new RequestException(
|
||||
sprintf(
|
||||
'[curl] (#%s) %s [url] %s',
|
||||
$curl['result'],
|
||||
function_exists('curl_strerror')
|
||||
? curl_strerror($curl['result'])
|
||||
: self::ERROR_STR,
|
||||
$request->getUrl()
|
||||
),
|
||||
$request
|
||||
),
|
||||
$stats
|
||||
);
|
||||
} catch (RequestException $e) {
|
||||
$this->throwException($e, $context);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function throwException(RequestException $e, BatchContext $context)
|
||||
{
|
||||
if ($context->throwsExceptions()) {
|
||||
$this->releaseMultiHandle($context->getMultiHandle());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a curl_multi handle from the cache or creates a new one
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
private function checkoutMultiHandle()
|
||||
{
|
||||
// Find an unused handle in the cache
|
||||
$key = array_search(false, $this->multiOwned, true);
|
||||
if (false !== $key) {
|
||||
$this->multiOwned[$key] = true;
|
||||
return $this->multiHandles[$key];
|
||||
}
|
||||
|
||||
// Add a new handle
|
||||
$handle = curl_multi_init();
|
||||
$id = (int) $handle;
|
||||
$this->multiHandles[$id] = $handle;
|
||||
$this->multiOwned[$id] = true;
|
||||
|
||||
return $handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases a curl_multi handle back into the cache and removes excess cache
|
||||
*
|
||||
* @param resource $handle Curl multi handle to remove
|
||||
*/
|
||||
private function releaseMultiHandle($handle)
|
||||
{
|
||||
$id = (int) $handle;
|
||||
|
||||
if (count($this->multiHandles) <= 3) {
|
||||
$this->multiOwned[$id] = false;
|
||||
} else {
|
||||
// Prune excessive handles
|
||||
curl_multi_close($this->multiHandles[$id]);
|
||||
unset($this->multiHandles[$id], $this->multiOwned[$id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Adapter\Curl;
|
||||
|
||||
use GuzzleHttp\Adapter\TransactionInterface;
|
||||
use GuzzleHttp\Event\RequestEvents;
|
||||
use GuzzleHttp\Message\MessageFactoryInterface;
|
||||
use GuzzleHttp\Stream\Stream;
|
||||
use GuzzleHttp\Stream\StreamInterface;
|
||||
|
||||
/**
|
||||
* Mediator between curl handles and request objects
|
||||
*/
|
||||
class RequestMediator
|
||||
{
|
||||
/** @var TransactionInterface */
|
||||
private $transaction;
|
||||
/** @var MessageFactoryInterface */
|
||||
private $messageFactory;
|
||||
private $statusCode;
|
||||
private $reasonPhrase;
|
||||
private $body;
|
||||
private $protocolVersion;
|
||||
private $headers;
|
||||
|
||||
/**
|
||||
* @param TransactionInterface $transaction Transaction to populate
|
||||
* @param MessageFactoryInterface $messageFactory Creates responses
|
||||
*/
|
||||
public function __construct(
|
||||
TransactionInterface $transaction,
|
||||
MessageFactoryInterface $messageFactory
|
||||
) {
|
||||
$this->transaction = $transaction;
|
||||
$this->messageFactory = $messageFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the body that will hold the response body
|
||||
*
|
||||
* @param StreamInterface $body Response body
|
||||
*/
|
||||
public function setResponseBody(StreamInterface $body = null)
|
||||
{
|
||||
$this->body = $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive a response header from curl
|
||||
*
|
||||
* @param resource $curl Curl handle
|
||||
* @param string $header Received header
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function receiveResponseHeader($curl, $header)
|
||||
{
|
||||
static $normalize = ["\r", "\n"];
|
||||
$length = strlen($header);
|
||||
$header = str_replace($normalize, '', $header);
|
||||
|
||||
if (strpos($header, 'HTTP/') === 0) {
|
||||
$startLine = explode(' ', $header, 3);
|
||||
// Only download the body to a target body when a successful
|
||||
// response is received.
|
||||
if ($startLine[1][0] != '2') {
|
||||
$this->body = null;
|
||||
}
|
||||
$this->statusCode = $startLine[1];
|
||||
$this->reasonPhrase = isset($startLine[2]) ? $startLine[2] : null;
|
||||
$this->protocolVersion = substr($startLine[0], -3);
|
||||
$this->headers = [];
|
||||
} elseif ($pos = strpos($header, ':')) {
|
||||
$this->headers[substr($header, 0, $pos)][] = substr($header, $pos + 1);
|
||||
} elseif ($header == '' && $this->statusCode >= 200) {
|
||||
$response = $this->messageFactory->createResponse(
|
||||
$this->statusCode,
|
||||
$this->headers,
|
||||
$this->body,
|
||||
[
|
||||
'protocol_version' => $this->protocolVersion,
|
||||
'reason_phrase' => $this->reasonPhrase
|
||||
]
|
||||
);
|
||||
$this->headers = $this->body = null;
|
||||
$this->transaction->setResponse($response);
|
||||
// Allows events to react before downloading any of the body
|
||||
RequestEvents::emitHeaders($this->transaction);
|
||||
}
|
||||
|
||||
return $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to the response body of a request
|
||||
*
|
||||
* @param resource $curl
|
||||
* @param string $write
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function writeResponseBody($curl, $write)
|
||||
{
|
||||
if (!($response = $this->transaction->getResponse())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Add a default body on the response if one was not found
|
||||
if (!($body = $response->getBody())) {
|
||||
$body = new Stream(fopen('php://temp', 'r+'));
|
||||
$response->setBody($body);
|
||||
}
|
||||
|
||||
return $body->write($write);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
return (string) $this->transaction->getRequest()->getBody()->read($length);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Adapter;
|
||||
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
|
||||
/**
|
||||
* Decorates a regular AdapterInterface object and creates a
|
||||
* ParallelAdapterInterface object that sends multiple HTTP requests serially.
|
||||
*/
|
||||
class FakeParallelAdapter implements ParallelAdapterInterface
|
||||
{
|
||||
/** @var AdapterInterface */
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* @param AdapterInterface $adapter Adapter used to send requests
|
||||
*/
|
||||
public function __construct(AdapterInterface $adapter)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
}
|
||||
|
||||
public function sendAll(\Iterator $transactions, $parallel)
|
||||
{
|
||||
foreach ($transactions as $transaction) {
|
||||
try {
|
||||
$this->adapter->send($transaction);
|
||||
} catch (RequestException $e) {
|
||||
// no op for batch transaction
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Adapter;
|
||||
|
||||
use GuzzleHttp\Event\RequestEvents;
|
||||
use GuzzleHttp\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Adapter that can be used to associate mock responses with a transaction
|
||||
* while still emulating the event workflow of real adapters.
|
||||
*/
|
||||
class MockAdapter implements AdapterInterface
|
||||
{
|
||||
private $response;
|
||||
|
||||
/**
|
||||
* @param ResponseInterface|callable $response Response to serve or function
|
||||
* to invoke that handles a transaction
|
||||
*/
|
||||
public function __construct($response = null)
|
||||
{
|
||||
$this->setResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the response that will be served by the adapter
|
||||
*
|
||||
* @param ResponseInterface|callable $response Response to serve or
|
||||
* function to invoke that handles a transaction
|
||||
*/
|
||||
public function setResponse($response)
|
||||
{
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
public function send(TransactionInterface $transaction)
|
||||
{
|
||||
RequestEvents::emitBefore($transaction);
|
||||
if (!$transaction->getResponse()) {
|
||||
|
||||
// Read the request body if it is present
|
||||
if ($transaction->getRequest()->getBody()) {
|
||||
$transaction->getRequest()->getBody()->__toString();
|
||||
}
|
||||
|
||||
$response = is_callable($this->response)
|
||||
? call_user_func($this->response, $transaction)
|
||||
: $this->response;
|
||||
if (!$response instanceof ResponseInterface) {
|
||||
throw new \RuntimeException('Invalid mocked response');
|
||||
}
|
||||
|
||||
$transaction->setResponse($response);
|
||||
RequestEvents::emitHeaders($transaction);
|
||||
RequestEvents::emitComplete($transaction);
|
||||
}
|
||||
|
||||
return $transaction->getResponse();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Adapter;
|
||||
|
||||
/**
|
||||
* Adapter interface used to transfer multiple HTTP requests in parallel.
|
||||
*
|
||||
* Parallel adapters follow the same rules as AdapterInterface except that
|
||||
* RequestExceptions are never thrown in a parallel transfer and parallel
|
||||
* adapters do not return responses.
|
||||
*/
|
||||
interface ParallelAdapterInterface
|
||||
{
|
||||
/**
|
||||
* Transfers multiple HTTP requests in parallel.
|
||||
*
|
||||
* RequestExceptions MUST not be thrown from a parallel transfer.
|
||||
*
|
||||
* @param \Iterator $transactions Iterator that yields TransactionInterface
|
||||
* @param int $parallel Max number of requests to send in parallel
|
||||
*/
|
||||
public function sendAll(\Iterator $transactions, $parallel);
|
||||
}
|
||||
|
|
@ -0,0 +1,347 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Adapter;
|
||||
|
||||
use GuzzleHttp\Event\RequestEvents;
|
||||
use GuzzleHttp\Exception\AdapterException;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use GuzzleHttp\Message\MessageFactoryInterface;
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
use GuzzleHttp\Stream;
|
||||
|
||||
/**
|
||||
* HTTP adapter that uses PHP's HTTP stream wrapper.
|
||||
*
|
||||
* When using the StreamAdapter, custom stream context options can be specified
|
||||
* using the **stream_context** option in a request's **config** option. The
|
||||
* structure of the "stream_context" option is an associative array where each
|
||||
* key is a transport name and each option is an associative array of options.
|
||||
*/
|
||||
class StreamAdapter implements AdapterInterface
|
||||
{
|
||||
/** @var MessageFactoryInterface */
|
||||
private $messageFactory;
|
||||
|
||||
/**
|
||||
* @param MessageFactoryInterface $messageFactory
|
||||
*/
|
||||
public function __construct(MessageFactoryInterface $messageFactory)
|
||||
{
|
||||
$this->messageFactory = $messageFactory;
|
||||
}
|
||||
|
||||
public function send(TransactionInterface $transaction)
|
||||
{
|
||||
// HTTP/1.1 streams using the PHP stream wrapper require a
|
||||
// Connection: close header. Setting here so that it is added before
|
||||
// emitting the request.before_send event.
|
||||
$request = $transaction->getRequest();
|
||||
if ($request->getProtocolVersion() == '1.1' &&
|
||||
!$request->hasHeader('Connection')
|
||||
) {
|
||||
$transaction->getRequest()->setHeader('Connection', 'close');
|
||||
}
|
||||
|
||||
RequestEvents::emitBefore($transaction);
|
||||
if (!$transaction->getResponse()) {
|
||||
$this->createResponse($transaction);
|
||||
RequestEvents::emitComplete($transaction);
|
||||
}
|
||||
|
||||
return $transaction->getResponse();
|
||||
}
|
||||
|
||||
private function createResponse(TransactionInterface $transaction)
|
||||
{
|
||||
$request = $transaction->getRequest();
|
||||
$stream = $this->createStream($request, $http_response_header);
|
||||
|
||||
if (!$request->getConfig()['stream']) {
|
||||
$stream = $this->getSaveToBody($request, $stream);
|
||||
}
|
||||
|
||||
// Track the response headers of the request
|
||||
$this->createResponseObject($http_response_header, $transaction, $stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drain the steam into the destination stream
|
||||
*/
|
||||
private function getSaveToBody(RequestInterface $request, $stream)
|
||||
{
|
||||
if ($saveTo = $request->getConfig()['save_to']) {
|
||||
// Stream the response into the destination stream
|
||||
$saveTo = is_string($saveTo)
|
||||
? Stream\create(fopen($saveTo, 'r+'))
|
||||
: Stream\create($saveTo);
|
||||
} else {
|
||||
// Stream into the default temp stream
|
||||
$saveTo = Stream\create();
|
||||
}
|
||||
|
||||
while (!feof($stream)) {
|
||||
$saveTo->write(fread($stream, 8096));
|
||||
}
|
||||
|
||||
fclose($stream);
|
||||
$saveTo->seek(0);
|
||||
|
||||
return $saveTo;
|
||||
}
|
||||
|
||||
private function createResponseObject(
|
||||
array $headers,
|
||||
TransactionInterface $transaction,
|
||||
$stream
|
||||
) {
|
||||
$parts = explode(' ', array_shift($headers), 3);
|
||||
$options = ['protocol_version' => substr($parts[0], -3)];
|
||||
if (isset($parts[2])) {
|
||||
$options['reason_phrase'] = $parts[2];
|
||||
}
|
||||
|
||||
// Set the size on the stream if it was returned in the response
|
||||
$responseHeaders = [];
|
||||
foreach ($headers as $header) {
|
||||
$headerParts = explode(':', $header, 2);
|
||||
$responseHeaders[$headerParts[0]] = isset($headerParts[1])
|
||||
? $headerParts[1]
|
||||
: '';
|
||||
}
|
||||
|
||||
$response = $this->messageFactory->createResponse(
|
||||
$parts[1],
|
||||
$responseHeaders,
|
||||
$stream,
|
||||
$options
|
||||
);
|
||||
|
||||
$transaction->setResponse($response);
|
||||
RequestEvents::emitHeaders($transaction);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a resource and check to ensure it was created successfully
|
||||
*
|
||||
* @param callable $callback Callable that returns stream resource
|
||||
* @param RequestInterface $request Request used when throwing exceptions
|
||||
* @param array $options Options used when throwing exceptions
|
||||
*
|
||||
* @return resource
|
||||
* @throws RequestException on error
|
||||
*/
|
||||
private function createResource(callable $callback, RequestInterface $request, $options)
|
||||
{
|
||||
// Turn off error reporting while we try to initiate the request
|
||||
$level = error_reporting(0);
|
||||
$resource = call_user_func($callback);
|
||||
error_reporting($level);
|
||||
|
||||
// If the resource could not be created, then grab the last error and
|
||||
// throw an exception.
|
||||
if (!is_resource($resource)) {
|
||||
$message = 'Error creating resource. [url] ' . $request->getUrl() . ' ';
|
||||
if (isset($options['http']['proxy'])) {
|
||||
$message .= "[proxy] {$options['http']['proxy']} ";
|
||||
}
|
||||
foreach (error_get_last() as $key => $value) {
|
||||
$message .= "[{$key}] {$value} ";
|
||||
}
|
||||
throw new RequestException(trim($message), $request);
|
||||
}
|
||||
|
||||
return $resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the stream for the request with the context options.
|
||||
*
|
||||
* @param RequestInterface $request Request being sent
|
||||
* @param mixed $http_response_header Populated by stream wrapper
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
private function createStream(
|
||||
RequestInterface $request,
|
||||
&$http_response_header
|
||||
) {
|
||||
static $methods;
|
||||
if (!$methods) {
|
||||
$methods = array_flip(get_class_methods(__CLASS__));
|
||||
}
|
||||
|
||||
$params = [];
|
||||
$options = $this->getDefaultOptions($request);
|
||||
foreach ($request->getConfig()->toArray() as $key => $value) {
|
||||
$method = "add_{$key}";
|
||||
if (isset($methods[$method])) {
|
||||
$this->{$method}($request, $options, $value, $params);
|
||||
}
|
||||
}
|
||||
|
||||
$this->applyCustomOptions($request, $options);
|
||||
$context = $this->createStreamContext($request, $options, $params);
|
||||
|
||||
return $this->createStreamResource(
|
||||
$request,
|
||||
$options,
|
||||
$context,
|
||||
$http_response_header
|
||||
);
|
||||
}
|
||||
|
||||
private function getDefaultOptions(RequestInterface $request)
|
||||
{
|
||||
$headers = '';
|
||||
foreach ($request->getHeaders() as $name => $values) {
|
||||
$headers .= $name . ': ' . implode(', ', $values) . "\r\n";
|
||||
}
|
||||
|
||||
return [
|
||||
'http' => [
|
||||
'method' => $request->getMethod(),
|
||||
'header' => trim($headers),
|
||||
'protocol_version' => $request->getProtocolVersion(),
|
||||
'ignore_errors' => true,
|
||||
'follow_location' => 0,
|
||||
'content' => (string) $request->getBody()
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
private function add_proxy(RequestInterface $request, &$options, $value, &$params)
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
$options['http']['proxy'] = $value;
|
||||
$options['http']['request_fulluri'] = true;
|
||||
} else {
|
||||
$scheme = $request->getScheme();
|
||||
if (isset($value[$scheme])) {
|
||||
$options['http']['proxy'] = $value[$scheme];
|
||||
$options['http']['request_fulluri'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function add_timeout(RequestInterface $request, &$options, $value, &$params)
|
||||
{
|
||||
$options['http']['timeout'] = $value;
|
||||
}
|
||||
|
||||
private function add_verify(RequestInterface $request, &$options, $value, &$params)
|
||||
{
|
||||
if ($value === true || is_string($value)) {
|
||||
$options['http']['verify_peer'] = true;
|
||||
if ($value !== true) {
|
||||
if (!file_exists($value)) {
|
||||
throw new \RuntimeException("SSL certificate authority file not found: {$value}");
|
||||
}
|
||||
$options['http']['allow_self_signed'] = true;
|
||||
$options['http']['cafile'] = $value;
|
||||
}
|
||||
} elseif ($value === false) {
|
||||
$options['http']['verify_peer'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
private function add_cert(RequestInterface $request, &$options, $value, &$params)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$options['http']['passphrase'] = $value[1];
|
||||
$value = $value[0];
|
||||
}
|
||||
|
||||
if (!file_exists($value)) {
|
||||
throw new \RuntimeException("SSL certificate not found: {$value}");
|
||||
}
|
||||
|
||||
$options['http']['local_cert'] = $value;
|
||||
}
|
||||
|
||||
private function add_debug(RequestInterface $request, &$options, $value, &$params)
|
||||
{
|
||||
static $map = [
|
||||
STREAM_NOTIFY_CONNECT => 'CONNECT',
|
||||
STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
|
||||
STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT',
|
||||
STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS',
|
||||
STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS',
|
||||
STREAM_NOTIFY_REDIRECTED => 'REDIRECTED',
|
||||
STREAM_NOTIFY_PROGRESS => 'PROGRESS',
|
||||
STREAM_NOTIFY_FAILURE => 'FAILURE',
|
||||
STREAM_NOTIFY_COMPLETED => 'COMPLETED',
|
||||
STREAM_NOTIFY_RESOLVE => 'RESOLVE'
|
||||
];
|
||||
|
||||
static $args = ['severity', 'message', 'message_code',
|
||||
'bytes_transferred', 'bytes_max'];
|
||||
|
||||
if (!is_resource($value)) {
|
||||
$value = fopen('php://output', 'w');
|
||||
}
|
||||
|
||||
$params['notification'] = function () use ($request, $value, $map, $args) {
|
||||
$passed = func_get_args();
|
||||
$code = array_shift($passed);
|
||||
fprintf($value, '<%s> [%s] ', $request->getUrl(), $map[$code]);
|
||||
foreach (array_filter($passed) as $i => $v) {
|
||||
fwrite($value, $args[$i] . ': "' . $v . '" ');
|
||||
}
|
||||
fwrite($value, "\n");
|
||||
};
|
||||
}
|
||||
|
||||
private function applyCustomOptions(
|
||||
RequestInterface $request,
|
||||
array &$options
|
||||
) {
|
||||
// Overwrite any generated options with custom options
|
||||
if ($custom = $request->getConfig()['stream_context']) {
|
||||
if (!is_array($custom)) {
|
||||
throw new AdapterException('stream_context must be an array');
|
||||
}
|
||||
$options = array_replace_recursive($options, $custom);
|
||||
}
|
||||
}
|
||||
|
||||
private function createStreamContext(
|
||||
RequestInterface $request,
|
||||
array $options,
|
||||
array $params
|
||||
) {
|
||||
return $this->createResource(function () use (
|
||||
$request,
|
||||
$options,
|
||||
$params
|
||||
) {
|
||||
return stream_context_create($options, $params);
|
||||
}, $request, $options);
|
||||
}
|
||||
|
||||
private function createStreamResource(
|
||||
RequestInterface $request,
|
||||
array $options,
|
||||
$context,
|
||||
&$http_response_header
|
||||
) {
|
||||
$url = $request->getUrl();
|
||||
// Add automatic gzip decompression
|
||||
if (strpos($request->getHeader('Accept-Encoding'), 'gzip') !== false) {
|
||||
$url = 'compress.zlib://' . $url;
|
||||
}
|
||||
|
||||
return $this->createResource(function () use (
|
||||
$url,
|
||||
&$http_response_header,
|
||||
$context
|
||||
) {
|
||||
if (false === strpos($url, 'http')) {
|
||||
trigger_error("URL is invalid: {$url}", E_USER_WARNING);
|
||||
return null;
|
||||
}
|
||||
return fopen($url, 'r', null, $context);
|
||||
}, $request, $options);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Adapter;
|
||||
|
||||
/**
|
||||
* Sends streaming requests to a streaming compatible adapter while sending all
|
||||
* other requests to a default adapter.
|
||||
*
|
||||
* This, for example, could be useful for taking advantage of the performance
|
||||
* benefits of the CurlAdapter while still supporting true streaming through
|
||||
* the StreamAdapter.
|
||||
*/
|
||||
class StreamingProxyAdapter implements AdapterInterface
|
||||
{
|
||||
private $defaultAdapter;
|
||||
private $streamingAdapter;
|
||||
|
||||
/**
|
||||
* @param AdapterInterface $defaultAdapter Adapter used for non-streaming responses
|
||||
* @param AdapterInterface $streamingAdapter Adapter used for streaming responses
|
||||
*/
|
||||
public function __construct(
|
||||
AdapterInterface $defaultAdapter,
|
||||
AdapterInterface $streamingAdapter
|
||||
) {
|
||||
$this->defaultAdapter = $defaultAdapter;
|
||||
$this->streamingAdapter = $streamingAdapter;
|
||||
}
|
||||
|
||||
public function send(TransactionInterface $transaction)
|
||||
{
|
||||
return $transaction->getRequest()->getConfig()['stream']
|
||||
? $this->streamingAdapter->send($transaction)
|
||||
: $this->defaultAdapter->send($transaction);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Adapter;
|
||||
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
use GuzzleHttp\Message\ResponseInterface;
|
||||
|
||||
class Transaction implements TransactionInterface
|
||||
{
|
||||
/** @var ClientInterface */
|
||||
private $client;
|
||||
/** @var RequestInterface */
|
||||
private $request;
|
||||
/** @var ResponseInterface */
|
||||
private $response;
|
||||
|
||||
/**
|
||||
* @param ClientInterface $client Client that is used to send the requests
|
||||
* @param RequestInterface $request
|
||||
*/
|
||||
public function __construct(
|
||||
ClientInterface $client,
|
||||
RequestInterface $request
|
||||
) {
|
||||
$this->client = $client;
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
public function setResponse(ResponseInterface $response)
|
||||
{
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
public function getClient()
|
||||
{
|
||||
return $this->client;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Adapter;
|
||||
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
use GuzzleHttp\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Represents a transactions that consists of a request, response, and client
|
||||
*/
|
||||
interface TransactionInterface
|
||||
{
|
||||
/**
|
||||
* @return RequestInterface
|
||||
*/
|
||||
public function getRequest();
|
||||
|
||||
/**
|
||||
* @return ResponseInterface|null
|
||||
*/
|
||||
public function getResponse();
|
||||
|
||||
/**
|
||||
* Set a response on the transaction
|
||||
*
|
||||
* @param ResponseInterface $response Response to set
|
||||
*/
|
||||
public function setResponse(ResponseInterface $response);
|
||||
|
||||
/**
|
||||
* @return ClientInterface
|
||||
*/
|
||||
public function getClient();
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Adapter;
|
||||
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use GuzzleHttp\Event\ListenerAttacherTrait;
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Converts a sequence of request objects into a transaction.
|
||||
* @internal
|
||||
*/
|
||||
class TransactionIterator implements \Iterator
|
||||
{
|
||||
use ListenerAttacherTrait;
|
||||
|
||||
/** @var \Iterator */
|
||||
private $source;
|
||||
|
||||
/** @var ClientInterface */
|
||||
private $client;
|
||||
|
||||
/** @var array Listeners to attach to each request */
|
||||
private $eventListeners = [];
|
||||
|
||||
public function __construct(
|
||||
$source,
|
||||
ClientInterface $client,
|
||||
array $options
|
||||
) {
|
||||
$this->client = $client;
|
||||
$this->eventListeners = $this->prepareListeners(
|
||||
$options,
|
||||
['before', 'complete', 'error']
|
||||
);
|
||||
if ($source instanceof \Iterator) {
|
||||
$this->source = $source;
|
||||
} elseif (is_array($source)) {
|
||||
$this->source = new \ArrayIterator($source);
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Expected an Iterator or array');
|
||||
}
|
||||
}
|
||||
|
||||
public function current()
|
||||
{
|
||||
$request = $this->source->current();
|
||||
if (!$request instanceof RequestInterface) {
|
||||
throw new \RuntimeException('All must implement RequestInterface');
|
||||
}
|
||||
|
||||
$this->attachListeners($request, $this->eventListeners);
|
||||
|
||||
return new Transaction($this->client, $request);
|
||||
}
|
||||
|
||||
public function next()
|
||||
{
|
||||
$this->source->next();
|
||||
}
|
||||
|
||||
public function key()
|
||||
{
|
||||
return $this->source->key();
|
||||
}
|
||||
|
||||
public function valid()
|
||||
{
|
||||
return $this->source->valid();
|
||||
}
|
||||
|
||||
public function rewind() {}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue