Skip to content
This repository was archived by the owner on Jan 30, 2020. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/Client/Adapter/Curl.php
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,10 @@ public function connect($host, $port = 80, $secure = false)
* to wrong host, no PUT file defined, unsupported method, or unsupported
* cURL option.
* @throws AdapterException\InvalidArgumentException if $method is currently not supported
* @throws AdapterException\TimeoutException when a request exceeds the CURLOPT_TIMEOUT
* setting before it completes.
* @throws AdapterException\ConnectTimeoutException when a request exceeds the
* CURLOPT_CONNECTTIMEOUT setting when trying to connect to a host:port.
*/
public function write($method, $uri, $httpVersion = 1.1, $headers = [], $body = '')
{
Expand Down Expand Up @@ -425,7 +429,17 @@ public function write($method, $uri, $httpVersion = 1.1, $headers = [], $body =
$request .= $body;

if (empty($this->response)) {
throw new AdapterException\RuntimeException("Error in cURL request: " . curl_error($this->curl));
$errorMessage = "Error in cURL request: " . curl_error($this->curl);
preg_match('/: (.*?) timed out/', $errorMessage, $matches);
$type = isset($matches[1]) ? $matches[1] : null;
switch ($type) {
case 'Operation':
throw new AdapterException\TimeoutException($errorMessage);
case 'Connection':
throw new AdapterException\ConnectTimeoutException($errorMessage);
default:
throw new AdapterException\RuntimeException($errorMessage);
}
}

// separating header from body because it is dangerous to accidentially replace strings in the body
Expand Down
19 changes: 19 additions & 0 deletions src/Client/Adapter/Exception/ConnectTimeoutException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace Zend\Http\Client\Adapter\Exception;

/**
* Connection timeout exceptions occur when the adapter exceeds the specified time limit
* to connect to a host:port, whereas timeout exceptions occur when the adapter exceeds the time
* limit to complete an operation.
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a description detailing why this is different from other timeout exceptions

class ConnectTimeoutException extends RuntimeException implements ExceptionInterface
{
}
69 changes: 69 additions & 0 deletions test/Client/CurlTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -397,4 +397,73 @@ public function testSetCurlOptPostFields()
$this->client->send();
$this->assertEquals('foo=bar', $this->client->getResponse()->getBody());
}

public function testCurlThrowsTimeoutExceptionWhenTimeoutErrorOccursForValidAddress()
{
$timeout = 1;
$timeoutInMs = $timeout * 1000 + 1;
$this->client->setUri($this->baseuri . "testTimeout.php?timeout=$timeout");
$adapter = new Adapter\Curl();
$adapter->setOptions([
'curloptions' => [CURLOPT_TIMEOUT => $timeout]
]);
$this->client->setAdapter($adapter);
$this->client->setMethod('GET');
$this->setExpectedException(Adapter\Exception\TimeoutException::CLASS, "Error in cURL request: Operation timed out after $timeoutInMs milliseconds with 0 bytes received");
$this->client->send();
}

public function testCurlThrowsConnectTimeoutExceptionWhenNonRoutableAddressIsUsed()
{
$timeoutInMs = 1001;
$this->client->setUri('http://10.0.0.0');
$adapter = new Adapter\Curl();
$adapter->setOptions([
'curloptions' => [CURLOPT_CONNECTTIMEOUT_MS => $timeoutInMs - 1]
]);
$this->client->setAdapter($adapter);
$this->client->setMethod('GET');
$this->setExpectedException(Adapter\Exception\ConnectTimeoutException::CLASS, "Error in cURL request: Connection timed out after $timeoutInMs milliseconds");
$this->client->send();
}

public function testCurlThrowsRuntimeExceptionWhenNonTimeoutErrorOccurs()
{
$this->client->setUri($this->baseuri . "testHttpAuth.php");
$adapter = new Adapter\Curl();
$adapter->setOptions([
'curloptions' => [CURLOPT_FAILONERROR => true]
]);
$this->client->setAdapter($adapter);
$this->client->setMethod('GET');
$this->setExpectedException(Adapter\Exception\RuntimeException::CLASS, "Error in cURL request: The requested URL returned error: 401 Unauthorized");
$this->client->send();
}

public function testCurlRetrievesResponseWhenTimeoutExceptionDoesNotOccur()
{
$timeout = 1;
$this->client->setUri($this->baseuri . "testTimeout.php?timeout=$timeout");
$adapter = new Adapter\Curl();
$adapter->setOptions([
'curloptions' => [CURLOPT_TIMEOUT => $timeout + 1]
]);
$this->client->setAdapter($adapter);
$this->client->setMethod('GET');
$this->client->send();
$this->assertEquals($timeout, $this->client->getResponse()->getBody());
}

public function testCurlRetrievesResponseWhenConnectTimeoutExceptionDoesNotOccur()
{
$this->client->setUri($this->baseuri . "testSimpleRequests.php");
$adapter = new Adapter\Curl();
$adapter->setOptions([
'curloptions' => [CURLOPT_CONNECTTIMEOUT_MS => 1000]
]);
$this->client->setAdapter($adapter);
$this->client->setMethod('GET');
$this->client->send();
$this->assertEquals('Success' . PHP_EOL, $this->client->getResponse()->getBody());
}
}
12 changes: 12 additions & 0 deletions test/Client/_files/testTimeout.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

$timeout = isset($_GET['timeout']) ? $_GET['timeout'] : null;
sleep($timeout);
echo($timeout);