Skip to content

Commit 11fa8af

Browse files
authored
Merge pull request #49 from php-http/options-handling
use options resolver to handle curl options and report problems
2 parents 9f7923e + 01f9c98 commit 11fa8af

File tree

4 files changed

+39
-14
lines changed

4 files changed

+39
-14
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Unreleased
44

5+
- Allow cURL options to overwrite our default spec-compliant default configuration
56

67
## 1.7.1 - 2018-03-36
78

composer.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
"php-http/httplug": "^2.0",
2222
"php-http/message-factory": "^1.0.2",
2323
"php-http/message": "^1.2",
24-
"php-http/discovery": "^1.0"
24+
"php-http/discovery": "^1.0",
25+
"symfony/options-resolver": "^3.4 || ^4.0"
2526
},
2627
"require-dev": {
2728
"guzzlehttp/psr7": "^1.0",

src/Client.php

+21-13
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Http\Promise\Promise;
1313
use Psr\Http\Message\RequestInterface;
1414
use Psr\Http\Message\ResponseInterface;
15+
use Symfony\Component\OptionsResolver\OptionsResolver;
1516

1617
/**
1718
* PSR-7 compatible cURL based HTTP client.
@@ -62,8 +63,6 @@ class Client implements HttpClient, HttpAsyncClient
6263
private $multiRunner = null;
6364

6465
/**
65-
* Create new client.
66-
*
6766
* @param MessageFactory|null $messageFactory HTTP Message factory
6867
* @param StreamFactory|null $streamFactory HTTP Stream factory
6968
* @param array $options cURL options {@link http://php.net/curl_setopt}
@@ -79,7 +78,20 @@ public function __construct(
7978
) {
8079
$this->messageFactory = $messageFactory ?: MessageFactoryDiscovery::find();
8180
$this->streamFactory = $streamFactory ?: StreamFactoryDiscovery::find();
82-
$this->options = $options;
81+
$resolver = new OptionsResolver();
82+
$resolver->setDefaults([
83+
CURLOPT_HEADER => false,
84+
CURLOPT_RETURNTRANSFER => false,
85+
CURLOPT_FOLLOWLOCATION => false,
86+
]);
87+
$resolver->setAllowedValues(CURLOPT_HEADER, [false]); // our parsing will fail if this is set to true
88+
$resolver->setAllowedValues(CURLOPT_RETURNTRANSFER, [false]); // our parsing will fail if this is set to true
89+
90+
// We do not know what everything curl supports and might support in the future.
91+
// Make sure that we accept everything that is in the options.
92+
$resolver->setDefined(array_keys($options));
93+
94+
$this->options = $resolver->resolve($options);
8395
}
8496

8597
/**
@@ -111,15 +123,15 @@ public function __destruct()
111123
public function sendRequest(RequestInterface $request): ResponseInterface
112124
{
113125
$responseBuilder = $this->createResponseBuilder();
114-
$options = $this->createCurlOptions($request, $responseBuilder);
126+
$requestOptions = $this->prepareRequestOptions($request, $responseBuilder);
115127

116128
if (is_resource($this->handle)) {
117129
curl_reset($this->handle);
118130
} else {
119131
$this->handle = curl_init();
120132
}
121133

122-
curl_setopt_array($this->handle, $options);
134+
curl_setopt_array($this->handle, $requestOptions);
123135
curl_exec($this->handle);
124136

125137
$errno = curl_errno($this->handle);
@@ -165,8 +177,8 @@ public function sendAsyncRequest(RequestInterface $request)
165177

166178
$handle = curl_init();
167179
$responseBuilder = $this->createResponseBuilder();
168-
$options = $this->createCurlOptions($request, $responseBuilder);
169-
curl_setopt_array($handle, $options);
180+
$requestOptions = $this->prepareRequestOptions($request, $responseBuilder);
181+
curl_setopt_array($handle, $requestOptions);
170182

171183
$core = new PromiseCore($request, $handle, $responseBuilder);
172184
$promise = new CurlPromise($core, $this->multiRunner);
@@ -176,7 +188,7 @@ public function sendAsyncRequest(RequestInterface $request)
176188
}
177189

178190
/**
179-
* Generates cURL options.
191+
* Update cURL options for this request and hook in the response builder.
180192
*
181193
* @param RequestInterface $request
182194
* @param ResponseBuilder $responseBuilder
@@ -187,14 +199,10 @@ public function sendAsyncRequest(RequestInterface $request)
187199
*
188200
* @return array
189201
*/
190-
private function createCurlOptions(RequestInterface $request, ResponseBuilder $responseBuilder)
202+
private function prepareRequestOptions(RequestInterface $request, ResponseBuilder $responseBuilder)
191203
{
192204
$options = $this->options;
193205

194-
$options[CURLOPT_HEADER] = false;
195-
$options[CURLOPT_RETURNTRANSFER] = false;
196-
$options[CURLOPT_FOLLOWLOCATION] = false;
197-
198206
try {
199207
$options[CURLOPT_HTTP_VERSION]
200208
= $this->getProtocolVersion($request->getProtocolVersion());

tests/ClientTest.php

+15
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
namespace Http\Client\Curl\Tests;
66

77
use Http\Client\Curl\Client;
8+
use Http\Message\MessageFactory;
9+
use Http\Message\StreamFactory;
810
use PHPUnit\Framework\TestCase;
11+
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
912
use Zend\Diactoros\Request;
1013

1114
/**
@@ -73,6 +76,18 @@ public function testRewindLargeStream()
7376
static::assertTrue(false !== strstr($options[CURLOPT_READFUNCTION](null, null, $length), 'abcdef'), 'Steam was not rewinded');
7477
}
7578

79+
public function testInvalidCurlOptions()
80+
{
81+
$this->expectException(InvalidOptionsException::class);
82+
new Client(
83+
$this->createMock(MessageFactory::class),
84+
$this->createMock(StreamFactory::class),
85+
[
86+
CURLOPT_HEADER => true, // this won't work with our client
87+
]
88+
);
89+
}
90+
7691
/**
7792
* Discovery should be used if no factory given.
7893
*/

0 commit comments

Comments
 (0)