Skip to content

Commit 6af7139

Browse files
committed
Consistent end() semantics
1 parent 2fd3728 commit 6af7139

3 files changed

Lines changed: 62 additions & 10 deletions

File tree

README.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,21 @@ Basic HTTP/1.0 client.
66

77
## Basic usage
88

9-
Requests are prepared using the ``Client#request()`` method. Body can be
10-
sent with ``Request#write()``. ``Request#end()`` finishes sending the request
11-
(or sends it at all if no body was written).
9+
Requests are prepared using the ``Client#request()`` method.
10+
11+
The `Request#write(string $data)` method can be used to
12+
write data to the request body.
13+
Data will be buffered until the underlying connection is established, at which
14+
point buffered data will be sent and all further data will be passed to the
15+
underlying connection immediately.
16+
17+
The `Request#end(?string $data = null)` method can be used to
18+
finish sending the request.
19+
You may optionally pass a last request body data chunk that will be sent just
20+
like a `write()` call.
21+
Calling this method finalizes the outgoing request body (which may be empty).
22+
Data will be buffered until the underlying connection is established, at which
23+
point buffered data will be sent and all further data will be ignored.
1224

1325
Request implements WritableStreamInterface, so a Stream can be piped to it.
1426
Interesting events emitted by Request:

src/Request.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class Request implements WritableStreamInterface
3131
private $responseFactory;
3232
private $response;
3333
private $state = self::STATE_INIT;
34+
private $ended = false;
3435

3536
private $pendingWrites = '';
3637

@@ -42,7 +43,7 @@ public function __construct(ConnectorInterface $connector, RequestData $requestD
4243

4344
public function isWritable()
4445
{
45-
return self::STATE_END > $this->state;
46+
return self::STATE_END > $this->state && !$this->ended;
4647
}
4748

4849
private function writeHead()
@@ -107,15 +108,17 @@ public function write($data)
107108

108109
public function end($data = null)
109110
{
110-
if (null !== $data && !is_scalar($data)) {
111-
throw new \InvalidArgumentException('$data must be null or scalar');
111+
if (!$this->isWritable()) {
112+
return;
112113
}
113114

114115
if (null !== $data) {
115116
$this->write($data);
116117
} else if (self::STATE_WRITING_HEAD > $this->state) {
117118
$this->writeHead();
118119
}
120+
121+
$this->ended = true;
119122
}
120123

121124
/** @internal */

tests/RequestTest.php

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use React\Stream\Stream;
88
use React\Promise\RejectedPromise;
99
use React\Promise\Deferred;
10+
use React\Promise\Promise;
1011

1112
class RequestTest extends TestCase
1213
{
@@ -442,15 +443,36 @@ public function pipeShouldPipeDataIntoTheRequestBody()
442443

443444
/**
444445
* @test
445-
* @expectedException InvalidArgumentException
446-
* @expectedExceptionMessage $data must be null or scalar
447446
*/
448-
public function endShouldOnlyAcceptScalars()
447+
public function writeShouldStartConnecting()
449448
{
450449
$requestData = new RequestData('POST', 'http://www.example.com');
451450
$request = new Request($this->connector, $requestData);
452451

453-
$request->end(array());
452+
$this->connector->expects($this->once())
453+
->method('connect')
454+
->with('www.example.com:80')
455+
->willReturn(new Promise(function () { }));
456+
457+
$request->write('test');
458+
}
459+
460+
/**
461+
* @test
462+
*/
463+
public function endShouldStartConnectingAndChangeStreamIntoNonWritableMode()
464+
{
465+
$requestData = new RequestData('POST', 'http://www.example.com');
466+
$request = new Request($this->connector, $requestData);
467+
468+
$this->connector->expects($this->once())
469+
->method('connect')
470+
->with('www.example.com:80')
471+
->willReturn(new Promise(function () { }));
472+
473+
$request->end();
474+
475+
$this->assertFalse($request->isWritable());
454476
}
455477

456478
/**
@@ -479,6 +501,21 @@ public function writeAfterCloseReturnsFalse()
479501
$this->assertFalse($request->write('nope'));
480502
}
481503

504+
/**
505+
* @test
506+
*/
507+
public function endAfterCloseIsNoOp()
508+
{
509+
$requestData = new RequestData('POST', 'http://www.example.com');
510+
$request = new Request($this->connector, $requestData);
511+
512+
$this->connector->expects($this->never())
513+
->method('connect');
514+
515+
$request->close();
516+
$request->end();
517+
}
518+
482519
/** @test */
483520
public function requestShouldRelayErrorEventsFromResponse()
484521
{

0 commit comments

Comments
 (0)