diff --git a/docs/faq.rst b/docs/faq.rst index ee7949bf8..8d2f38b52 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -65,6 +65,23 @@ used with a client. ] ]); +If you use asynchronous requests with cURL multi handler and want to tweak it, +additional options can be specified as an associative array in the +**multi_handle_options** key of the ``CurlMultiHandler`` constructor. + +.. code-block:: php + + use \GuzzleHttp\Client; + use \GuzzleHttp\HandlerStack; + use \GuzzleHttp\Handler\CurlMultiHandler; + + $client = new Client(['handler' => HandlerStack::create(new CurlMultiHandler([ + 'options' => [ + CURLMOPT_MAX_TOTAL_CONNECTIONS => 50, + CURLMOPT_MAX_HOST_CONNECTIONS => 5, + ] + ]))]); + How can I add custom stream context options? ============================================ diff --git a/src/Handler/CurlMultiHandler.php b/src/Handler/CurlMultiHandler.php index d8297623c..b73e5c72d 100644 --- a/src/Handler/CurlMultiHandler.php +++ b/src/Handler/CurlMultiHandler.php @@ -1,9 +1,9 @@ selectTimeout = 1; } + + $this->options = isset($options['options']) ? $options['options'] : []; } public function __get($name) { if ($name === '_mh') { - return $this->_mh = curl_multi_init(); + $this->_mh = curl_multi_init(); + + foreach ($this->options as $option => $value) { + // A warning is raised in case of a wrong option. + curl_multi_setopt($this->_mh, $option, $value); + } + + // Further calls to _mh will return the value directly, without entering the + // __get() method at all. + return $this->_mh; } throw new \BadMethodCallException(); diff --git a/tests/Handler/CurlMultiHandlerTest.php b/tests/Handler/CurlMultiHandlerTest.php index 03043f5ff..6e0660cfb 100644 --- a/tests/Handler/CurlMultiHandlerTest.php +++ b/tests/Handler/CurlMultiHandlerTest.php @@ -9,6 +9,31 @@ class CurlMultiHandlerTest extends TestCase { + public function setUp() + { + $_SERVER['curl_test'] = true; + unset($_SERVER['_curl_multi']); + } + + public function tearDown() + { + unset($_SERVER['_curl_multi'], $_SERVER['curl_test']); + } + + public function testCanAddCustomCurlOptions() + { + Server::flush(); + Server::enqueue([new Response()]); + $a = new CurlMultiHandler(['options' => [ + CURLMOPT_PIPELINING => 1, + CURLMOPT_MAXCONNECTS => 5, + ]]); + $request = new Request('GET', Server::$url); + $a($request, []); + $this->assertEquals(1, $_SERVER['_curl_multi'][CURLMOPT_PIPELINING]); + $this->assertEquals(5, $_SERVER['_curl_multi'][CURLMOPT_MAXCONNECTS]); + } + public function testSendsRequest() { Server::enqueue([new Response()]); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 1cf17b6df..36fbd62e4 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -10,7 +10,7 @@ }); } -// Override curl_setopt_array() to get the last set curl options +// Override curl_setopt_array() and curl_multi_setopt() to get the last set curl options namespace GuzzleHttp\Handler { function curl_setopt_array($handle, array $options) { @@ -19,6 +19,16 @@ function curl_setopt_array($handle, array $options) } else { unset($_SERVER['_curl']); } - \curl_setopt_array($handle, $options); + return \curl_setopt_array($handle, $options); + } + + function curl_multi_setopt($handle, $option, $value) + { + if (!empty($_SERVER['curl_test'])) { + $_SERVER['_curl_multi'][$option] = $value; + } else { + unset($_SERVER['_curl_multi']); + } + return \curl_multi_setopt($handle, $option, $value); } }