@@ -10,13 +10,15 @@ Event-driven, streaming plaintext HTTP and secure HTTPS server for [ReactPHP](ht
10
10
* [ Usage] ( #usage )
11
11
* [ Server] ( #server )
12
12
* [ StreamingServer] ( #streamingserver )
13
+ * [ listen()] ( #listen )
13
14
* [ Request] ( #request )
14
15
* [ Request parameters] ( #request-parameters )
15
16
* [ Query parameters] ( #query-parameters )
16
17
* [ Request body] ( #request-body )
17
18
* [ Streaming request] ( #streaming-request )
18
19
* [ Request method] ( #request-method )
19
20
* [ Cookie parameters] ( #cookie-parameters )
21
+ * [ Invalid request] ( #invalid-request )
20
22
* [ Response] ( #response )
21
23
* [ Deferred response] ( #deferred-response )
22
24
* [ Streaming response] ( #streaming-response )
@@ -65,11 +67,10 @@ The `Server` class is responsible for handling incoming connections and then
65
67
processing each incoming HTTP request.
66
68
67
69
It buffers and parses the complete incoming HTTP request in memory. Once the
68
- complete request has been received, it will invoke the request handler.
69
-
70
- For each request, it executes the callback function passed to the
71
- constructor with the respective [ request] ( #request ) object and expects
72
- a respective [ response] ( #response ) object in return.
70
+ complete request has been received, it will invoke the request handler function.
71
+ This request handler function needs to be passed to the constructor and will be
72
+ invoked with the respective [ request] ( #request ) object and expects a
73
+ [ response] ( #response ) object in return:
73
74
74
75
``` php
75
76
$server = new Server(function (ServerRequestInterface $request) {
@@ -83,18 +84,79 @@ $server = new Server(function (ServerRequestInterface $request) {
83
84
});
84
85
```
85
86
86
- For most users a server that buffers and parses a requests before handling it over as a
87
- PSR-7 request is what they want. The ` Server ` facade takes care of that, and takes the more
88
- advanced configuration out of hand. Under the hood it uses [ StreamingServer] ( #streamingserver )
89
- with the the three stock middleware using default settings from ` php.ini ` .
87
+ Each incoming HTTP request message is always represented by the
88
+ [ PSR-7 ` ServerRequestInterface ` ] ( https://www.php-fig.org/psr/psr-7/#321-psrhttpmessageserverrequestinterface ) ,
89
+ see also following [ request] ( #request ) chapter for more details.
90
+ Each outgoing HTTP response message is always represented by the
91
+ [ PSR-7 ` ResponseInterface ` ] ( https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface ) ,
92
+ see also following [ response] ( #response ) chapter for more details.
93
+
94
+ In order to process any connections, the server needs to be attached to an
95
+ instance of ` React\Socket\ServerInterface ` through the [ ` listen() ` ] ( #listen ) method
96
+ as described in the following chapter. In its most simple form, you can attach
97
+ this to a [ ` React\Socket\Server ` ] ( https://github.com/reactphp/socket#server )
98
+ in order to start a plaintext HTTP server like this:
99
+
100
+ ``` php
101
+ $server = new Server($handler);
102
+
103
+ $socket = new React\Socket\Server('0.0.0.0:8080', $loop);
104
+ $server->listen($socket);
105
+ ```
90
106
91
- The [ LimitConcurrentRequestsMiddleware] ( #limitconcurrentrequestsmiddleware ) requires a limit,
92
- as such the ` Server ` facade uses the ` memory_limit ` and ` post_max_size ` ini settings to
93
- calculate a sensible limit. It assumes a maximum of a quarter of the ` memory_limit ` for
94
- buffering and the other three quarter for parsing and handling the requests. The limit is
95
- division of half of ` memory_limit ` by ` memory_limit ` rounded up.
107
+ See also the [ ` listen() ` ] ( #listen ) method and the [ first example] ( examples ) for more details.
96
108
97
- > Note that any errors emitted by the wrapped ` StreamingServer ` are forwarded by ` Server ` .
109
+ The ` Server ` class is built as a facade around the underlying
110
+ [ ` StreamingServer ` ] ( #streamingserver ) to provide sane defaults for 80% of the
111
+ use cases and is the recommended way to use this library unless you're sure
112
+ you know what you're doing.
113
+
114
+ Unlike the underlying [ ` StreamingServer ` ] ( #streamingserver ) , this class
115
+ buffers and parses the complete incoming HTTP request in memory. Once the
116
+ complete request has been received, it will invoke the request handler
117
+ function. This means the [ request] ( #request ) passed to your request handler
118
+ function will be fully compatible with PSR-7.
119
+
120
+ On the other hand, buffering complete HTTP requests in memory until they can
121
+ be processed by your request handler function means that this class has to
122
+ employ a number of limits to avoid consuming too much memory. In order to
123
+ take the more advanced configuration out your hand, it respects setting from
124
+ your [ ` php.ini ` ] ( https://www.php.net/manual/en/ini.core.php ) to apply its
125
+ default settings. This is a list of PHP settings this class respects with
126
+ their respective default values:
127
+
128
+ ```
129
+ memory_limit 128M
130
+ post_max_size 8M
131
+ enable_post_data_reading 1
132
+ max_input_nesting_level 64
133
+ max_input_vars 1000
134
+
135
+ file_uploads 1
136
+ upload_max_filesize 2M
137
+ max_file_uploads 20
138
+ ```
139
+
140
+ In particular, the ` post_max_size ` setting limits how much memory a single HTTP
141
+ request is allowed to consume while buffering its request body. On top of
142
+ this, this class will try to avoid consuming more than 1/4 of your
143
+ ` memory_limit ` for buffering multiple concurrent HTTP requests. As such, with
144
+ the above default settings of ` 128M ` max, it will try to consume no more than
145
+ ` 32M ` for buffering multiple concurrent HTTP requests. As a consequence, it
146
+ will limit the concurrency to 4 HTTP requests with the above defaults.
147
+
148
+ It is imperative that you assign reasonable values to your PHP ini settings.
149
+ It is usually recommended to either reduce the memory a single request is
150
+ allowed to take (set ` post_max_size 1M ` or less) or to increase the total memory
151
+ limit to allow for more concurrent requests (set ` memory_limit 512M ` or more).
152
+ Failure to do so means that this class may have to disable concurrency and
153
+ only handle one request at a time.
154
+
155
+ Internally, this class automatically assigns these limits to the
156
+ [ middleware] ( #middleware ) request handlers as described below. For more
157
+ advanced use cases, you may also use the advanced
158
+ [ ` StreamingServer ` ] ( #streamingserver ) and assign these middleware request
159
+ handlers yourself as described in the following chapters.
98
160
99
161
### StreamingServer
100
162
@@ -103,11 +165,11 @@ processing each incoming HTTP request.
103
165
104
166
Unlike the [ ` Server ` ] ( #server ) class, it does not buffer and parse the incoming
105
167
HTTP request body by default. This means that the request handler will be
106
- invoked with a streaming request body.
107
-
108
- For each request, it executes the callback function passed to the
109
- constructor with the respective [ request] ( #request ) object and expects
110
- a respective [ response ] ( #response ) object in return.
168
+ invoked with a streaming request body. Once the request headers have been
169
+ received, it will invoke the request handler function. This request handler
170
+ function needs to be passed to the constructor and will be invoked with the
171
+ respective [ request] ( #request ) object and expects a [ response ] ( #response )
172
+ object in return:
111
173
112
174
``` php
113
175
$server = new StreamingServer(function (ServerRequestInterface $request) {
@@ -121,82 +183,88 @@ $server = new StreamingServer(function (ServerRequestInterface $request) {
121
183
});
122
184
```
123
185
124
- In order to process any connections, the server needs to be attached to an
125
- instance of ` React\Socket\ServerInterface ` which emits underlying streaming
126
- connections in order to then parse incoming data as HTTP.
186
+ Each incoming HTTP request message is always represented by the
187
+ [ PSR-7 ` ServerRequestInterface ` ] ( https://www.php-fig.org/psr/psr-7/#321-psrhttpmessageserverrequestinterface ) ,
188
+ see also following [ request] ( #request ) chapter for more details.
189
+ Each outgoing HTTP response message is always represented by the
190
+ [ PSR-7 ` ResponseInterface ` ] ( https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface ) ,
191
+ see also following [ response] ( #response ) chapter for more details.
127
192
128
- You can attach this to a
129
- [ ` React\Socket\Server ` ] ( https://github.com/reactphp/socket#server )
193
+ In order to process any connections, the server needs to be attached to an
194
+ instance of ` React\Socket\ServerInterface ` through the [ ` listen() ` ] ( #listen ) method
195
+ as described in the following chapter. In its most simple form, you can attach
196
+ this to a [ ` React\Socket\Server ` ] ( https://github.com/reactphp/socket#server )
130
197
in order to start a plaintext HTTP server like this:
131
198
132
199
``` php
133
200
$server = new StreamingServer($handler);
134
201
135
- $socket = new React\Socket\Server(8080, $loop);
202
+ $socket = new React\Socket\Server('0.0.0.0: 8080' , $loop);
136
203
$server->listen($socket);
137
204
```
138
205
139
- See also the ` listen() ` method and the [ first example] ( examples ) for more details.
140
-
141
- Similarly, you can also attach this to a
142
- [ ` React\Socket\SecureServer ` ] ( https://github.com/reactphp/socket#secureserver )
143
- in order to start a secure HTTPS server like this:
206
+ See also the [ ` listen() ` ] ( #listen ) method and the [ first example] ( examples ) for more details.
207
+
208
+ The ` StreamingServer ` class is considered advanced usage and unless you know
209
+ what you're doing, you're recommended to use the [ ` Server ` ] ( #server ) class
210
+ instead. The ` StreamingServer ` class is specifically designed to help with
211
+ more advanced use cases where you want to have full control over consuming
212
+ the incoming HTTP request body and concurrency settings.
213
+
214
+ In particular, this class does not buffer and parse the incoming HTTP request
215
+ in memory. It will invoke the request handler function once the HTTP request
216
+ headers have been received, i.e. before receiving the potentially much larger
217
+ HTTP request body. This means the [ request] ( #request ) passed to your request
218
+ handler function may not be fully compatible with PSR-7. See also
219
+ [ streaming request] ( #streaming-request ) below for more details.
220
+
221
+ ### listen()
222
+
223
+ The ` listen(React\Socket\ServerInterface $socket): void ` method can be used to
224
+ start processing connections from the given socket server.
225
+ The given [ ` React\Socket\ServerInterface ` ] ( https://github.com/reactphp/socket#serverinterface )
226
+ is responsible for emitting the underlying streaming connections.
227
+ This HTTP server needs to be attached to it in order to process any connections
228
+ and pase incoming streaming data as incoming HTTP request messages.
229
+ In its most common form, you can attach this to a
230
+ [ ` React\Socket\Server ` ] ( https://github.com/reactphp/socket#server )
231
+ in order to start a plaintext HTTP server like this:
144
232
145
233
``` php
234
+ $server = new Server($handler);
235
+ // or
146
236
$server = new StreamingServer($handler);
147
237
148
- $socket = new React\Socket\Server(8080, $loop);
149
- $socket = new React\Socket\SecureServer($socket, $loop, array(
150
- 'local_cert' => __DIR__ . '/localhost.pem'
151
- ));
152
-
238
+ $socket = new React\Socket\Server('0.0.0.0:8080', $loop);
153
239
$server->listen($socket);
154
240
```
155
241
156
- See also [ example #11 ] ( examples ) for more details.
157
-
158
- When HTTP/1.1 clients want to send a bigger request body, they MAY send only
159
- the request headers with an additional ` Expect: 100-continue ` header and
160
- wait before sending the actual (large) message body.
161
- In this case the server will automatically send an intermediary
162
- ` HTTP/1.1 100 Continue ` response to the client.
163
- This ensures you will receive the request body without a delay as expected.
164
- The [ Response] ( #response ) still needs to be created as described in the
165
- examples above.
242
+ This example will start listening for HTTP requests on the alternative HTTP port
243
+ ` 8080 ` on all interfaces (publicly). As an alternative, it is very common to use
244
+ a reverse proxy and let this HTTP server listen on the localhost (loopback)
245
+ interface only by using the listen address ` 127.0.0.1:8080 ` instead. This way, you
246
+ host your application(s) on the default HTTP port ` 80 ` and only route specific
247
+ requests to this HTTP server.
166
248
167
- See also [ request] ( #request ) and [ response] ( #response )
168
- for more details (e.g. the request data body).
169
-
170
- The ` StreamingServer ` supports both HTTP/1.1 and HTTP/1.0 request messages.
171
- If a client sends an invalid request message, uses an invalid HTTP protocol
172
- version or sends an invalid ` Transfer-Encoding ` in the request header, it will
173
- emit an ` error ` event, send an HTTP error response to the client and
174
- close the connection:
249
+ Likewise, it's usually recommended to use a reverse proxy setup to accept
250
+ secure HTTPS requests on default HTTPS port ` 443 ` (TLS termination) and only
251
+ route plaintext requests to this HTTP server. As an alternative, you can also
252
+ accept secure HTTPS requests with this HTTP server by attaching this to a
253
+ [ ` React\Socket\Server ` ] ( https://github.com/reactphp/socket#server ) using a
254
+ secure TLS listen address, a certificate file and optional ` passphrase ` like this:
175
255
176
256
``` php
177
- $server->on('error', function (Exception $e) {
178
- echo 'Error: ' . $e->getMessage() . PHP_EOL;
179
- });
180
- ```
181
-
182
- The server will also emit an ` error ` event if you return an invalid
183
- type in the callback function or have a unhandled ` Exception ` or ` Throwable ` .
184
- If your callback function throws an ` Exception ` or ` Throwable ` ,
185
- the ` StreamingServer ` will emit a ` RuntimeException ` and add the thrown exception
186
- as previous:
257
+ $server = new Server($handler);
258
+ // or
259
+ $server = new StreamingServer($handler);
187
260
188
- ``` php
189
- $server->on('error', function (Exception $e) {
190
- echo 'Error: ' . $e->getMessage() . PHP_EOL;
191
- if ($e->getPrevious() !== null) {
192
- $previousException = $e->getPrevious();
193
- echo $previousException->getMessage() . PHP_EOL;
194
- }
195
- });
261
+ $socket = new React\Socket\Server('tls://0.0.0.0:8443', $loop, array(
262
+ 'local_cert' => __DIR__ . '/localhost.pem'
263
+ ));
264
+ $server->listen($socket);
196
265
```
197
266
198
- Note that the request object can also emit an error.
199
- Check out [ request] ( #request ) for more details.
267
+ See also [ example #11 ] ( examples ) for more details.
200
268
201
269
### Request
202
270
@@ -405,6 +473,14 @@ This method operates on the buffered request body, i.e. the request body size
405
473
is always known, even when the request does not specify a ` Content-Length ` request
406
474
header or when using ` Transfer-Encoding: chunked ` for HTTP/1.1 requests.
407
475
476
+ > Note: The ` Server ` automatically takes care of handling requests with the
477
+ additional ` Expect: 100-continue ` request header. When HTTP/1.1 clients want to
478
+ send a bigger request body, they MAY send only the request headers with an
479
+ additional ` Expect: 100-continue ` request header and wait before sending the actual
480
+ (large) message body. In this case the server will automatically send an
481
+ intermediary ` HTTP/1.1 100 Continue ` response to the client. This ensures you
482
+ will receive the request body without a delay as expected.
483
+
408
484
#### Streaming request
409
485
410
486
If you're using the advanced [ ` StreamingServer ` ] ( #streamingserver ) , the
@@ -536,6 +612,14 @@ $server = new StreamingServer(function (ServerRequestInterface $request) {
536
612
});
537
613
```
538
614
615
+ > Note: The ` StreamingServer ` automatically takes care of handling requests with the
616
+ additional ` Expect: 100-continue ` request header. When HTTP/1.1 clients want to
617
+ send a bigger request body, they MAY send only the request headers with an
618
+ additional ` Expect: 100-continue ` request header and wait before sending the actual
619
+ (large) message body. In this case the server will automatically send an
620
+ intermediary ` HTTP/1.1 100 Continue ` response to the client. This ensures you
621
+ will receive the streaming request body without a delay as expected.
622
+
539
623
#### Request method
540
624
541
625
Note that the server supports * any* request method (including custom and non-
@@ -608,6 +692,26 @@ This encoding is also used internally when decoding the name and value of cookie
608
692
609
693
See also [ example #5 ] ( examples ) for more details.
610
694
695
+ #### Invalid request
696
+
697
+ The ` Server ` and ` StreamingServer ` classes support both HTTP/1.1 and HTTP/1.0 request
698
+ messages. If a client sends an invalid request message, uses an invalid HTTP
699
+ protocol version or sends an invalid ` Transfer-Encoding ` request header value,
700
+ the server will automatically send a ` 400 ` (Bad Request) HTTP error response
701
+ to the client and close the connection.
702
+ On top of this, it will emit an ` error ` event that can be used for logging
703
+ purposes like this:
704
+
705
+ ``` php
706
+ $server->on('error', function (Exception $e) {
707
+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
708
+ });
709
+ ```
710
+
711
+ Note that the server will also emit an ` error ` event if you do not return a
712
+ valid response object from your request handler function. See also
713
+ [ invalid response] ( #invalid-response ) for more details.
714
+
611
715
### Response
612
716
613
717
The callback function passed to the constructor of the [ ` Server ` ] ( #server ) or
@@ -831,9 +935,37 @@ to the message if the same request would have used an (unconditional) `GET`.
831
935
832
936
#### Invalid response
833
937
834
- An invalid return value or an unhandled ` Exception ` or ` Throwable ` in the code
835
- of the callback function, will result in an ` 500 Internal Server Error ` message.
836
- Make sure to catch ` Exceptions ` or ` Throwables ` to create own response messages.
938
+ As stated above, each outgoing HTTP response is always represented by the
939
+ [ PSR-7 ` ResponseInterface ` ] ( https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface ) .
940
+ If your request handler function returns an invalid value or throws an
941
+ unhandled ` Exception ` or ` Throwable ` , the server will automatically send a ` 500 `
942
+ (Internal Server Error) HTTP error response to the client.
943
+ On top of this, it will emit an ` error ` event that can be used for logging
944
+ purposes like this:
945
+
946
+ ``` php
947
+ $server->on('error', function (Exception $e) {
948
+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
949
+ if ($e->getPrevious() !== null) {
950
+ echo 'Previous: ' . $e->getPrevious()->getMessage() . PHP_EOL;
951
+ }
952
+ });
953
+ ```
954
+
955
+ Note that the server will also emit an ` error ` event if the client sends an
956
+ invalid HTTP request that never reaches your request handler function. See
957
+ also [ invalid request] ( #invalid-request ) for more details.
958
+ Additionally, a [ streaming request] ( #streaming-request ) body can also emit
959
+ an ` error ` event on the request body.
960
+
961
+ The server will only send a very generic ` 500 ` (Interval Server Error) HTTP
962
+ error response without any further details to the client if an unhandled
963
+ error occurs. While we understand this might make initial debugging harder,
964
+ it also means that the server does not leak any application details or stack
965
+ traces to the outside by default. It is usually recommended to catch any
966
+ ` Exception ` or ` Throwable ` within your request handler function or alternatively
967
+ use a [ ` middleware ` ] ( #middleware ) to avoid this generic error handling and
968
+ create your own HTTP response message instead.
837
969
838
970
#### Default response headers
839
971
0 commit comments