Skip to content
This repository was archived by the owner on Jan 30, 2020. It is now read-only.

Commit 09f4d27

Browse files
committed
Update for release 2.6.0
2 parents 22e0317 + 03864a9 commit 09f4d27

File tree

15 files changed

+300
-10
lines changed

15 files changed

+300
-10
lines changed

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,28 @@
22

33
All notable changes to this project will be documented in this file, in reverse chronological order by release.
44

5+
## 2.6.0 - 2017-01-31
6+
7+
### Added
8+
- [#99](https://github.com/zendframework/zend-http/pull/99) added
9+
TimeoutException for cURL adapter.
10+
- [#98](https://github.com/zendframework/zend-http/pull/98) added connection
11+
timeout (`connecttimeout`) for cURL and Socket adapters.
12+
- [#97](https://github.com/zendframework/zend-http/pull/97) added support to
13+
`sslcafile` and `sslcapath` to cURL adapter.
14+
15+
### Deprecated
16+
17+
- Nothing.
18+
19+
### Removed
20+
21+
- Nothing.
22+
23+
### Fixed
24+
25+
- Nothing.
26+
527
## 2.5.6 - 2017-01-31
628

729
### Added
@@ -27,6 +49,8 @@ All notable changes to this project will be documented in this file, in reverse
2749
Security Policy CSP HTTP header when it is `none` (empty value).
2850
- [#92](https://github.com/zendframework/zend-http/pull/92) fixes the flatten
2951
cookies value for array value (also multidimensional).
52+
- [#34](https://github.com/zendframework/zend-http/issues/33) fixes the
53+
standard separator (&) for application/x-www-form-urlencoded.
3054

3155
## 2.5.5 - 2016-08-08
3256

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
"homepage": "https://github.com/zendframework/zend-http",
1010
"extra": {
1111
"branch-alias": {
12-
"dev-master": "2.5-dev",
13-
"dev-develop": "2.6-dev"
12+
"dev-master": "2.6-dev",
13+
"dev-develop": "2.7-dev"
1414
}
1515
},
1616
"require": {

doc/book/client/adapters.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ via either the client's constructor or `setOptions()` method:
3333
Parameter | Description | Expected Type | Default Value
3434
---------------------|--------------------------------------------------------------------------------------|---------------|--------------
3535
`persistent` | Whether to use persistent TCP connections | boolean | `FALSE`
36-
`ssltransport` | SSL transport layer (eg. `sslv2`, `tls`) | string | `ssl`
36+
`ssltransport` | SSL transport layer (eg. `sslv2`, `tls`) | string | `ssl`
3737
`sslcert` | Path to a PEM encoded SSL certificate | string | `NULL`
3838
`sslpassphrase` | Passphrase for the SSL certificate file | string | `NULL`
3939
`sslverifypeer` | Whether to verify the SSL peer | string | `TRUE`
@@ -259,6 +259,17 @@ the 'curloptions' key in the constructor of the adapter, or by calling
259259
constants of the cURL extension. You can get access to the underling cURL handle
260260
by calling `$adapter->getHandle();`
261261

262+
The cURL configuration options that can be set via `setCurlOption($name, $value)`
263+
method are:
264+
265+
Parameter | Description | Expected Type | Default Value
266+
---------------------|--------------------------------------------------------------------------------------|---------------|--------------
267+
`proxyuser` | Whether to use persistent TCP connections | boolean | `FALSE`
268+
`proxypass` | SSL transport layer (eg. `sslv2`, `tls`) | string | `ssl`
269+
`proxyhost` | Path to a PEM encoded SSL certificate | string | `NULL`
270+
`proxyport` | Passphrase for the SSL certificate file | string | `NULL`
271+
`sslverifypeer` | Whether to verify the SSL peer | string | `TRUE`
272+
262273
### Transfering files by handle
263274

264275
You can use cURL to transfer very large files over HTTP by filehandle.

doc/book/client/intro.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ Parameter | Description
8484
`encodecookies` | Whether to pass the cookie value through urlencode/urldecode. Enabling this breaks support with some web servers. Disabling this limits the range of values the cookies can contain. | boolean | TRUE
8585
`outputstream` | Destination for streaming of received data (options: string (filename), true for temp file, false/null to disable streaming) | boolean | FALSE
8686
`rfc3986strict` | Whether to strictly adhere to RFC 3986 (in practice, this means replacing '+' with '%20') | boolean | FALSE
87+
`sslcapath` | Path to SSL certificate directory | string | `NULL`
88+
`sslcafile` | Path to Certificate Authority (CA) bundle | string | `NULL`
8789

8890
The options are also passed to the adapter class upon instantiation, so the same
8991
configuration can be used for adapter configuration. See the

src/Client.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,17 @@ class Client implements Stdlib\DispatchableInterface
103103
'strictredirects' => false,
104104
'useragent' => 'Zend\Http\Client',
105105
'timeout' => 10,
106+
'connecttimeout' => null,
106107
'adapter' => 'Zend\Http\Client\Adapter\Socket',
107108
'httpversion' => Request::VERSION_11,
108109
'storeresponse' => true,
109110
'keepalive' => false,
110111
'outputstream' => false,
111112
'encodecookies' => true,
112113
'argseparator' => null,
113-
'rfc3986strict' => false
114+
'rfc3986strict' => false,
115+
'sslcafile' => null,
116+
'sslcapath' => null,
114117
];
115118

116119
/**

src/Client/Adapter/Curl.php

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020
*/
2121
class Curl implements HttpAdapter, StreamInterface
2222
{
23+
/**
24+
* Operation timeout.
25+
*
26+
* @var int
27+
*/
28+
const ERROR_OPERATION_TIMEDOUT = 28;
29+
2330
/**
2431
* Parameters array
2532
*
@@ -195,20 +202,36 @@ public function connect($host, $port = 80, $secure = false)
195202
curl_setopt($this->curl, CURLOPT_PORT, intval($port));
196203
}
197204

198-
if (isset($this->config['timeout'])) {
205+
if (isset($this->config['connecttimeout'])) {
206+
$connectTimeout = $this->config['connecttimeout'];
207+
} elseif (isset($this->config['timeout'])) {
208+
$connectTimeout = $this->config['timeout'];
209+
} else {
210+
$connectTimeout = null;
211+
}
212+
if ($connectTimeout !== null) {
199213
if (defined('CURLOPT_CONNECTTIMEOUT_MS')) {
200-
curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT_MS, $this->config['timeout'] * 1000);
214+
curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT_MS, $connectTimeout * 1000);
201215
} else {
202-
curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT, $this->config['timeout']);
216+
curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT, $connectTimeout);
203217
}
218+
}
204219

220+
if (isset($this->config['timeout'])) {
205221
if (defined('CURLOPT_TIMEOUT_MS')) {
206222
curl_setopt($this->curl, CURLOPT_TIMEOUT_MS, $this->config['timeout'] * 1000);
207223
} else {
208224
curl_setopt($this->curl, CURLOPT_TIMEOUT, $this->config['timeout']);
209225
}
210226
}
211227

228+
if (isset($this->config['sslcafile']) && $this->config['sslcafile']) {
229+
curl_setopt($this->curl, CURLOPT_CAINFO, $this->config['sslcafile']);
230+
}
231+
if (isset($this->config['sslcapath']) && $this->config['sslcapath']) {
232+
curl_setopt($this->curl, CURLOPT_CAPATH, $this->config['sslcapath']);
233+
}
234+
212235
if (isset($this->config['maxredirects'])) {
213236
// Set Max redirects
214237
curl_setopt($this->curl, CURLOPT_MAXREDIRS, $this->config['maxredirects']);
@@ -247,6 +270,7 @@ public function connect($host, $port = 80, $secure = false)
247270
* to wrong host, no PUT file defined, unsupported method, or unsupported
248271
* cURL option.
249272
* @throws AdapterException\InvalidArgumentException if $method is currently not supported
273+
* @throws AdapterException\TimeoutException if connection timed out
250274
*/
251275
public function write($method, $uri, $httpVersion = 1.1, $headers = [], $body = '')
252276
{
@@ -425,6 +449,12 @@ public function write($method, $uri, $httpVersion = 1.1, $headers = [], $body =
425449
$request .= $body;
426450

427451
if ($response === false || empty($this->response)) {
452+
if (curl_errno($this->curl) === static::ERROR_OPERATION_TIMEDOUT) {
453+
throw new AdapterException\TimeoutException(
454+
"Read timed out",
455+
AdapterException\TimeoutException::READ_TIMEOUT
456+
);
457+
}
428458
throw new AdapterException\RuntimeException("Error in cURL request: " . curl_error($this->curl));
429459
}
430460

src/Client/Adapter/Socket.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class Socket implements HttpAdapter, StreamInterface
7070
'sslcapath' => null,
7171
'sslallowselfsigned' => false,
7272
'sslusecontext' => false,
73+
'sslverifypeername' => true,
7374
];
7475

7576
/**
@@ -238,19 +239,35 @@ public function connect($host, $port = 80, $secure = false)
238239
throw new AdapterException\RuntimeException('Unable to set sslpassphrase option');
239240
}
240241
}
242+
243+
if ($this->config['sslverifypeername'] !== null) {
244+
if (! stream_context_set_option(
245+
$context,
246+
'ssl',
247+
'verify_peer_name',
248+
$this->config['sslverifypeername']
249+
)) {
250+
throw new AdapterException\RuntimeException('Unable to set sslverifypeername option');
251+
}
252+
}
241253
}
242254

243255
$flags = STREAM_CLIENT_CONNECT;
244256
if ($this->config['persistent']) {
245257
$flags |= STREAM_CLIENT_PERSISTENT;
246258
}
247259

260+
if (isset($this->config['connecttimeout'])) {
261+
$connectTimeout = $this->config['connecttimeout'];
262+
} else {
263+
$connectTimeout = $this->config['timeout'];
264+
}
248265
ErrorHandler::start();
249266
$this->socket = stream_socket_client(
250267
$host . ':' . $port,
251268
$errno,
252269
$errstr,
253-
(int) $this->config['timeout'],
270+
(int) $connectTimeout,
254271
$flags,
255272
$context
256273
);

src/Response.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class Response extends AbstractMessage implements ResponseInterface
7070
const STATUS_CODE_428 = 428;
7171
const STATUS_CODE_429 = 429;
7272
const STATUS_CODE_431 = 431;
73+
const STATUS_CODE_451 = 451;
7374
const STATUS_CODE_500 = 500;
7475
const STATUS_CODE_501 = 501;
7576
const STATUS_CODE_502 = 502;
@@ -137,6 +138,7 @@ class Response extends AbstractMessage implements ResponseInterface
137138
428 => 'Precondition Required',
138139
429 => 'Too Many Requests',
139140
431 => 'Request Header Fields Too Large',
141+
451 => 'Unavailable For Legal Reasons',
140142
// SERVER ERROR
141143
500 => 'Internal Server Error',
142144
501 => 'Not Implemented',
@@ -399,6 +401,16 @@ public function isNotFound()
399401
return (404 === $this->getStatusCode());
400402
}
401403

404+
/**
405+
* Does the status code indicate the resource is gone?
406+
*
407+
* @return bool
408+
*/
409+
public function isGone()
410+
{
411+
return (410 === $this->getStatusCode());
412+
}
413+
402414
/**
403415
* Do we have a normal, OK response?
404416
*

test/Client/CommonHttpTests.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Zend\Http\Request;
1717
use Zend\Http\Response;
1818
use Zend\Stdlib\Parameters;
19+
use Exception;
1920

2021
/**
2122
* This Testsuite includes all Zend_Http_Client that require a working web
@@ -1158,4 +1159,77 @@ public static function invalidConfigProvider()
11581159
[55]
11591160
];
11601161
}
1162+
1163+
/**
1164+
* Get an URI that does not accept HTTP connections.
1165+
*
1166+
* @return string
1167+
*/
1168+
protected function getNotRespondingUri()
1169+
{
1170+
$notRespondingUri = getenv('TESTS_ZEND_HTTP_CLIENT_NOTRESPONDINGURI');
1171+
if (! $notRespondingUri) {
1172+
$notRespondingUri = 'http://192.168.10.250:65530/';
1173+
}
1174+
1175+
return $notRespondingUri;
1176+
}
1177+
1178+
1179+
/**
1180+
* Check connecttimeout/timeout: invalid URIs should timeout after 'connecttimeout' seconds.
1181+
*/
1182+
public function testConnectTimeout1()
1183+
{
1184+
$connectTimeout = 1;
1185+
$executeTimeout = 5;
1186+
$this->client
1187+
->setUri($this->getNotRespondingUri())
1188+
->setMethod('GET')
1189+
->setOptions([
1190+
'connecttimeout' => $connectTimeout,
1191+
'timeout' => $executeTimeout,
1192+
]);
1193+
$timeoutException = null;
1194+
$startTime = microtime(true);
1195+
try {
1196+
$this->client->send();
1197+
} catch (Exception $x) {
1198+
$timeoutException = $x;
1199+
}
1200+
$endTime = microtime(true);
1201+
if ($timeoutException === null) {
1202+
$this->markTestSkipped("There's something responding at ".$this->getNotRespondingUri());
1203+
}
1204+
$deltaTime = ceil($endTime - $startTime);
1205+
$this->assertGreaterThanOrEqual($connectTimeout, $deltaTime);
1206+
$this->assertLessThan($executeTimeout, $deltaTime);
1207+
}
1208+
1209+
/**
1210+
* Check connecttimeout/timeout: valid but slow URIs should timeout after 'timeout' seconds.
1211+
*/
1212+
public function testConnectTimeout2()
1213+
{
1214+
$connectTimeout = 1;
1215+
$executeTimeout = 2;
1216+
$this->client
1217+
->setUri($this->baseuri . 'testConnectTimeout.php')
1218+
->setMethod('GET')
1219+
->setOptions([
1220+
'connecttimeout' => $connectTimeout,
1221+
'timeout' => $executeTimeout,
1222+
]);
1223+
$timeoutException = null;
1224+
$startTime = microtime(true);
1225+
try {
1226+
$this->client->send();
1227+
} catch (Exception $x) {
1228+
$timeoutException = $x;
1229+
}
1230+
$endTime = microtime(true);
1231+
$this->assertNotNull($timeoutException, 'The request should timeout');
1232+
$deltaTime = ceil($endTime - $startTime);
1233+
$this->assertGreaterThanOrEqual($executeTimeout, $deltaTime);
1234+
}
11611235
}

test/Client/CurlTest.php

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
use Zend\Config\Config;
1313
use Zend\Http\Client\Adapter;
14+
use Zend\Http\Client\Adapter\Exception\TimeoutException;
1415

1516
/**
1617
* This Testsuite includes all Zend_Http_Client that require a working web
@@ -434,7 +435,40 @@ public function testNoCaseSensitiveHeaderName()
434435
$this->assertFalse($headers->has('Content-Encoding'));
435436
}
436437

437-
public function testTimeoutDetection()
438+
public function testSslCaPathAndFileConfig()
439+
{
440+
$adapter = new Adapter\Curl();
441+
$options = [
442+
'sslcapath' => __DIR__ . DIRECTORY_SEPARATOR . '/_files',
443+
'sslcafile' => 'ca-bundle.crt'
444+
];
445+
$adapter->setOptions($options);
446+
447+
$config = $this->readAttribute($adapter, 'config');
448+
$this->assertEquals($options['sslcapath'], $config['sslcapath']);
449+
$this->assertEquals($options['sslcafile'], $config['sslcafile']);
450+
}
451+
452+
public function testTimeout()
453+
{
454+
$this->client
455+
->setUri($this->baseuri . 'testTimeout.php')
456+
->setOptions([
457+
'timeout' => 1,
458+
]);
459+
$adapter = new Adapter\Curl();
460+
$this->client->setAdapter($adapter);
461+
$this->client->setMethod('GET');
462+
$timeoutException = null;
463+
try {
464+
$this->client->send();
465+
} catch (TimeoutException $x) {
466+
$timeoutException = $x;
467+
}
468+
$this->assertNotNull($timeoutException);
469+
}
470+
471+
public function testTimeoutWithStream()
438472
{
439473
$this->client
440474
->setOptions([

0 commit comments

Comments
 (0)