diff --git a/README.md b/README.md index 55ebb8e7..271f5e87 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ multiple concurrent HTTP requests without blocking. * [json()](#json) * [plaintext()](#plaintext) * [xml()](#xml) + * [Request](#request-1) * [ServerRequest](#serverrequest) * [ResponseException](#responseexception) * [React\Http\Middleware](#reacthttpmiddleware) @@ -2628,6 +2629,24 @@ $response = React\Http\Message\Response::xml( )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST); ``` +#### Request + +The `React\Http\Message\Request` class can be used to +respresent an outgoing HTTP request message. + +This class implements the +[PSR-7 `RequestInterface`](https://www.php-fig.org/psr/psr-7/#32-psrhttpmessagerequestinterface) +which extends the +[PSR-7 `MessageInterface`](https://www.php-fig.org/psr/psr-7/#31-psrhttpmessagemessageinterface). + +This is mostly used internally to represent each outgoing HTTP request +message for the HTTP client implementation. Likewise, you can also use this +class with other HTTP client implementations and for tests. + +> Internally, this implementation builds on top of an existing outgoing + request message and only adds support for streaming. This base class is + considered an implementation detail that may change in the future. + #### ServerRequest The `React\Http\Message\ServerRequest` class can be used to diff --git a/src/Browser.php b/src/Browser.php index 16c98fb3..3e3458af 100644 --- a/src/Browser.php +++ b/src/Browser.php @@ -3,13 +3,12 @@ namespace React\Http; use Psr\Http\Message\ResponseInterface; -use RingCentral\Psr7\Request; use RingCentral\Psr7\Uri; use React\EventLoop\Loop; use React\EventLoop\LoopInterface; -use React\Http\Io\ReadableBodyStream; use React\Http\Io\Sender; use React\Http\Io\Transaction; +use React\Http\Message\Request; use React\Promise\PromiseInterface; use React\Socket\ConnectorInterface; use React\Stream\ReadableStreamInterface; @@ -838,10 +837,6 @@ private function requestMayBeStreaming($method, $url, array $headers = array(), $url = Uri::resolve($this->baseUrl, $url); } - if ($body instanceof ReadableStreamInterface) { - $body = new ReadableBodyStream($body); - } - foreach ($this->defaultHeaders as $key => $value) { $explicitHeaderExists = false; foreach (\array_keys($headers) as $headerKey) { diff --git a/src/Io/Transaction.php b/src/Io/Transaction.php index b64622a8..cbf8f3eb 100644 --- a/src/Io/Transaction.php +++ b/src/Io/Transaction.php @@ -5,14 +5,13 @@ use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\UriInterface; -use React\Http\Message\Response; -use RingCentral\Psr7\Request; -use RingCentral\Psr7\Uri; use React\EventLoop\LoopInterface; +use React\Http\Message\Response; use React\Http\Message\ResponseException; use React\Promise\Deferred; use React\Promise\PromiseInterface; use React\Stream\ReadableStreamInterface; +use RingCentral\Psr7\Uri; /** * @internal diff --git a/src/Message/Request.php b/src/Message/Request.php new file mode 100644 index 00000000..cf59641e --- /dev/null +++ b/src/Message/Request.php @@ -0,0 +1,58 @@ + Internally, this implementation builds on top of an existing outgoing + * request message and only adds support for streaming. This base class is + * considered an implementation detail that may change in the future. + * + * @see RequestInterface + */ +final class Request extends BaseRequest implements RequestInterface +{ + /** + * @param string $method HTTP method for the request. + * @param string|UriInterface $url URL for the request. + * @param array $headers Headers for the message. + * @param string|ReadableStreamInterface|StreamInterface $body Message body. + * @param string $version HTTP protocol version. + * @throws \InvalidArgumentException for an invalid URL or body + */ + public function __construct( + $method, + $url, + array $headers = array(), + $body = '', + $version = '1.1' + ) { + if (\is_string($body)) { + $body = new BufferedBody($body); + } elseif ($body instanceof ReadableStreamInterface && !$body instanceof StreamInterface) { + $body = new ReadableBodyStream($body); + } elseif (!$body instanceof StreamInterface) { + throw new \InvalidArgumentException('Invalid request body given'); + } + + parent::__construct($method, $url, $headers, $body, $version); + } +} diff --git a/src/Message/ServerRequest.php b/src/Message/ServerRequest.php index fdb3ec5e..25532cf4 100644 --- a/src/Message/ServerRequest.php +++ b/src/Message/ServerRequest.php @@ -8,7 +8,7 @@ use React\Http\Io\BufferedBody; use React\Http\Io\HttpBodyStream; use React\Stream\ReadableStreamInterface; -use RingCentral\Psr7\Request; +use RingCentral\Psr7\Request as BaseRequest; /** * Respresents an incoming server request message. @@ -30,7 +30,7 @@ * * @see ServerRequestInterface */ -final class ServerRequest extends Request implements ServerRequestInterface +final class ServerRequest extends BaseRequest implements ServerRequestInterface { private $attributes = array(); diff --git a/tests/FunctionalBrowserTest.php b/tests/FunctionalBrowserTest.php index 95092ac1..6def2ecc 100644 --- a/tests/FunctionalBrowserTest.php +++ b/tests/FunctionalBrowserTest.php @@ -7,16 +7,15 @@ use React\EventLoop\Loop; use React\Http\Browser; use React\Http\HttpServer; +use React\Http\Message\Response; use React\Http\Message\ResponseException; use React\Http\Middleware\StreamingRequestMiddleware; -use React\Http\Message\Response; use React\Promise\Promise; use React\Promise\Stream; use React\Socket\Connector; use React\Socket\SocketServer; use React\Stream\ReadableStreamInterface; use React\Stream\ThroughStream; -use RingCentral\Psr7\Request; class FunctionalBrowserTest extends TestCase { diff --git a/tests/Io/SenderTest.php b/tests/Io/SenderTest.php index 587ba0c2..91b87b30 100644 --- a/tests/Io/SenderTest.php +++ b/tests/Io/SenderTest.php @@ -6,10 +6,10 @@ use React\Http\Client\RequestData; use React\Http\Io\ReadableBodyStream; use React\Http\Io\Sender; +use React\Http\Message\Request; use React\Promise; use React\Stream\ThroughStream; use React\Tests\Http\TestCase; -use RingCentral\Psr7\Request; class SenderTest extends TestCase { diff --git a/tests/Io/TransactionTest.php b/tests/Io/TransactionTest.php index 05022009..e0d04e39 100644 --- a/tests/Io/TransactionTest.php +++ b/tests/Io/TransactionTest.php @@ -7,14 +7,14 @@ use Psr\Http\Message\ResponseInterface; use React\Http\Io\ReadableBodyStream; use React\Http\Io\Transaction; +use React\Http\Message\Request; +use React\Http\Message\Response; use React\Http\Message\ResponseException; use React\EventLoop\Loop; use React\Promise; use React\Promise\Deferred; use React\Stream\ThroughStream; use React\Tests\Http\TestCase; -use RingCentral\Psr7\Request; -use RingCentral\Psr7\Response; class TransactionTest extends TestCase { diff --git a/tests/Message/RequestTest.php b/tests/Message/RequestTest.php new file mode 100644 index 00000000..29baf8a7 --- /dev/null +++ b/tests/Message/RequestTest.php @@ -0,0 +1,63 @@ +getBody(); + $this->assertSame(3, $body->getSize()); + $this->assertEquals('foo', (string) $body); + } + + public function testConstructWithStreamingRequestBodyReturnsBodyWhichImplementsReadableStreamInterfaceWithUnknownSize() + { + $request = new Request( + 'GET', + 'http://localhost', + array(), + new ThroughStream() + ); + + $body = $request->getBody(); + $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $body); + $this->assertInstanceOf('React\Stream\ReadableStreamInterface', $body); + $this->assertNull($body->getSize()); + } + + public function testConstructWithHttpBodyStreamReturnsBodyAsIs() + { + $request = new Request( + 'GET', + 'http://localhost', + array(), + $body = new HttpBodyStream(new ThroughStream(), 100) + ); + + $this->assertSame($body, $request->getBody()); + } + + public function testConstructWithNullBodyThrows() + { + $this->setExpectedException('InvalidArgumentException', 'Invalid request body given'); + new Request( + 'GET', + 'http://localhost', + array(), + null + ); + } +}