@@ -11,7 +11,6 @@ Event-driven, streaming plaintext HTTP and secure HTTPS server for [ReactPHP](ht
1111 * [ Server] ( #server )
1212 * [ Request] ( #request )
1313 * [ Response] ( #response )
14- * [ writeHead()] ( #writehead )
1514* [ Install] ( #install )
1615* [ Tests] ( #tests )
1716* [ License] ( #license )
@@ -24,9 +23,12 @@ This is an HTTP server which responds with `Hello World` to every request.
2423$loop = React\EventLoop\Factory::create();
2524$socket = new React\Socket\Server(8080, $loop);
2625
27- $http = new Server($socket, function (RequestInterface $request, Response $response) {
28- $response->writeHead(200, array('Content-Type' => 'text/plain'));
29- $response->end("Hello World!\n");
26+ $http = new Server($socket, function (RequestInterface $request) {
27+ return new Response(
28+ 200,
29+ array('Content-Type' => 'text/plain'),
30+ "Hello World!\n"
31+ );
3032});
3133
3234$loop->run();
@@ -52,9 +54,12 @@ constructor with the respective [request](#request) and
5254``` php
5355$socket = new React\Socket\Server(8080, $loop);
5456
55- $http = new Server($socket, function (RequestInterface $request, Response $response) {
56- $response->writeHead(200, array('Content-Type' => 'text/plain'));
57- $response->end("Hello World!\n");
57+ $http = new Server($socket, function (RequestInterface $request) {
58+ return new Response(
59+ 200,
60+ array('Content-Type' => 'text/plain'),
61+ "Hello World!\n"
62+ );
5863});
5964```
6065
@@ -70,9 +75,12 @@ $socket = new React\Socket\SecureServer($socket, $loop, array(
7075 'local_cert' => __DIR__ . '/localhost.pem'
7176));
7277
73- $http = new Server($socket, function (RequestInterface $request, Response $response) {
74- $response->writeHead(200, array('Content-Type' => 'text/plain'));
75- $response->end("Hello World!\n");
78+ $http = new Server($socket, function (RequestInterface $request) {
79+ return new Response(
80+ 200,
81+ array('Content-Type' => 'text/plain'),
82+ "Hello World!\n"
83+ );
7684});
7785```
7886
@@ -117,10 +125,15 @@ This request object implements the
117125and will be passed to the callback function like this.
118126
119127 ``` php
120- $http = new Server($socket, function (RequestInterface $request, Response $response) {
121- $response->writeHead(200, array('Content-Type' => 'text/plain'));
122- $response->write("The method of the request is: " . $request->getMethod());
123- $response->end("The requested path is: " . $request->getUri()->getPath());
128+ $http = new Server($socket, function (RequestInterface $request) {
129+ $body = "The method of the request is: " . $request->getMethod();
130+ $body .= "The requested path is: " . $request->getUri()->getPath();
131+
132+ return new Response(
133+ 200,
134+ array('Content-Type' => 'text/plain'),
135+ $body
136+ );
124137});
125138```
126139
@@ -155,22 +168,31 @@ Instead, you should use the `ReactPHP ReadableStreamInterface` which
155168gives you access to the incoming request body as the individual chunks arrive:
156169
157170``` php
158- $http = new Server($socket, function (RequestInterface $request, Response $response) {
159- $contentLength = 0;
160- $body = $request->getBody();
161- $body->on('data', function ($data) use (& $contentLength) {
162- $contentLength += strlen($data);
163- });
164-
165- $body->on('end', function () use ($response, & $contentLength){
166- $response->writeHead(200, array('Content-Type' => 'text/plain'));
167- $response->end("The length of the submitted request body is: " . $contentLength);
168- });
169-
170- // an error occures e.g. on invalid chunked encoded data or an unexpected 'end' event
171- $body->on('error', function (\Exception $exception) use ($response, & $contentLength) {
172- $response->writeHead(400, array('Content-Type' => 'text/plain'));
173- $response->end("An error occured while reading at length: " . $contentLength);
171+ $http = new Server($socket, function (RequestInterface $request) {
172+ return new Promise(function ($resolve, $reject) use ($request) {
173+ $contentLength = 0;
174+ $request->getBody()->on('data', function ($data) use (& $contentLength) {
175+ $contentLength += strlen($data);
176+ });
177+
178+ $request->getBody()->on('end', function () use ($resolve, & $contentLength){
179+ $response = new Response(
180+ 200,
181+ array('Content-Type' => 'text/plain'),
182+ "The length of the submitted request body is: " . $contentLength
183+ );
184+ $resolve($response);
185+ });
186+
187+ // an error occures e.g. on invalid chunked encoded data or an unexpected 'end' event
188+ $request->getBody()->on('error', function (\Exception $exception) use ($resolve, & $contentLength) {
189+ $response = new Response(
190+ 400,
191+ array('Content-Type' => 'text/plain'),
192+ "An error occured while reading at length: " . $contentLength
193+ );
194+ $resolve($response);
195+ });
174196 });
175197});
176198```
@@ -210,58 +232,114 @@ Note that this value may be `null` if the request body size is unknown in
210232advance because the request message uses chunked transfer encoding.
211233
212234``` php
213- $http = new Server($socket, function (RequestInterface $request, Response $response ) {
235+ $http = new Server($socket, function (RequestInterface $request) {
214236 $size = $request->getBody()->getSize();
215237 if ($size === null) {
216- $response->writeHead(411, array('Content-Type' => 'text/plain'));
217- $response->write('The request does not contain an explicit length.');
218- $response->write('This server does not accept chunked transfer encoding.');
219- $response->end();
220- return;
238+ $body = 'The request does not contain an explicit length.';
239+ $body .= 'This server does not accept chunked transfer encoding.';
240+
241+ return new Response(
242+ 411,
243+ array('Content-Type' => 'text/plain'),
244+ $body
245+ );
221246 }
222- $response->writeHead(200, array('Content-Type' => 'text/plain'));
223- $response->end("Request body size: " . $size . " bytes\n");
247+
248+ return new Response(
249+ 200,
250+ array('Content-Type' => 'text/plain'),
251+ "Request body size: " . $size . " bytes\n"
252+ );
224253});
225254```
226255
227256### Response
228257
229- The ` Response ` class is responsible for streaming the outgoing response body.
258+ The callback function passed to the constructor of the [ Server] ( #server )
259+ is responsible to return process a response, which will be delivered to the client.
260+ This function MUST return either a implementation of the
261+ [ PSR-7 ResponseInterface] ( https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message.md#33-psrhttpmessageresponseinterface )
262+ object or a
263+ [ ReactPHP Promise] ( https://github.com/reactphp/promise#reactpromise )
264+ which will resolve a ` PSR-7 ResponseInterface ` object.
230265
231- It implements the ` WritableStreamInterface ` .
232-
233- See also [ example #3 ] ( examples ) for more details.
266+ ``` php
267+ $http = new Server($socket, function (RequestInterface $request) {
268+ return new Response(
269+ 200,
270+ array('Content-Type' => 'text/plain'),
271+ "Hello World!\n"
272+ );
273+ });
274+ ```
234275
235- The constructor is internal, you SHOULD NOT call this yourself.
236- The ` Server ` is responsible for emitting ` Request ` and ` Response ` objects.
276+ You will find a ` Response ` class
277+ which implements the ` PSR-7 RequestInterface ` in this project.
278+ We use instantiation of this class in our projects,
279+ but feel free to use any implemantation of the
280+ ` PSR-7 RequestInterface ` you prefer.
237281
238- The ` Response ` will automatically use the same HTTP protocol version as the
239- corresponding ` Request ` .
282+ If your response takes time to be processed you SHOULD use a ` ReactPHP Promise ` .
240283
241- HTTP/1.1 responses will automatically apply chunked transfer encoding if
242- no ` Content-Length ` header has been set.
243- See [ ` writeHead() ` ] ( #writehead ) for more details.
284+ ``` php
285+ $server = new Server($socket, function (RequestInterface $request) {
286+ return new Promise(function ($resolve, $reject) use ($request) {
287+ $contentLength = 0;
288+ $body = $request->getBody();
289+
290+ $body->on('data', function ($data) use (& $contentLength) {
291+ $contentLength += strlen($data);
292+ });
293+
294+ $body->on('end', function () use ($resolve, & $contentLength){
295+ $resolve(
296+ new Response(
297+ 200,
298+ array('Content-Type' => 'text/plain'),
299+ "The length of the submitted request body is: " . $contentLength
300+ )
301+ );
302+ });
303+ });
304+ });
305+ ```
244306
245- See the above usage example and the class outline for details.
307+ The above example simply counts the number of bytes received in the request body.
308+ This needs to be a promise, because the request body may needs time to be completed.
309+ The ` ReactPHP Promise ` will resolve in a ` Response ` object when the request
310+ body ends.
311+ Always use the ` ReactPHP Promise ` when your
312+ Check out the documentation of [ ReactPHP Promise] ( https://github.com/reactphp/promise#reactpromise )
313+ for more information to the ` Response ` object.
314+
315+ This library is able to stream the response body data directly to the client,
316+ so you don't have to buffer the data or block your application.
317+ Add any stream that implements the
318+ [ ReactPHP ReadableStreamInterface] ( https://github.com/reactphp/stream#readablestreaminterface )
246319
247- #### writeHead()
320+ ``` php
321+ $server = new Server($socket, function (RequestInterface $request) use ($loop) {
322+ $stream = new ReadableStream();
248323
249- The ` writeHead(int $status = 200, array $headers = array(): void ` method can be used to
250- write the given HTTP message header.
324+ $timer = $loop->addPeriodicTimer(0.5, function () use ($stream) {
325+ $stream->emit('data', array(microtime(true) . PHP_EOL));
326+ });
251327
252- This method MUST be invoked once before calling ` write() ` or ` end() ` to send
253- the actual HTTP message body:
328+ $loop->addTimer(5, function() use ($loop, $timer, $stream) {
329+ $loop->cancelTimer($timer);
330+ $stream->emit('end');
331+ });
254332
255- ``` php
256- $response->writeHead(200, array(
257- 'Content-Type' => 'text/plain'
258- ));
259- $response->end('Hello World!');
333+ return new Response(200, array('Content-Type' => 'text/plain'), $stream);
334+ });
260335```
261336
262- Calling this method more than once will result in an ` Exception `
263- (unless the response has ended/closed already).
264- Calling this method after the response has ended/closed is a NOOP.
337+ The above example will emit every 0.5 seconds the current Unix timestamp
338+ with microseconds as float to the client and will end after 5 seconds.
339+ This is just a example you could use of the streaming,
340+ you could also send a big amount of data via little chunks
341+ or use it for body data that needs to calculated.
342+ Use the oppertunties of the ` ReactPHP Streams `
265343
266344Unless you specify a ` Content-Length ` header yourself, HTTP/1.1 responses
267345will automatically use chunked transfer encoding and send the respective header
@@ -270,49 +348,61 @@ will automatically use chunked transfer encoding and send the respective header
270348If you know the length of your body, you MAY specify it like this instead:
271349
272350``` php
273- $data = 'Hello World!';
274-
275- $response->writeHead(200, array(
276- 'Content-Type' => 'text/plain',
277- 'Content-Length' => strlen($data)
278- ));
279- $response->end($data);
351+ $server = new Server($socket, function (RequestInterface $request) use ($loop) {
352+ $data = "Hello world!\n";
353+
354+ return new Response(
355+ 200,
356+ array(
357+ 'Content-Type' => 'text/plain',
358+ 'Content-Length' => strlen($data)
359+ ),
360+ $data
361+ );
362+ });
280363```
281364
365+ An unhandled Exception in the code of the callback function will result
366+ in an ` 500 Internal Server Error ` message.
367+ Make sure to catch these ` Exceptions ` to create own response messages.
368+
369+ After the return in the callback function the response will be processed by the ` Server ` .
370+ The ` Server ` will add the protocol version of the reequest, so you don't have to.
371+
282372A ` Date ` header will be automatically added with the system date and time if none is given.
283373You can add a custom ` Date ` header yourself like this:
284374
285375``` php
286- $response->writeHead(200, array(
287- 'Date' => date('D, d M Y H:i:s T')
288- ) );
376+ $server = new Server($socket, function (RequestInterface $request) {
377+ return new \RingCentral\Psr7\Response(200, array( 'Date' => date('D, d M Y H:i:s T')));
378+ } );
289379```
290380
291381If you don't have a appropriate clock to rely on, you should
292- unset this header with an empty array :
382+ unset this header with an empty string :
293383
294384``` php
295- $response->writeHead(200, array(
296- 'Date' => array()
297- ) );
385+ $server = new Server($socket, function (RequestInterface $request) {
386+ return new Response(200, array( 'Date' => ''));
387+ } );
298388```
299389
300390Note that it will automatically assume a ` X-Powered-By: react/alpha ` header
301391unless your specify a custom ` X-Powered-By ` header yourself:
302392
303393``` php
304- $response->writeHead(200, array(
305- 'X-Powered-By' => 'PHP 3'
306- ) );
394+ $server = new Server($socket, function (RequestInterface $request) {
395+ return new Response(200, array( 'X-Powered-By' => 'PHP 3'));
396+ } );
307397```
308398
309399If you do not want to send this header at all, you can use an empty array as
310400value like this:
311401
312402``` php
313- $response->writeHead(200, array(
314- 'X-Powered-By' => array()
315- ) );
403+ $server = new Server($socket, function (RequestInterface $request) {
404+ return new Response(200, array( 'X-Powered-By' => ''));
405+ } );
316406```
317407
318408Note that persistent connections (` Connection: keep-alive ` ) are currently
0 commit comments