From 6ca832f446a8467b4c730101f5df2200291a64b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Wed, 7 Apr 2021 17:49:31 +0200 Subject: [PATCH 1/2] Add factory methods for common HTML/JSON/plaintext/XML response types --- README.md | 181 +++++++++++++++++++++++++++ src/Message/Response.php | 215 +++++++++++++++++++++++++++++++++ tests/Message/ResponseTest.php | 71 +++++++++++ 3 files changed, 467 insertions(+) diff --git a/README.md b/README.md index 50797fe6..e178ee72 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,10 @@ multiple concurrent HTTP requests without blocking. * [withResponseBuffer()](#withresponsebuffer) * [React\Http\Message](#reacthttpmessage) * [Response](#response) + * [html()](#html) + * [json()](#json) + * [plaintext()](#plaintext) + * [xml()](#xml) * [ServerRequest](#serverrequest) * [ResponseException](#responseexception) * [React\Http\Middleware](#reacthttpmiddleware) @@ -2463,6 +2467,183 @@ constants with the `STATUS_*` prefix. For instance, the `200 OK` and response message and only adds required streaming support. This base class is considered an implementation detail that may change in the future. +##### html() + +The static `html(string $html): Response` method can be used to +create an HTML response. + +```php +$html = << + +Hello wörld! + + +HTML; + +$response = React\Http\Message\Response::html($html); +``` + +This is a convenient shortcut method that returns the equivalent of this: + +``` +$response = new React\Http\Message\Response( + React\Http\Message\Response::STATUS_OK, + [ + 'Content-Type' => 'text/html; charset=utf-8' + ], + $html +); +``` + +This method always returns a response with a `200 OK` status code and +the appropriate `Content-Type` response header for the given HTTP source +string encoded in UTF-8 (Unicode). It's generally recommended to end the +given plaintext string with a trailing newline. + +If you want to use a different status code or custom HTTP response +headers, you can manipulate the returned response object using the +provided PSR-7 methods or directly instantiate a custom HTTP response +object using the `Response` constructor: + +```php +$response = React\Http\Message\Response::html( + "

Error

\n

Invalid user name given.

\n" +)->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST); +``` + +##### json() + +The static `json(mixed $data): Response` method can be used to +create a JSON response. + +```php +$response = React\Http\Message\Response::json(['name' => 'Alice']); +``` + +This is a convenient shortcut method that returns the equivalent of this: + +``` +$response = new React\Http\Message\Response( + React\Http\Message\Response::STATUS_OK, + [ + 'Content-Type' => 'application/json' + ], + json_encode( + ['name' => 'Alice'], + JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION + ) . "\n" +); +``` + +This method always returns a response with a `200 OK` status code and +the appropriate `Content-Type` response header for the given structured +data encoded as a JSON text. + +The given structured data will be encoded as a JSON text. Any `string` +values in the data must be encoded in UTF-8 (Unicode). If the encoding +fails, this method will throw an `InvalidArgumentException`. + +By default, the given structured data will be encoded with the flags as +shown above. This includes pretty printing (PHP 5.4+) and preserving +zero fractions for `float` values (PHP 5.6.6+) to ease debugging. It is +assumed any additional data overhead is usually compensated by using HTTP +response compression. + +If you want to use a different status code or custom HTTP response +headers, you can manipulate the returned response object using the +provided PSR-7 methods or directly instantiate a custom HTTP response +object using the `Response` constructor: + +```php +$response = React\Http\Message\Response::json( + ['error' => 'Invalid user name given'] +)->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST); +``` + +##### plaintext() + +The static `plaintext(string $text): Response` method can be used to +create a plaintext response. + +```php +$response = React\Http\Message\Response::plaintext("Hello wörld!\n"); +``` + +This is a convenient shortcut method that returns the equivalent of this: + +``` +$response = new React\Http\Message\Response( + React\Http\Message\Response::STATUS_OK, + [ + 'Content-Type' => 'text/plain; charset=utf-8' + ], + "Hello wörld!\n" +); +``` + +This method always returns a response with a `200 OK` status code and +the appropriate `Content-Type` response header for the given plaintext +string encoded in UTF-8 (Unicode). It's generally recommended to end the +given plaintext string with a trailing newline. + +If you want to use a different status code or custom HTTP response +headers, you can manipulate the returned response object using the +provided PSR-7 methods or directly instantiate a custom HTTP response +object using the `Response` constructor: + +```php +$response = React\Http\Message\Response::plaintext( + "Error: Invalid user name given.\n" +)->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST); +``` + +##### xml() + +The static `xml(string $xml): Response` method can be used to +create an XML response. + +```php +$xml = << + + Hello wörld! + + +XML; + +$response = React\Http\Message\Response::xml($xml); +``` + +This is a convenient shortcut method that returns the equivalent of this: + +``` +$response = new React\Http\Message\Response( + React\Http\Message\Response::STATUS_OK, + [ + 'Content-Type' => 'application/xml' + ], + $xml +); +``` + +This method always returns a response with a `200 OK` status code and +the appropriate `Content-Type` response header for the given XML source +string. It's generally recommended to use UTF-8 (Unicode) and specify +this as part of the leading XML declaration and to end the given XML +source string with a trailing newline. + +If you want to use a different status code or custom HTTP response +headers, you can manipulate the returned response object using the +provided PSR-7 methods or directly instantiate a custom HTTP response +object using the `Response` constructor: + +```php +$response = React\Http\Message\Response::xml( + "Invalid user name given.\n" +)->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST); +``` + #### ServerRequest The `React\Http\Message\ServerRequest` class can be used to diff --git a/src/Message/Response.php b/src/Message/Response.php index 0ce8bef0..edd6245b 100644 --- a/src/Message/Response.php +++ b/src/Message/Response.php @@ -42,6 +42,221 @@ */ final class Response extends Psr7Response implements StatusCodeInterface { + /** + * Create an HTML response + * + * ```php + * $html = << + * + * Hello wörld! + * + * + * HTML; + * + * $response = React\Http\Message\Response::html($html); + * ``` + * + * This is a convenient shortcut method that returns the equivalent of this: + * + * ``` + * $response = new React\Http\Message\Response( + * React\Http\Message\Response::STATUS_OK, + * [ + * 'Content-Type' => 'text/html; charset=utf-8' + * ], + * $html + * ); + * ``` + * + * This method always returns a response with a `200 OK` status code and + * the appropriate `Content-Type` response header for the given HTTP source + * string encoded in UTF-8 (Unicode). It's generally recommended to end the + * given plaintext string with a trailing newline. + * + * If you want to use a different status code or custom HTTP response + * headers, you can manipulate the returned response object using the + * provided PSR-7 methods or directly instantiate a custom HTTP response + * object using the `Response` constructor: + * + * ```php + * $response = React\Http\Message\Response::html( + * "

Error

\n

Invalid user name given.

\n" + * )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST); + * ``` + * + * @param string $html + * @return self + */ + public static function html($html) + { + return new self(self::STATUS_OK, array('Content-Type' => 'text/html; charset=utf-8'), $html); + } + + /** + * Create a JSON response + * + * ```php + * $response = React\Http\Message\Response::json(['name' => 'Alice']); + * ``` + * + * This is a convenient shortcut method that returns the equivalent of this: + * + * ``` + * $response = new React\Http\Message\Response( + * React\Http\Message\Response::STATUS_OK, + * [ + * 'Content-Type' => 'application/json' + * ], + * json_encode( + * ['name' => 'Alice'], + * JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION + * ) . "\n" + * ); + * ``` + * + * This method always returns a response with a `200 OK` status code and + * the appropriate `Content-Type` response header for the given structured + * data encoded as a JSON text. + * + * The given structured data will be encoded as a JSON text. Any `string` + * values in the data must be encoded in UTF-8 (Unicode). If the encoding + * fails, this method will throw an `InvalidArgumentException`. + * + * By default, the given structured data will be encoded with the flags as + * shown above. This includes pretty printing (PHP 5.4+) and preserving + * zero fractions for `float` values (PHP 5.6.6+) to ease debugging. It is + * assumed any additional data overhead is usually compensated by using HTTP + * response compression. + * + * If you want to use a different status code or custom HTTP response + * headers, you can manipulate the returned response object using the + * provided PSR-7 methods or directly instantiate a custom HTTP response + * object using the `Response` constructor: + * + * ```php + * $response = React\Http\Message\Response::json( + * ['error' => 'Invalid user name given'] + * )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST); + * ``` + * + * @param mixed $data + * @return self + * @throws \InvalidArgumentException when encoding fails + */ + public static function json($data) + { + $json = @\json_encode( + $data, + (\defined('JSON_PRETTY_PRINT') ? \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE : 0) | (\defined('JSON_PRESERVE_ZERO_FRACTION') ? \JSON_PRESERVE_ZERO_FRACTION : 0) + ); + + // throw on error, now `false` but used to be `(string) "null"` before PHP 5.5 + if ($json === false || (\PHP_VERSION_ID < 50500 && \json_last_error() !== \JSON_ERROR_NONE)) { + throw new \InvalidArgumentException( + 'Unable to encode given data as JSON' . (\function_exists('json_last_error_msg') ? ': ' . \json_last_error_msg() : ''), + \json_last_error() + ); + } + + return new self(self::STATUS_OK, array('Content-Type' => 'application/json'), $json . "\n"); + } + + /** + * Create a plaintext response + * + * ```php + * $response = React\Http\Message\Response::plaintext("Hello wörld!\n"); + * ``` + * + * This is a convenient shortcut method that returns the equivalent of this: + * + * ``` + * $response = new React\Http\Message\Response( + * React\Http\Message\Response::STATUS_OK, + * [ + * 'Content-Type' => 'text/plain; charset=utf-8' + * ], + * "Hello wörld!\n" + * ); + * ``` + * + * This method always returns a response with a `200 OK` status code and + * the appropriate `Content-Type` response header for the given plaintext + * string encoded in UTF-8 (Unicode). It's generally recommended to end the + * given plaintext string with a trailing newline. + * + * If you want to use a different status code or custom HTTP response + * headers, you can manipulate the returned response object using the + * provided PSR-7 methods or directly instantiate a custom HTTP response + * object using the `Response` constructor: + * + * ```php + * $response = React\Http\Message\Response::plaintext( + * "Error: Invalid user name given.\n" + * )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST); + * ``` + * + * @param string $text + * @return self + */ + public static function plaintext($text) + { + return new self(self::STATUS_OK, array('Content-Type' => 'text/plain; charset=utf-8'), $text); + } + + /** + * Create an XML response + * + * ```php + * $xml = << + * + * Hello wörld! + * + * + * XML; + * + * $response = React\Http\Message\Response::xml($xml); + * ``` + * + * This is a convenient shortcut method that returns the equivalent of this: + * + * ``` + * $response = new React\Http\Message\Response( + * React\Http\Message\Response::STATUS_OK, + * [ + * 'Content-Type' => 'application/xml' + * ], + * $xml + * ); + * ``` + * + * This method always returns a response with a `200 OK` status code and + * the appropriate `Content-Type` response header for the given XML source + * string. It's generally recommended to use UTF-8 (Unicode) and specify + * this as part of the leading XML declaration and to end the given XML + * source string with a trailing newline. + * + * If you want to use a different status code or custom HTTP response + * headers, you can manipulate the returned response object using the + * provided PSR-7 methods or directly instantiate a custom HTTP response + * object using the `Response` constructor: + * + * ```php + * $response = React\Http\Message\Response::xml( + * "Invalid user name given.\n" + * )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST); + * ``` + * + * @param string $xml + * @return self + */ + public static function xml($xml) + { + return new self(self::STATUS_OK, array('Content-Type' => 'application/xml'), $xml); + } + /** * @param int $status HTTP status code (e.g. 200/404), see `self::STATUS_*` constants * @param array $headers additional response headers diff --git a/tests/Message/ResponseTest.php b/tests/Message/ResponseTest.php index 3c6ad3fd..ed21cdc2 100644 --- a/tests/Message/ResponseTest.php +++ b/tests/Message/ResponseTest.php @@ -53,4 +53,75 @@ public function testResourceBodyWillThrow() $this->setExpectedException('InvalidArgumentException'); new Response(200, array(), tmpfile()); } + + + public function testHtmlMethodReturnsHtmlResponse() + { + $response = Response::html('Hello wörld!'); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); + $this->assertEquals('Hello wörld!', (string) $response->getBody()); + } + + /** + * @requires PHP 5.4 + */ + public function testJsonMethodReturnsPrettyPrintedJsonResponse() + { + $response = Response::json(array('text' => 'Hello wörld!')); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('application/json', $response->getHeaderLine('Content-Type')); + $this->assertEquals("{\n \"text\": \"Hello wörld!\"\n}\n", (string) $response->getBody()); + } + + /** + * @requires PHP 5.6.6 + */ + public function testJsonMethodReturnsZeroFractionsInJsonResponse() + { + $response = Response::json(1.0); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('application/json', $response->getHeaderLine('Content-Type')); + $this->assertEquals("1.0\n", (string) $response->getBody()); + } + + public function testJsonMethodReturnsJsonTextForSimpleString() + { + $response = Response::json('Hello world!'); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('application/json', $response->getHeaderLine('Content-Type')); + $this->assertEquals("\"Hello world!\"\n", (string) $response->getBody()); + } + + public function testJsonMethodThrowsForInvalidString() + { + if (PHP_VERSION_ID < 50500) { + $this->setExpectedException('InvalidArgumentException', 'Unable to encode given data as JSON'); + } else { + $this->setExpectedException('InvalidArgumentException', 'Unable to encode given data as JSON: Malformed UTF-8 characters, possibly incorrectly encoded'); + } + Response::json("Hello w\xF6rld!"); + } + + public function testPlaintextMethodReturnsPlaintextResponse() + { + $response = Response::plaintext("Hello wörld!\n"); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('text/plain; charset=utf-8', $response->getHeaderLine('Content-Type')); + $this->assertEquals("Hello wörld!\n", (string) $response->getBody()); + } + + public function testXmlMethodReturnsXmlResponse() + { + $response = Response::xml('Hello wörld!'); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('application/xml', $response->getHeaderLine('Content-Type')); + $this->assertEquals('Hello wörld!', (string) $response->getBody()); + } } From a49404c6e61dc01623844d887e3533e6a98c3542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Sat, 22 Jan 2022 14:31:58 +0100 Subject: [PATCH 2/2] Update documentation and examples to use new response factory methods --- README.md | 140 ++++++---------------- examples/51-server-hello-world.php | 13 +- examples/52-server-count-visitors.php | 11 +- examples/53-server-whatsmyip.php | 13 +- examples/54-server-query-parameter.php | 11 +- examples/55-server-cookie-handling.php | 24 +--- examples/56-server-sleep.php | 22 ++-- examples/57-server-error-handling.php | 28 ++--- examples/58-server-stream-response.php | 3 +- examples/59-server-json-api.php | 41 ++----- examples/61-server-hello-world-https.php | 13 +- examples/62-server-form-upload.php | 6 +- examples/63-server-streaming-request.php | 14 +-- examples/71-server-http-proxy.php | 24 +--- examples/99-server-benchmark-download.php | 6 +- 15 files changed, 99 insertions(+), 270 deletions(-) diff --git a/README.md b/README.md index e178ee72..23e4b5fa 100644 --- a/README.md +++ b/README.md @@ -112,11 +112,7 @@ This is an HTTP server which responds with `Hello World!` to every request. require __DIR__ . '/vendor/autoload.php'; $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain' - ), + return React\Http\Message\Response::plaintext( "Hello World!\n" ); }); @@ -738,11 +734,7 @@ object and expects a [response](#server-response) object in return: ```php $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain' - ), + return React\Http\Message\Response::plaintext( "Hello World!\n" ); }); @@ -953,14 +945,10 @@ and will be passed to the callback function like this. ```php $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - $body = "The method of the request is: " . $request->getMethod(); - $body .= "The requested path is: " . $request->getUri()->getPath(); + $body = "The method of the request is: " . $request->getMethod() . "\n"; + $body .= "The requested path is: " . $request->getUri()->getPath() . "\n"; - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain' - ), + return React\Http\Message\Response::plaintext( $body ); }); @@ -996,13 +984,9 @@ The following parameters are currently available: ```php $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - $body = "Your IP is: " . $request->getServerParams()['REMOTE_ADDR']; + $body = "Your IP is: " . $request->getServerParams()['REMOTE_ADDR'] . "\n"; - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain' - ), + return React\Http\Message\Response::plaintext( $body ); }); @@ -1030,11 +1014,7 @@ $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterf $body = 'The value of "foo" is: ' . htmlspecialchars($queryParams['foo']); } - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Content-Type' => 'text/html' - ), + return React\Http\Message\Response::html( $body ); }); @@ -1077,9 +1057,7 @@ request headers (commonly used for `POST` requests for HTML form submission data $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { $name = $request->getParsedBody()['name'] ?? 'anonymous'; - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array(), + return React\Http\Message\Response::plaintext( "Hello $name!\n" ); }); @@ -1102,10 +1080,8 @@ $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterf $data = json_decode((string)$request->getBody()); $name = $data->name ?? 'anonymous'; - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array('Content-Type' => 'application/json'), - json_encode(['message' => "Hello $name!"]) + return React\Http\Message\Response::json( + ['message' => "Hello $name!"] ); }); ``` @@ -1125,9 +1101,7 @@ $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterf $files = $request->getUploadedFiles(); $name = isset($files['avatar']) ? $files['avatar']->getClientFilename() : 'nothing'; - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array(), + return React\Http\Message\Response::plaintext( "Uploaded $name\n" ); }); @@ -1208,24 +1182,16 @@ $http = new React\Http\HttpServer( }); $body->on('end', function () use ($resolve, &$bytes){ - $resolve(new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain' - ), + $resolve(React\Http\Message\Response::plaintext( "Received $bytes bytes\n" )); }); // an error occures e.g. on invalid chunked encoded data or an unexpected 'end' event $body->on('error', function (Exception $e) use ($resolve, &$bytes) { - $resolve(new React\Http\Message\Response( - React\Http\Message\Response::STATUS_BAD_REQUEST, - array( - 'Content-Type' => 'text/plain' - ), + $resolve(React\Http\Message\Response::plaintext( "Encountered error after $bytes bytes: {$e->getMessage()}\n" - )); + )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST)); }); }); } @@ -1272,23 +1238,15 @@ $http = new React\Http\HttpServer( function (Psr\Http\Message\ServerRequestInterface $request) { $size = $request->getBody()->getSize(); if ($size === null) { - $body = 'The request does not contain an explicit length.'; - $body .= 'This example does not accept chunked transfer encoding.'; - - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_LENGTH_REQUIRED, - array( - 'Content-Type' => 'text/plain' - ), + $body = "The request does not contain an explicit length. "; + $body .= "This example does not accept chunked transfer encoding.\n"; + + return React\Http\Message\Response::plaintext( $body - ); + )->withStatus(React\Http\Message\Response::STATUS_LENGTH_REQUIRED); } - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain' - ), + return React\Http\Message\Response::plaintext( "Request body size: " . $size . " bytes\n" ); } @@ -1344,25 +1302,16 @@ $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterf $key = 'react\php'; if (isset($request->getCookieParams()[$key])) { - $body = "Your cookie value is: " . $request->getCookieParams()[$key]; + $body = "Your cookie value is: " . $request->getCookieParams()[$key] . "\n"; - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain' - ), + return React\Http\Message\Response::plaintext( $body ); } - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain', - 'Set-Cookie' => urlencode($key) . '=' . urlencode('test;more') - ), - "Your cookie has been set." - ); + return React\Http\Message\Response::plaintext( + "Your cookie has been set.\n" + )->withHeader('Set-Cookie', urlencode($key) . '=' . urlencode('test;more')); }); ``` @@ -1413,11 +1362,7 @@ In its most simple form, you can use it like this: ```php $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain' - ), + return React\Http\Message\Response::plaintext( "Hello World!\n" ); }); @@ -1441,18 +1386,17 @@ This example shows how such a long-term action could look like: ```php $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - return new Promise(function ($resolve, $reject) { + $promise = new Promise(function ($resolve, $reject) { Loop::addTimer(1.5, function() use ($resolve) { - $response = new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain' - ), - "Hello world" - ); - $resolve($response); + $resolve(); }); }); + + return $promise->then(function () { + return React\Http\Message\Response::plaintext( + "Hello World!" + ); + }); }); ``` @@ -1571,11 +1515,7 @@ a `string` response body like this: ```php $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain' - ), + return React\Http\Message\Response::plaintext( "Hello World!\n" ); }); @@ -1845,11 +1785,9 @@ $http = new React\Http\HttpServer( $resolve($next($request)); }); return $promise->then(null, function (Exception $e) { - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_INTERNAL_SERVER_ERROR, - array(), - 'Internal error: ' . $e->getMessage() - ); + return React\Http\Message\Response::plaintext( + 'Internal error: ' . $e->getMessage() . "\n" + )->withStatus(React\Http\Message\Response::STATUS_INTERNAL_SERVER_ERROR); }); }, function (Psr\Http\Message\ServerRequestInterface $request) { diff --git a/examples/51-server-hello-world.php b/examples/51-server-hello-world.php index 88831525..9ff84eee 100644 --- a/examples/51-server-hello-world.php +++ b/examples/51-server-hello-world.php @@ -1,17 +1,10 @@ 'text/plain' - ), - "Hello world\n" +$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { + return React\Http\Message\Response::plaintext( + "Hello World!\n" ); }); diff --git a/examples/52-server-count-visitors.php b/examples/52-server-count-visitors.php index bdd53af9..341f9498 100644 --- a/examples/52-server-count-visitors.php +++ b/examples/52-server-count-visitors.php @@ -1,17 +1,10 @@ 'text/plain' - ), +$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) use (&$counter) { + return React\Http\Message\Response::plaintext( "Welcome number " . ++$counter . "!\n" ); }); diff --git a/examples/53-server-whatsmyip.php b/examples/53-server-whatsmyip.php index e0835a82..1e394b9e 100644 --- a/examples/53-server-whatsmyip.php +++ b/examples/53-server-whatsmyip.php @@ -1,18 +1,11 @@ getServerParams()['REMOTE_ADDR']; +$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { + $body = "Your IP is: " . $request->getServerParams()['REMOTE_ADDR'] . "\n"; - return new Response( - Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain' - ), + return React\Http\Message\Response::plaintext( $body ); }); diff --git a/examples/54-server-query-parameter.php b/examples/54-server-query-parameter.php index 18dd56b0..9b2d5749 100644 --- a/examples/54-server-query-parameter.php +++ b/examples/54-server-query-parameter.php @@ -1,11 +1,8 @@ getQueryParams(); $body = 'The query parameter "foo" is not set. Click the following link '; @@ -15,11 +12,7 @@ $body = 'The value of "foo" is: ' . htmlspecialchars($queryParams['foo']); } - return new Response( - Response::STATUS_OK, - array( - 'Content-Type' => 'text/html' - ), + return React\Http\Message\Response::html( $body ); }); diff --git a/examples/55-server-cookie-handling.php b/examples/55-server-cookie-handling.php index 8260fc33..796da24d 100644 --- a/examples/55-server-cookie-handling.php +++ b/examples/55-server-cookie-handling.php @@ -1,33 +1,21 @@ getCookieParams()[$key])) { - $body = "Your cookie value is: " . $request->getCookieParams()[$key]; + $body = "Your cookie value is: " . $request->getCookieParams()[$key] . "\n"; - return new Response( - Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain' - ), + return React\Http\Message\Response::plaintext( $body ); } - return new Response( - Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain', - 'Set-Cookie' => urlencode($key) . '=' . urlencode('test;more') - ), - "Your cookie has been set." - ); + return React\Http\Message\Response::plaintext( + "Your cookie has been set.\n" + )->withHeader('Set-Cookie', urlencode($key) . '=' . urlencode('test;more')); }); $socket = new React\Socket\SocketServer(isset($argv[1]) ? $argv[1] : '0.0.0.0:0'); diff --git a/examples/56-server-sleep.php b/examples/56-server-sleep.php index 6bb6f82b..2a3c9027 100644 --- a/examples/56-server-sleep.php +++ b/examples/56-server-sleep.php @@ -1,25 +1,21 @@ 'text/plain' - ), - "Hello world" - ); - $resolve($response); + $resolve(); }); }); + + return $promise->then(function () { + return React\Http\Message\Response::plaintext( + "Hello world!\n" + ); + }); }); $socket = new React\Socket\SocketServer(isset($argv[1]) ? $argv[1] : '0.0.0.0:0'); diff --git a/examples/57-server-error-handling.php b/examples/57-server-error-handling.php index 71cbad15..a9fb6bad 100644 --- a/examples/57-server-error-handling.php +++ b/examples/57-server-error-handling.php @@ -1,30 +1,18 @@ 'text/plain' - ), - "Hello World!\n" - ); + if ($count % 2 === 0) { + throw new Exception('Second call'); + } - $resolve($response); - }); + return React\Http\Message\Response::plaintext( + "Hello World!\n" + ); }); $socket = new React\Socket\SocketServer(isset($argv[1]) ? $argv[1] : '0.0.0.0:0'); diff --git a/examples/58-server-stream-response.php b/examples/58-server-stream-response.php index 015ddd9a..cf65a3bf 100644 --- a/examples/58-server-stream-response.php +++ b/examples/58-server-stream-response.php @@ -1,13 +1,12 @@ getMethod() !== 'GET' || $request->getUri()->getPath() !== '/') { return new Response(Response::STATUS_NOT_FOUND); } diff --git a/examples/59-server-json-api.php b/examples/59-server-json-api.php index 0d50b52b..f48be7e3 100644 --- a/examples/59-server-json-api.php +++ b/examples/59-server-json-api.php @@ -6,49 +6,32 @@ // $ php examples/59-server-json-api.php 8080 // $ curl -v http://localhost:8080/ -H 'Content-Type: application/json' -d '{"name":"Alice"}' -use Psr\Http\Message\ServerRequestInterface; use React\Http\Message\Response; require __DIR__ . '/../vendor/autoload.php'; -$http = new React\Http\HttpServer(function (ServerRequestInterface $request) { +$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { if ($request->getHeaderLine('Content-Type') !== 'application/json') { - return new Response( - Response::STATUS_UNSUPPORTED_MEDIA_TYPE, - array( - 'Content-Type' => 'application/json' - ), - json_encode(array('error' => 'Only supports application/json')) . "\n" - ); + return Response::json( + array('error' => 'Only supports application/json') + )->withStatus(Response::STATUS_UNSUPPORTED_MEDIA_TYPE); } $input = json_decode($request->getBody()->getContents()); if (json_last_error() !== JSON_ERROR_NONE) { - return new Response( - Response::STATUS_BAD_REQUEST, - array( - 'Content-Type' => 'application/json' - ), - json_encode(array('error' => 'Invalid JSON data given')) . "\n" - ); + return Response::json( + array('error' => 'Invalid JSON data given') + )->withStatus(Response::STATUS_BAD_REQUEST); } if (!isset($input->name) || !is_string($input->name)) { - return new Response( - Response::STATUS_UNPROCESSABLE_ENTITY, - array( - 'Content-Type' => 'application/json' - ), - json_encode(array('error' => 'JSON data does not contain a string "name" property')) . "\n" - ); + return Response::json( + array('error' => 'JSON data does not contain a string "name" property') + )->withStatus(Response::STATUS_UNPROCESSABLE_ENTITY); } - return new Response( - Response::STATUS_OK, - array( - 'Content-Type' => 'application/json' - ), - json_encode(array('message' => 'Hello ' . $input->name)) . "\n" + return Response::json( + array('message' => 'Hello ' . $input->name) ); }); diff --git a/examples/61-server-hello-world-https.php b/examples/61-server-hello-world-https.php index 2fd6f9af..23906430 100644 --- a/examples/61-server-hello-world-https.php +++ b/examples/61-server-hello-world-https.php @@ -1,17 +1,10 @@ 'text/plain' - ), - "Hello world!\n" +$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { + return React\Http\Message\Response::plaintext( + "Hello World!\n" ); }); diff --git a/examples/62-server-form-upload.php b/examples/62-server-form-upload.php index 899caa0a..52864c82 100644 --- a/examples/62-server-form-upload.php +++ b/examples/62-server-form-upload.php @@ -109,11 +109,7 @@ HTML; - return new Response( - Response::STATUS_OK, - array( - 'Content-Type' => 'text/html; charset=UTF-8' - ), + return Response::html( $html ); }; diff --git a/examples/63-server-streaming-request.php b/examples/63-server-streaming-request.php index b20b8f08..fef6f008 100644 --- a/examples/63-server-streaming-request.php +++ b/examples/63-server-streaming-request.php @@ -19,24 +19,16 @@ function (Psr\Http\Message\ServerRequestInterface $request) { }); $body->on('end', function () use ($resolve, &$bytes){ - $resolve(new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain' - ), + $resolve(React\Http\Message\Response::plaintext( "Received $bytes bytes\n" )); }); // an error occures e.g. on invalid chunked encoded data or an unexpected 'end' event $body->on('error', function (Exception $e) use ($resolve, &$bytes) { - $resolve(new React\Http\Message\Response( - React\Http\Message\Response::STATUS_BAD_REQUEST, - array( - 'Content-Type' => 'text/plain' - ), + $resolve(React\Http\Message\Response::plaintext( "Encountered error after $bytes bytes: {$e->getMessage()}\n" - )); + )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST)); }); }); } diff --git a/examples/71-server-http-proxy.php b/examples/71-server-http-proxy.php index e0bf8404..cf63c4ae 100644 --- a/examples/71-server-http-proxy.php +++ b/examples/71-server-http-proxy.php @@ -3,25 +3,17 @@ // $ php examples/71-server-http-proxy.php 8080 // $ curl -v --proxy http://localhost:8080 http://reactphp.org/ -use Psr\Http\Message\RequestInterface; -use React\Http\Message\Response; -use RingCentral\Psr7; - require __DIR__ . '/../vendor/autoload.php'; // Note how this example uses the `HttpServer` without the `StreamingRequestMiddleware`. // This means that this proxy buffers the whole request before "processing" it. // As such, this is store-and-forward proxy. This could also use the advanced // `StreamingRequestMiddleware` to forward the incoming request as it comes in. -$http = new React\Http\HttpServer(function (RequestInterface $request) { +$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { if (strpos($request->getRequestTarget(), '://') === false) { - return new Response( - Response::STATUS_BAD_REQUEST, - array( - 'Content-Type' => 'text/plain' - ), - 'This is a plain HTTP proxy' - ); + return React\Http\Message\Response::plaintext( + "This is a plain HTTP proxy\n" + )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST); } // prepare outgoing client request by updating request-target and Host header @@ -35,12 +27,8 @@ // pseudo code only: simply dump the outgoing request as a string // left up as an exercise: use an HTTP client to send the outgoing request // and forward the incoming response to the original client request - return new Response( - Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain' - ), - Psr7\str($outgoing) + return React\Http\Message\Response::plaintext( + RingCentral\Psr7\str($outgoing) ); }); diff --git a/examples/99-server-benchmark-download.php b/examples/99-server-benchmark-download.php index 6c737605..ddd4760a 100644 --- a/examples/99-server-benchmark-download.php +++ b/examples/99-server-benchmark-download.php @@ -93,11 +93,7 @@ public function getSize() $http = new React\Http\HttpServer(function (ServerRequestInterface $request) { switch ($request->getUri()->getPath()) { case '/': - return new Response( - Response::STATUS_OK, - array( - 'Content-Type' => 'text/html' - ), + return Response::html( '1g.bin
10g.bin' ); case '/1g.bin':