@@ -10,13 +10,6 @@ Event-driven, streaming plaintext HTTP and secure HTTPS server for [ReactPHP](ht
10
10
* [ Usage] ( #usage )
11
11
* [ Server] ( #server )
12
12
* [ Request] ( #request )
13
- * [ getMethod()] ( #getmethod )
14
- * [ getQueryParams()] ( #getqueryparams )
15
- * [ getProtocolVersion()] ( #getprotocolversion )
16
- * [ getHeaders()] ( #getheaders )
17
- * [ getHeader()] ( #getheader )
18
- * [ getHeaderLine()] ( #getheaderline )
19
- * [ hasHeader()] ( #hasheader )
20
13
* [ Response] ( #response )
21
14
* [ writeHead()] ( #writehead )
22
15
* [ Install] ( #install )
@@ -31,7 +24,7 @@ This is an HTTP server which responds with `Hello World` to every request.
31
24
$loop = React\EventLoop\Factory::create();
32
25
$socket = new React\Socket\Server(8080, $loop);
33
26
34
- $http = new Server($socket, function (Request $request, Response $response) {
27
+ $http = new Server($socket, function (RequestInterface $request, Response $response) {
35
28
$response->writeHead(200, array('Content-Type' => 'text/plain'));
36
29
$response->end("Hello World!\n");
37
30
});
@@ -53,13 +46,13 @@ emits underlying streaming connections in order to then parse incoming data
53
46
as HTTP.
54
47
55
48
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:
58
51
59
52
``` php
60
53
$socket = new React\Socket\Server(8080, $loop);
61
54
62
- $http = new Server($socket, function (Request $request, Response $response) {
55
+ $http = new Server($socket, function (RequestInterface $request, Response $response) {
63
56
$response->writeHead(200, array('Content-Type' => 'text/plain'));
64
57
$response->end("Hello World!\n");
65
58
});
@@ -75,7 +68,7 @@ $socket = new React\Socket\SecureServer($socket, $loop, array(
75
68
'local_cert' => __DIR__ . '/localhost.pem'
76
69
));
77
70
78
- $http = new Server($socket, function (Request $request, Response $response) {
71
+ $http = new Server($socket, function (RequestInterface $request, Response $response) {
79
72
$response->writeHead(200, array('Content-Type' => 'text/plain'));
80
73
$response->end("Hello World!\n");
81
74
});
@@ -90,8 +83,8 @@ This ensures you will receive the request body without a delay as expected.
90
83
The [ Response] ( #response ) still needs to be created as described in the
91
84
examples above.
92
85
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).
95
88
96
89
The ` Server ` supports both HTTP/1.1 and HTTP/1.0 request messages.
97
90
If a client sends an invalid request message, uses an invalid HTTP protocol
@@ -105,98 +98,125 @@ $http->on('error', function (Exception $e) {
105
98
});
106
99
```
107
100
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.
110
103
111
104
### Request
112
105
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.
117
108
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.
119
114
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:
122
152
123
153
``` php
124
- $http = new Server($socket, function (Request $request, Response $response) {
154
+ $http = new Server($socket, function (RequestInterface $request, Response $response) {
125
155
$contentLength = 0;
126
- $request->on('data', function ($data) use (& $contentLength) {
156
+ $body = $request->getBody();
157
+ $body->on('data', function ($data) use (& $contentLength) {
127
158
$contentLength += strlen($data);
128
159
});
129
160
130
- $request ->on('end', function () use ($response, & $contentLength){
161
+ $body ->on('end', function () use ($response, & $contentLength){
131
162
$response->writeHead(200, array('Content-Type' => 'text/plain'));
132
163
$response->end("The length of the submitted request body is: " . $contentLength);
133
164
});
134
165
135
166
// 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) {
137
168
$response->writeHead(400, array('Content-Type' => 'text/plain'));
138
169
$response->end("An error occured while reading at length: " . $contentLength);
139
170
});
140
171
});
141
172
```
142
173
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.
162
176
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.
164
182
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 .
167
185
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).
169
191
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.
197
193
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
+ ```
200
220
201
221
### Response
202
222
0 commit comments