diff --git a/README.md b/README.md index 3764938..703e9dd 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,8 @@ point buffered data will be sent and all further data will be ignored. The `Request#close()` method can be used to forefully close sending the request. Unlike the `end()` method, this method discards any buffers and closes the -underlying connection. +underlying connection if it is already established or cancels the pending +connection attempt otherwise. Request implements WritableStreamInterface, so a Stream can be piped to it. Interesting events emitted by Request: diff --git a/src/Request.php b/src/Request.php index 8f49f63..45b3820 100644 --- a/src/Request.php +++ b/src/Request.php @@ -54,35 +54,38 @@ private function writeHead() $stateRef = &$this->state; $pendingWrites = &$this->pendingWrites; - $this - ->connect() - ->done( - function (ConnectionInterface $stream) use ($requestData, &$streamRef, &$stateRef, &$pendingWrites) { - $streamRef = $stream; + $promise = $this->connect(); + $promise->done( + function (ConnectionInterface $stream) use ($requestData, &$streamRef, &$stateRef, &$pendingWrites) { + $streamRef = $stream; - $stream->on('drain', array($this, 'handleDrain')); - $stream->on('data', array($this, 'handleData')); - $stream->on('end', array($this, 'handleEnd')); - $stream->on('error', array($this, 'handleError')); - $stream->on('close', array($this, 'handleClose')); + $stream->on('drain', array($this, 'handleDrain')); + $stream->on('data', array($this, 'handleData')); + $stream->on('end', array($this, 'handleEnd')); + $stream->on('error', array($this, 'handleError')); + $stream->on('close', array($this, 'handleClose')); - $headers = (string) $requestData; + $headers = (string) $requestData; - $more = $stream->write($headers . $pendingWrites); + $more = $stream->write($headers . $pendingWrites); - $stateRef = Request::STATE_HEAD_WRITTEN; + $stateRef = Request::STATE_HEAD_WRITTEN; - // clear pending writes if non-empty - if ($pendingWrites !== '') { - $pendingWrites = ''; + // clear pending writes if non-empty + if ($pendingWrites !== '') { + $pendingWrites = ''; - if ($more) { - $this->emit('drain'); - } + if ($more) { + $this->emit('drain'); } - }, - array($this, 'handleError') - ); + } + }, + array($this, 'handleError') + ); + + $this->on('close', function() use ($promise) { + $promise->cancel(); + }); } public function write($data) diff --git a/tests/FunctionalIntegrationTest.php b/tests/FunctionalIntegrationTest.php new file mode 100644 index 0000000..764e427 --- /dev/null +++ b/tests/FunctionalIntegrationTest.php @@ -0,0 +1,42 @@ +request('GET', 'http://www.google.com/'); + + $once = $this->expectCallableOnce(); + $request->on('response', function (Response $response) use ($once) { + $response->on('end', $once); + }); + + $request->end(); + + $loop->run(); + } + + public function testCancelPendingConnectionEmitsClose() + { + $loop = Factory::create(); + $client = new Client($loop); + + $request = $client->request('GET', 'http://www.google.com/'); + $request->on('error', $this->expectCallableNever()); + $request->on('close', $this->expectCallableOnce()); + $request->end(); + $request->close(); + + $loop->run(); + } +} diff --git a/tests/RequestTest.php b/tests/RequestTest.php index f693651..f991b37 100644 --- a/tests/RequestTest.php +++ b/tests/RequestTest.php @@ -516,6 +516,32 @@ public function endAfterCloseIsNoOp() $request->end(); } + /** + * @test + */ + public function closeShouldCancelPendingConnectionAttempt() + { + $requestData = new RequestData('POST', 'http://www.example.com'); + $request = new Request($this->connector, $requestData); + + $promise = new Promise(function () {}, function () { + throw new \RuntimeException(); + }); + + $this->connector->expects($this->once()) + ->method('connect') + ->with('www.example.com:80') + ->willReturn($promise); + + $request->end(); + + $request->on('error', $this->expectCallableNever()); + $request->on('close', $this->expectCallableOnce()); + + $request->close(); + $request->close(); + } + /** @test */ public function requestShouldRelayErrorEventsFromResponse() {