Skip to content

Commit 7891316

Browse files
committed
Update documentation for RequestInterface
1 parent 7ec68c5 commit 7891316

File tree

2 files changed

+101
-81
lines changed

2 files changed

+101
-81
lines changed

README.md

Lines changed: 99 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,6 @@ Event-driven, streaming plaintext HTTP and secure HTTPS server for [ReactPHP](ht
1010
* [Usage](#usage)
1111
* [Server](#server)
1212
* [Request](#request)
13-
* [getMethod()](#getmethod)
14-
* [getQueryParams()](#getqueryparams)
15-
* [getProtocolVersion()](#getprotocolversion)
16-
* [getHeaders()](#getheaders)
17-
* [getHeader()](#getheader)
18-
* [getHeaderLine()](#getheaderline)
19-
* [hasHeader()](#hasheader)
2013
* [Response](#response)
2114
* [writeHead()](#writehead)
2215
* [Install](#install)
@@ -31,7 +24,7 @@ This is an HTTP server which responds with `Hello World` to every request.
3124
$loop = React\EventLoop\Factory::create();
3225
$socket = new React\Socket\Server(8080, $loop);
3326

34-
$http = new Server($socket, function (Request $request, Response $response) {
27+
$http = new Server($socket, function (RequestInterface $request, Response $response) {
3528
$response->writeHead(200, array('Content-Type' => 'text/plain'));
3629
$response->end("Hello World!\n");
3730
});
@@ -53,13 +46,13 @@ emits underlying streaming connections in order to then parse incoming data
5346
as HTTP.
5447

5548
For each request, it executes the callback function passed to the
56-
constructor with the respective [`Request`](#request) and
57-
[`Response`](#response) objects:
49+
constructor with the respective [request](#request) and
50+
[response](#response) objects:
5851

5952
```php
6053
$socket = new React\Socket\Server(8080, $loop);
6154

62-
$http = new Server($socket, function (Request $request, Response $response) {
55+
$http = new Server($socket, function (RequestInterface $request, Response $response) {
6356
$response->writeHead(200, array('Content-Type' => 'text/plain'));
6457
$response->end("Hello World!\n");
6558
});
@@ -75,7 +68,7 @@ $socket = new React\Socket\SecureServer($socket, $loop, array(
7568
'local_cert' => __DIR__ . '/localhost.pem'
7669
));
7770

78-
$http = new Server($socket, function (Request $request, Response $response) {
71+
$http = new Server($socket, function (RequestInterface $request, Response $response) {
7972
$response->writeHead(200, array('Content-Type' => 'text/plain'));
8073
$response->end("Hello World!\n");
8174
});
@@ -90,8 +83,8 @@ This ensures you will receive the request body without a delay as expected.
9083
The [Response](#response) still needs to be created as described in the
9184
examples above.
9285

93-
See also [`Request`](#request) and [`Response`](#response)
94-
for more details(e.g. the request data body).
86+
See also [request](#request) and [response](#response)
87+
for more details (e.g. the request data body).
9588

9689
The `Server` supports both HTTP/1.1 and HTTP/1.0 request messages.
9790
If a client sends an invalid request message, uses an invalid HTTP protocol
@@ -105,98 +98,125 @@ $http->on('error', function (Exception $e) {
10598
});
10699
```
107100

108-
The request object can also emit an error. Checkout [Request](#request)
109-
for more details.
101+
Note that the request object can also emit an error.
102+
Check out [request](#request) for more details.
110103

111104
### Request
112105

113-
The `Request` class is responsible for streaming the incoming request body
114-
and contains meta data which was parsed from the request headers.
115-
If the request body is chunked-encoded, the data will be decoded and emitted on the data event.
116-
The `Transfer-Encoding` header will be removed.
106+
An seen above, the `Server` class is responsible for handling incoming
107+
connections and then processing each incoming HTTP request.
117108

118-
It implements the `ReadableStreamInterface`.
109+
The request object will be processed once the request headers have
110+
been received by the client.
111+
This request object implements the
112+
[PSR-7 RequestInterface](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message.md#32-psrhttpmessagerequestinterface)
113+
and will be passed to the callback function like this.
119114

120-
Listen on the `data` event and the `end` event of the [Request](#request)
121-
to evaluate the data of the request body:
115+
```php
116+
$http = new Server($socket, function (RequestInterface $request, Response $response) {
117+
$response->writeHead(200, array('Content-Type' => 'text/plain'));
118+
$response->write("The method of the request is: " . $request->getMethod());
119+
$response->end("The requested path is: " . $request->getUri()->getPath());
120+
});
121+
```
122+
123+
For more details about the request object, check out the documentation of
124+
[PSR-7 RequestInterface](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message.md#32-psrhttpmessagerequestinterface).
125+
126+
Note that the request object will be processed once the request headers have
127+
been received.
128+
This means that this happens irrespective of (i.e. *before*) receiving the
129+
(potentially much larger) request body.
130+
While this may be uncommon in the PHP ecosystem, this is actually a very powerful
131+
approach that gives you several advantages not otherwise possible:
132+
133+
* React to requests *before* receiving a large request body,
134+
such as rejecting an unauthenticated request or one that exceeds allowed
135+
message lengths (file uploads).
136+
* Start processing parts of the request body before the remainder of the request
137+
body arrives or if the sender is slowly streaming data.
138+
* Process a large request body without having to buffer anything in memory,
139+
such as accepting a huge file upload or possibly unlimited request body stream.
140+
141+
The `getBody()` method can be used to access the request body stream.
142+
This method returns a stream instance that implements both the
143+
[PSR-7 StreamInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessagestreaminterface)
144+
and the [ReactPHP ReadableStreamInterface](https://github.com/reactphp/stream#readablestreaminterface).
145+
However, most of the `PSR-7 StreamInterface` methods have been
146+
designed under the assumption of being in control of the request body.
147+
Given that this does not apply to this server, the following
148+
`PSR-7 StreamInterface` methods are not used and SHOULD NOT be called:
149+
`tell()`, `eof()`, `seek()`, `rewind()`, `write()` and `read()`.
150+
Instead, you should use the `ReactPHP ReadableStreamInterface` which
151+
gives you access to the incoming request body as the individual chunks arrive:
122152

123153
```php
124-
$http = new Server($socket, function (Request $request, Response $response) {
154+
$http = new Server($socket, function (RequestInterface $request, Response $response) {
125155
$contentLength = 0;
126-
$request->on('data', function ($data) use (&$contentLength) {
156+
$body = $request->getBody();
157+
$body->on('data', function ($data) use (&$contentLength) {
127158
$contentLength += strlen($data);
128159
});
129160

130-
$request->on('end', function () use ($response, &$contentLength){
161+
$body->on('end', function () use ($response, &$contentLength){
131162
$response->writeHead(200, array('Content-Type' => 'text/plain'));
132163
$response->end("The length of the submitted request body is: " . $contentLength);
133164
});
134165

135166
// an error occures e.g. on invalid chunked encoded data or an unexpected 'end' event
136-
$request->on('error', function (\Exception $exception) use ($response, &$contentLength) {
167+
$body->on('error', function (\Exception $exception) use ($response, &$contentLength) {
137168
$response->writeHead(400, array('Content-Type' => 'text/plain'));
138169
$response->end("An error occured while reading at length: " . $contentLength);
139170
});
140171
});
141172
```
142173

143-
An error will just `pause` the connection instead of closing it. A response message
144-
can still be sent.
145-
146-
A `close` event will be emitted after an `error` or `end` event.
147-
148-
The constructor is internal, you SHOULD NOT call this yourself.
149-
The `Server` is responsible for emitting `Request` and `Response` objects.
150-
151-
See the above usage example and the class outline for details.
152-
153-
#### getMethod()
154-
155-
The `getMethod(): string` method can be used to
156-
return the request method.
157-
158-
#### getPath()
159-
160-
The `getPath(): string` method can be used to
161-
return the request path.
174+
The above example simply counts the number of bytes received in the request body.
175+
This can be used as a skeleton for buffering or processing the request body.
162176

163-
#### getQueryParams()
177+
The `data` event will be emitted whenever new data is available on the request
178+
body stream.
179+
The server automatically takes care of decoding chunked transfer encoding
180+
and will only emit the actual payload as data.
181+
In this case, the `Transfer-Encoding` header will be removed.
164182

165-
The `getQueryParams(): array` method can be used to
166-
return an array with all query parameters ($_GET).
183+
The `end` event will be emitted when the request body stream terminates
184+
successfully, i.e. it was read until its expected end.
167185

168-
#### getProtocolVersion()
186+
The `error` event will be emitted in case the request stream contains invalid
187+
chunked data or the connection closes before the complete request stream has
188+
been received.
189+
The server will automatically `pause()` the connection instead of closing it.
190+
A response message can still be sent (unless the connection is already closed).
169191

170-
The `getProtocolVersion(): string` method can be used to
171-
return the HTTP protocol version (such as "1.0" or "1.1").
172-
173-
#### getHeaders()
174-
175-
The `getHeaders(): array` method can be used to
176-
return an array with ALL headers.
177-
178-
The keys represent the header name in the exact case in which they were
179-
originally specified. The values will be an array of strings for each
180-
value for the respective header name.
181-
182-
#### getHeader()
183-
184-
The `getHeader(string $name): string[]` method can be used to
185-
retrieve a message header value by the given case-insensitive name.
186-
187-
Returns a list of all values for this header name or an empty array if header was not found
188-
189-
#### getHeaderLine()
190-
191-
The `getHeaderLine(string $name): string` method can be used to
192-
retrieve a comma-separated string of the values for a single header.
193-
194-
Returns a comma-separated list of all values for this header name or an empty string if header was not found
195-
196-
#### hasHeader()
192+
A `close` event will be emitted after an `error` or `end` event.
197193

198-
The `hasHeader(string $name): bool` method can be used to
199-
check if a header exists by the given case-insensitive name.
194+
For more details about the request body stream, check out the documentation of
195+
[ReactPHP ReadableStreamInterface](https://github.com/reactphp/stream#readablestreaminterface).
196+
197+
The `getSize(): ?int` method can be used if you only want to know the request
198+
body size.
199+
This method returns the complete size of the request body as defined by the
200+
message boundaries.
201+
This value may be `0` if the request message does not contain a request body
202+
(such as a simple `GET` request).
203+
Note that this value may be `null` if the request body size is unknown in
204+
advance because the request message uses chunked transfer encoding.
205+
206+
```php
207+
$http = new Server($socket, function (RequestInterface $request, Response $response) {
208+
$size = $request->getBody()->getSize();
209+
if ($size === null) {
210+
$response->writeHead(411, array('Content-Type' => 'text/plain'));
211+
$response->write('The request does not contain an explicit length.');
212+
$response->write('This server does not support chunked transfer encoding.');
213+
$response->end();
214+
return;
215+
}
216+
$response->writeHead(200, array('Content-Type' => 'text/plain'));
217+
$response->end("Request body size: " . $size . " bytes\n");
218+
});
219+
```
200220

201221
### Response
202222

src/Server.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* ```php
2323
* $socket = new React\Socket\Server(8080, $loop);
2424
*
25-
* $http = new Server($socket, function (Request $request, Response $response) {
25+
* $http = new Server($socket, function (RequestInterface $request, Response $response) {
2626
* $response->writeHead(200, array('Content-Type' => 'text/plain'));
2727
* $response->end("Hello World!\n");
2828
* });
@@ -38,7 +38,7 @@
3838
* 'local_cert' => __DIR__ . '/localhost.pem'
3939
* ));
4040
*
41-
* $http = new Server($socket, function (Request $request, Response $response) {
41+
* $http = new Server($socket, function (RequestInterface $request, Response $response) {
4242
* $response->writeHead(200, array('Content-Type' => 'text/plain'));
4343
* $response->end("Hello World!\n");
4444
* });

0 commit comments

Comments
 (0)