Skip to content

Instantly share code, notes, and snippets.

@polyfractal
Last active December 18, 2015 15:38
Show Gist options
  • Save polyfractal/5805379 to your computer and use it in GitHub Desktop.
Save polyfractal/5805379 to your computer and use it in GitHub Desktop.
<?php
/**
* Naive curl multi-handle implementation. Was only supposed to be a temp function
* hence the ugliness
*/
public function performRequest($method, $uri, $params = null, $body = null)
{
$uri = $this->host . $uri;
if (isset($params) === true) {
$uri .= '?' . http_build_query($params);
}
$curlHandle = curl_init();
$opts = array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 3,
CURLOPT_CONNECTTIMEOUT => 3,
CURLOPT_URL => $uri,
CURLOPT_CUSTOMREQUEST => $method,
);
if (isset($body) === true) {
$opts[CURLOPT_POST] = true;
$opts[CURLOPT_POSTFIELDS] = $body;
}
// Custom Curl options? Merge them.
if (isset($this->connectionParams['curlOpts']) === true) {
$opts = array_merge($opts, $this->connectionParams['curlOpts']);
}
$this->log->addDebug("Curl Options:", $opts);
curl_setopt_array($curlHandle, $opts);
curl_multi_add_handle($this->multiHandle, $curlHandle);
$response = array();
do {
do {
$execrun = curl_multi_exec($this->multiHandle, $running);
} while ($execrun == CURLM_CALL_MULTI_PERFORM && $running === 1);
if ($execrun !== CURLM_OK) {
$this->log->addCritical('Unexpected Curl error: ' . $execrun);
throw new TransportException('Unexpected Curl error: ' . $execrun);
}
/*
Curl_multi_select not strictly nescessary, since we are only
performing one request total. May be useful if we ever
implement batching
From Guzzle: https://github.com/guzzle/guzzle/blob/master/src/Guzzle/Http/Curl/CurlMulti.php#L314
Select the curl handles until there is any
activity on any of the open file descriptors. See:
https://github.com/php/php-src/blob/master/ext/curl/multi.c#L170
*/
if ($running === 1 && $execrun === CURLM_OK && curl_multi_select($this->multiHandle, 0.001) === -1) {
/*
Perform a usleep if a previously executed select returned -1
@see https://bugs.php.net/bug.php?id=61141
*/
usleep(100);
}
// A request was just completed.
while ($transfer = curl_multi_info_read($this->multiHandle)) {
$response['responseText'] = curl_multi_getcontent($transfer['handle']);
$response['errorNumber'] = curl_errno($transfer['handle']);
$response['error'] = curl_error($transfer['handle']);
$response['requestInfo'] = curl_getinfo($transfer['handle']);
curl_multi_remove_handle($this->multiHandle, $transfer['handle']);
}
} while ($running === 1);
// If there was an error response, something like a time-out or
// refused connection error occurred.
if ($response['error'] !== '') {
$this->log->addError('Curl error: ' . $response['error']);
throw new TransportException('Curl error: ' . $response['error']);
}
// Log all 4xx-5xx errors.
if ($response['requestInfo']['http_code'] >= 400) {
$this->logRequestFail(
$method,
$uri,
$response['requestInfo']['total_time'],
$response['requestInfo']['http_code'],
$response['responseText'],
$response['error']
);
// Throw exceptions on 5xx (server) errors.
if ($response['requestInfo']['http_code'] >= 500) {
$exceptionText = $response['requestInfo']['http_code'] . ' Server Exception: ' . $response['responseText'];
$this->log->addError($exceptionText);
throw new ServerErrorResponseException($exceptionText);
}
}
$this->logRequestSuccess(
$method,
$uri,
$body,
$response['requestInfo']['http_code'],
$response['responseText'],
$response['requestInfo']['total_time']
);
return array(
'status' => $response['requestInfo']['http_code'],
'text' => $response['responseText'],
'info' => $response['requestInfo'],
);
}
<?php
/**
* These are the two relevant setup methods for the curlMultiHandleConnection and GuzzleConnection
* objects. Both are shared through the Pimple DIC, so a single multi-handle or Guzzle Client
* are shared between all the connections.
*/
private function setCurlMultihandle()
{
// Only used by some connections - won't be instantiated until used.
$this->dic['curlMultiHandle'] = $this->dic->share(
function () {
return curl_multi_init();
}
);
}
private function setGuzzleClient()
{
// Only used by Guzzle connections - won't be instantiated until used.
$this->dic['guzzleClient'] = $this->dic->share(
function () {
$guzzle = new \Guzzle\Http\Client(null,array(
'curl.options' => array(
'body_as_string' => true
)
));
return $guzzle;
}
);
}
<?php
/**
* Equivalent to performRequest in curlMultiHandleConnection
*/
public function performRequest($method, $uri, $params = null, $body = null)
{
$uri = $this->getURI($uri, $params);
$request = $this->buildGuzzleRequest($method, $uri, $body);
$response = $this->sendRequest($request, $body);
return array(
'status' => $response->getStatusCode(),
'text' => $response->getBody(true),
'info' => $response->getInfo(),
);
}
/**
* @param string $uri
* @param array $params
*
* @return string
*/
private function getURI($uri, $params)
{
$uri = $this->host . $uri;
if (isset($params) === true) {
$uri .= '?' . http_build_query($params);
}
return $uri;
}
/**
* @param string $method
* @param string $uri
* @param string $body
*
* @return Request
*/
private function buildGuzzleRequest($method, $uri, $body)
{
if ($method === 'GET' && isset($body) === true) {
$method = 'POST';
}
if (isset($body) === true) {
$request = $this->guzzle->$method($uri, array(), $body);
} else {
$request = $this->guzzle->$method($uri, array());
}
return $request;
}
/**
* @param Request $request
*
* @param string $body
*
* @return \Guzzle\Http\Message\Response
* @throws \Elasticsearch\Common\Exceptions\TransportException
*/
private function sendRequest(Request $request, $body)
{
try {
$request->send();
} catch (ServerErrorResponseException $exception) {
$this->process5xxError($request, $exception, $body);
} catch (ClientErrorResponseException $exception) {
$this->logErrorDueToFailure($request, $exception, $body);
} catch (CurlException $exception) {
$this->processCurlError($exception);
} catch (\Exception $exception) {
$error = 'Unexpected error: ' . $exception->getMessage();
$this->log->addCritical($error);
throw new TransportException($error);
}
$this->processSuccessfulRequest($request, $body);
return $request->getResponse();
}
<?php
/**
* User: zach
* Date: 6/14/13
* Time: 11:13 AM
*/
namespace Elasticsearch\Benchmarks;
use \Athletic\AthleticEvent;
use Elasticsearch\Client;
class IndexingEvent extends AthleticEvent
{
private $setupClient;
/** @var Client */
private $client;
/** @var Client */
private $guzzleClient;
private $document;
private $largeDocument;
private $mediumDocument;
protected function setUp()
{
$clientParams = array(
'hosts' => array('localhost:9400'),
);
$this->client = new Client($clientParams);
$guzzleParams = array(
'hosts' => array('localhost:9400'),
'connectionClass' => '\Elasticsearch\Connections\GuzzleConnection'
);
$this->guzzleClient = new Client($guzzleParams);
$indexParams['index'] = 'benchmarking_index';
// Setup code to create the elasticsearch index - does not go through the proxy
$this->setupClient = new Client();
$this->setupClient->indices()->create($indexParams);
$this->document = array();
$this->document['body'] = array('testField' => 'abc');
$this->document['index'] = 'benchmarking_index';
$this->document['type'] = 'test';
$this->mediumDocument = array();
$this->mediumDocument['body']['testField'] = str_repeat('a', 5000);
$this->mediumDocument['index'] = 'benchmarking_index';
$this->mediumDocument['type'] = 'test';
$this->largeDocument = array();
$this->largeDocument['body']['testField'] = str_repeat('a', 1000);
$this->largeDocument['index'] = 'benchmarking_index';
$this->largeDocument['type'] = 'test';
}
protected function tearDown()
{
$indexParams['index'] = 'benchmarking_index';
$this->setupClient->indices()->delete($indexParams);
}
/**
* @iterations 100
*/
public function guzzleSmall()
{
$this->guzzleClient->index($this->document);
}
/**
* @iterations 100
*/
public function curlMultiHandleSmall()
{
$this->client->index($this->document);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment