private function wrapHandler(callable $handler, LoggerInterface $logger, LoggerInterface $tracer) { return function (array $request, Connection $connection, Transport $transport = null, $options) use($handler, $logger, $tracer) { $this->lastRequest = ['request' => $request]; // Send the request using the wrapped handler. $response = Core::proxy($handler($request), function ($response) use($connection, $transport, $logger, $tracer, $request, $options) { $this->lastRequest['response'] = $response; if (isset($response['error'])) { if ($response['error'] instanceof ConnectException || $response['error'] instanceof RingException) { $this->log->warning('Curl exception encountered.'); $exception = $this->getCurlRetryException($request, $response); $this->logRequestFail($request['http_method'], $response['effective_url'], $request['body'], $request['headers'], $response['status'], $response['body'], $response['transfer_stats']['total_time'], $exception); $node = $connection->getHost(); $this->log->warning("Marking node {$node} dead."); $connection->markDead(); // If the transport has not been set, we are inside a Ping or Sniff, // so we don't want to retrigger retries anyway. // // TODO this could be handled better, but we are limited because connectionpools do not // have access to Transport. Architecturally, all of this needs to be refactored if (isset($transport)) { $transport->connectionPool->scheduleCheck(); $neverRetry = isset($request['client']['never_retry']) ? $request['client']['never_retry'] : false; $shouldRetry = $transport->shouldRetry($request); $shouldRetryText = $shouldRetry ? 'true' : 'false'; $this->log->warning("Retries left? {$shouldRetryText}"); if ($shouldRetry && !$neverRetry) { return $transport->performRequest($request['http_method'], $request['uri'], [], $request['body'], $options); } } $this->log->warning("Out of retries, throwing exception from {$node}"); // Only throw if we run out of retries throw $exception; } else { // Something went seriously wrong, bail $exception = new TransportException($response['error']->getMessage()); $this->logRequestFail($request['http_method'], $response['effective_url'], $request['body'], $request['headers'], $response['status'], $response['body'], $response['transfer_stats']['total_time'], $exception); throw $exception; } } else { $connection->markAlive(); if (isset($response['body']) === true) { $response['body'] = stream_get_contents($response['body']); $this->lastRequest['response']['body'] = $response['body']; } if ($response['status'] >= 400 && $response['status'] < 500) { $ignore = isset($request['client']['ignore']) ? $request['client']['ignore'] : []; $this->process4xxError($request, $response, $ignore); } elseif ($response['status'] >= 500) { $ignore = isset($request['client']['ignore']) ? $request['client']['ignore'] : []; $this->process5xxError($request, $response, $ignore); } // No error, deserialize $response['body'] = $this->serializer->deserialize($response['body'], $response['transfer_stats']); } $this->logRequestSuccess($request['http_method'], $response['effective_url'], $request['body'], $request['headers'], $response['status'], $response['body'], $response['transfer_stats']['total_time']); return isset($request['client']['verbose']) && $request['client']['verbose'] === true ? $response : $response['body']; }); return $response; }; }
private function wrapHandler(callable $handler, LoggerInterface $logger, LoggerInterface $tracer) { return function (array $request, Connection $connection, Transport $transport = null, $options) use($handler, $logger, $tracer) { $this->lastRequest = []; $this->lastRequest['request'] = $request; // Send the request using the wrapped handler. $response = Core::proxy($handler($request), function ($response) use($connection, $transport, $logger, $tracer, $request, $options) { $this->lastRequest['response'] = $response; if (isset($response['error']) === true) { if ($response['error'] instanceof ConnectException || $response['error'] instanceof RingException) { $connection->markDead(); $transport->connectionPool->scheduleCheck(); $neverRetry = isset($request['client']['never_retry']) ? $request['client']['never_retry'] : false; $shouldRetry = $transport->shouldRetry($request); if ($shouldRetry && !$neverRetry) { return $transport->performRequest($request['http_method'], $request['uri'], [], $request['body'], $options); } // Due to the magic of futures, this will only be invoked if the final retry fails, since // successful resolutions will go down the alternate `else` path the second time through // the proxy $this->throwCurlException($request, $response); } else { // Something went seriously wrong, bail throw new TransportException($response['error']->getMessage()); } } else { $connection->markAlive(); if (isset($response['body']) === true) { $response['body'] = stream_get_contents($response['body']); $this->lastRequest['response']['body'] = $response['body']; } if ($response['status'] >= 400 && $response['status'] < 500) { $ignore = isset($request['client']['ignore']) ? $request['client']['ignore'] : []; $this->process4xxError($request, $response, $ignore); } elseif ($response['status'] >= 500) { $ignore = isset($request['client']['ignore']) ? $request['client']['ignore'] : []; $this->process5xxError($request, $response, $ignore); } // No error, deserialize $response['body'] = $this->serializer->deserialize($response['body'], $response['transfer_stats']); } $this->logRequestSuccess($request['http_method'], $response['effective_url'], $request['body'], $request['headers'], $response['status'], $response['body'], $response['transfer_stats']['total_time']); return isset($request['client']['verbose']) && $request['client']['verbose'] === true ? $response : $response['body']; }); return $response; }; }
public function testProxiesDeferredFutureFailure() { $d = new Deferred(); $f = new FutureArray($d->promise()); $f2 = Core::proxy($f); $d->reject(new \Exception('foo')); try { $f2['hello?']; $this->fail('did not throw'); } catch (\Exception $e) { $this->assertEquals('foo', $e->getMessage()); } }
/** * @param callable $handler * @param LoggerInterface $logger * @return callable */ public static function processResponse(callable $handler, LoggerInterface $logger) { return function (array $request) use($handler, $logger) { return Core::proxy($handler($request), function (array $response) use($request, $logger) { // Is there any error? if (isset($response['error'])) { $exception = new TransportException('Request failure', 0, $response['error']); Logger::logState($logger, $request, $response, $exception, LogLevel::CRITICAL); throw $exception; } $response['json'] = null; // Read body if (isset($response['body']) && is_resource($response['body'])) { $response['body'] = stream_get_contents($response['body']); // false if something wrong // Extract json data if ($response['body']) { $response['json'] = json_decode($response['body'], true); // null if something wrong } } // Process errors if ($response['status'] >= 400) { $ignore = isset($request['client']['ignore']) ? (array) $request['client']['ignore'] : []; // It is possible to ignore some status codes if (!in_array($response['status'], $ignore)) { // Is there any message? $message = isset($response['json']['message']) ? $response['json']['message'] : 'Unknown error'; if ($response['status'] >= 400 && $response['status'] < 500) { if (404 == $response['status']) { $exception = new ResourceNotFoundException(); } else { $exception = new BadRequestException($message, $response['status']); } } else { $exception = new ServerException($message, $response['status']); } Logger::logState($logger, $request, $response, $exception, LogLevel::ERROR); throw $exception; } } Logger::logState($logger, $request, $response); return $response; }); }; }