diff --git a/README.md b/README.md
index 09a4ecd2..0ea7c11c 100644
--- a/README.md
+++ b/README.md
@@ -69,6 +69,7 @@ multiple concurrent HTTP requests without blocking.
* [withProtocolVersion()](#withprotocolversion)
* [withResponseBuffer()](#withresponsebuffer)
* [React\Http\Message](#reacthttpmessage)
+ * [ResponseFactory](#responsefactory)
* [Response](#response)
* [ServerRequest](#serverrequest)
* [ResponseException](#responseexception)
@@ -103,13 +104,7 @@ This is an HTTP server which responds with `Hello World!` to every request.
$loop = React\EventLoop\Factory::create();
$server = new React\Http\Server($loop, function (Psr\Http\Message\ServerRequestInterface $request) {
- return new React\Http\Message\Response(
- 200,
- array(
- 'Content-Type' => 'text/plain'
- ),
- "Hello World!\n"
- );
+ return React\Http\Message\ResponseFactory::plain("Hello World!\n");
});
$socket = new React\Socket\Server(8080, $loop);
@@ -719,13 +714,7 @@ object and expects a [response](#server-response) object in return:
```php
$server = new React\Http\Server($loop, function (Psr\Http\Message\ServerRequestInterface $request) {
- return new React\Http\Message\Response(
- 200,
- array(
- 'Content-Type' => 'text/plain'
- ),
- "Hello World!\n"
- );
+ return React\Http\Message\ResponseFactory::plain("Hello World!\n");
});
```
@@ -2374,6 +2363,19 @@ given setting applied.
### React\Http\Message
+#### ResponseFactory
+
+The `React\Http\Message\ResponseFactory` provides a few methods for well known content types. Except `json` all methods
+accept both string or [`ReadableStreamInterface`](https://github.com/reactphp/stream#readablestreaminterface) as body.
+`json` however, will also do the encoding to JSON for you.
+
+```php
+$htmlResponse = React\Http\Message\ResponseFactory::html('
Hello world!');
+$jsonResponse = React\Http\Message\ResponseFactory::json(array('message' => array('body' => 'Hello World!')));
+$plainResponse = React\Http\Message\ResponseFactory::plain('Hello world!');
+$xmlResponse = React\Http\Message\ResponseFactory::xml('Hello world!');
+```
+
#### Response
The `React\Http\Message\Response` class can be used to
diff --git a/examples/51-server-hello-world.php b/examples/51-server-hello-world.php
index f6903cff..c0c7d1e3 100644
--- a/examples/51-server-hello-world.php
+++ b/examples/51-server-hello-world.php
@@ -2,7 +2,7 @@
use Psr\Http\Message\ServerRequestInterface;
use React\EventLoop\Factory;
-use React\Http\Message\Response;
+use React\Http\Message\ResponseFactory;
use React\Http\Server;
require __DIR__ . '/../vendor/autoload.php';
@@ -10,13 +10,7 @@
$loop = Factory::create();
$server = new Server($loop, function (ServerRequestInterface $request) {
- return new Response(
- 200,
- array(
- 'Content-Type' => 'text/plain'
- ),
- "Hello world\n"
- );
+ return ResponseFactory::plain("Hello world\n");
});
$socket = new \React\Socket\Server(isset($argv[1]) ? $argv[1] : '0.0.0.0:0', $loop);
diff --git a/examples/52-server-count-visitors.php b/examples/52-server-count-visitors.php
index 2b8e897c..35b29962 100644
--- a/examples/52-server-count-visitors.php
+++ b/examples/52-server-count-visitors.php
@@ -2,7 +2,7 @@
use Psr\Http\Message\ServerRequestInterface;
use React\EventLoop\Factory;
-use React\Http\Message\Response;
+use React\Http\Message\ResponseFactory;
use React\Http\Server;
require __DIR__ . '/../vendor/autoload.php';
@@ -11,13 +11,7 @@
$counter = 0;
$server = new Server($loop, function (ServerRequestInterface $request) use (&$counter) {
- return new Response(
- 200,
- array(
- 'Content-Type' => 'text/plain'
- ),
- "Welcome number " . ++$counter . "!\n"
- );
+ return ResponseFactory::plain("Welcome number " . ++$counter . "!\n");
});
$socket = new \React\Socket\Server(isset($argv[1]) ? $argv[1] : '0.0.0.0:0', $loop);
diff --git a/examples/53-server-whatsmyip.php b/examples/53-server-whatsmyip.php
index 18f7504e..bab3b566 100644
--- a/examples/53-server-whatsmyip.php
+++ b/examples/53-server-whatsmyip.php
@@ -2,7 +2,7 @@
use Psr\Http\Message\ServerRequestInterface;
use React\EventLoop\Factory;
-use React\Http\Message\Response;
+use React\Http\Message\ResponseFactory;
use React\Http\Server;
require __DIR__ . '/../vendor/autoload.php';
@@ -12,13 +12,7 @@
$server = new Server($loop, function (ServerRequestInterface $request) {
$body = "Your IP is: " . $request->getServerParams()['REMOTE_ADDR'];
- return new Response(
- 200,
- array(
- 'Content-Type' => 'text/plain'
- ),
- $body
- );
+ return ResponseFactory::plain($body);
});
$socket = new \React\Socket\Server(isset($argv[1]) ? $argv[1] : '0.0.0.0:0', $loop);
diff --git a/examples/54-server-query-parameter.php b/examples/54-server-query-parameter.php
index 2786f380..178ec3e7 100644
--- a/examples/54-server-query-parameter.php
+++ b/examples/54-server-query-parameter.php
@@ -2,7 +2,7 @@
use Psr\Http\Message\ServerRequestInterface;
use React\EventLoop\Factory;
-use React\Http\Message\Response;
+use React\Http\Message\ResponseFactory;
use React\Http\Server;
require __DIR__ . '/../vendor/autoload.php';
@@ -19,13 +19,7 @@
$body = 'The value of "foo" is: ' . htmlspecialchars($queryParams['foo']);
}
- return new Response(
- 200,
- array(
- 'Content-Type' => 'text/html'
- ),
- $body
- );
+ return ResponseFactory::html($body);
});
$socket = new \React\Socket\Server(isset($argv[1]) ? $argv[1] : '0.0.0.0:0', $loop);
diff --git a/examples/55-server-cookie-handling.php b/examples/55-server-cookie-handling.php
index 6faf6be7..031dc6f4 100644
--- a/examples/55-server-cookie-handling.php
+++ b/examples/55-server-cookie-handling.php
@@ -2,7 +2,7 @@
use Psr\Http\Message\ServerRequestInterface;
use React\EventLoop\Factory;
-use React\Http\Message\Response;
+use React\Http\Message\ResponseFactory;
use React\Http\Server;
require __DIR__ . '/../vendor/autoload.php';
@@ -15,23 +15,10 @@
if (isset($request->getCookieParams()[$key])) {
$body = "Your cookie value is: " . $request->getCookieParams()[$key];
- return new Response(
- 200,
- array(
- 'Content-Type' => 'text/plain'
- ),
- $body
- );
+ return ResponseFactory::plain($body);
}
- return new Response(
- 200,
- array(
- 'Content-Type' => 'text/plain',
- 'Set-Cookie' => urlencode($key) . '=' . urlencode('test;more')
- ),
- "Your cookie has been set."
- );
+ return ResponseFactory::plain('Your cookie has been set.')->withAddedHeader('Set-Cookie', urlencode($key) . '=' . urlencode('test;more'));
});
$socket = new \React\Socket\Server(isset($argv[1]) ? $argv[1] : '0.0.0.0:0', $loop);
diff --git a/examples/56-server-sleep.php b/examples/56-server-sleep.php
index 3da6963b..40600853 100644
--- a/examples/56-server-sleep.php
+++ b/examples/56-server-sleep.php
@@ -2,7 +2,7 @@
use Psr\Http\Message\ServerRequestInterface;
use React\EventLoop\Factory;
-use React\Http\Message\Response;
+use React\Http\Message\ResponseFactory;
use React\Http\Server;
use React\Promise\Promise;
@@ -13,13 +13,7 @@
$server = new Server($loop, function (ServerRequestInterface $request) use ($loop) {
return new Promise(function ($resolve, $reject) use ($loop) {
$loop->addTimer(1.5, function() use ($resolve) {
- $response = new Response(
- 200,
- array(
- 'Content-Type' => 'text/plain'
- ),
- "Hello world"
- );
+ $response = ResponseFactory::plain('Hello world');
$resolve($response);
});
});
diff --git a/examples/57-server-error-handling.php b/examples/57-server-error-handling.php
index c8e99ee4..0c8e586a 100644
--- a/examples/57-server-error-handling.php
+++ b/examples/57-server-error-handling.php
@@ -2,7 +2,7 @@
use Psr\Http\Message\ServerRequestInterface;
use React\EventLoop\Factory;
-use React\Http\Message\Response;
+use React\Http\Message\ResponseFactory;
use React\Http\Server;
use React\Promise\Promise;
@@ -19,13 +19,7 @@
throw new Exception('Second call');
}
- $response = new Response(
- 200,
- array(
- 'Content-Type' => 'text/plain'
- ),
- "Hello World!\n"
- );
+ $response = ResponseFactory::plain("Hello World!\n");
$resolve($response);
});
diff --git a/examples/58-server-stream-response.php b/examples/58-server-stream-response.php
index 518c2cb4..73a338eb 100644
--- a/examples/58-server-stream-response.php
+++ b/examples/58-server-stream-response.php
@@ -3,6 +3,7 @@
use Psr\Http\Message\ServerRequestInterface;
use React\EventLoop\Factory;
use React\Http\Message\Response;
+use React\Http\Message\ResponseFactory;
use React\Http\Server;
use React\Stream\ThroughStream;
@@ -32,13 +33,7 @@
$loop->cancelTimer($timer);
});
- return new Response(
- 200,
- array(
- 'Content-Type' => 'text/plain'
- ),
- $stream
- );
+ return ResponseFactory::plain($stream);
});
$socket = new \React\Socket\Server(isset($argv[1]) ? $argv[1] : '0.0.0.0:0', $loop);
diff --git a/examples/59-server-json-api.php b/examples/59-server-json-api.php
index 8602a889..1637f83a 100644
--- a/examples/59-server-json-api.php
+++ b/examples/59-server-json-api.php
@@ -8,7 +8,7 @@
use Psr\Http\Message\ServerRequestInterface;
use React\EventLoop\Factory;
-use React\Http\Message\Response;
+use React\Http\Message\ResponseFactory;
use React\Http\Server;
require __DIR__ . '/../vendor/autoload.php';
@@ -17,43 +17,25 @@
$server = new Server($loop, function (ServerRequestInterface $request) {
if ($request->getHeaderLine('Content-Type') !== 'application/json') {
- return new Response(
- 415, // Unsupported Media Type
- array(
- 'Content-Type' => 'application/json'
- ),
- json_encode(array('error' => 'Only supports application/json')) . "\n"
- );
+ return ResponseFactory::json(
+ array('error' => 'Only supports application/json')
+ )->withStatus(415); // Unsupported Media Type
}
$input = json_decode($request->getBody()->getContents());
if (json_last_error() !== JSON_ERROR_NONE) {
- return new Response(
- 400, // Bad Request
- array(
- 'Content-Type' => 'application/json'
- ),
- json_encode(array('error' => 'Invalid JSON data given')) . "\n"
- );
+ return ResponseFactory::json(
+ array('error' => 'Invalid JSON data given')
+ )->withStatus(400); // Bad Request
}
if (!isset($input->name) || !is_string($input->name)) {
- return new Response(
- 422, // Unprocessable Entity
- array(
- 'Content-Type' => 'application/json'
- ),
- json_encode(array('error' => 'JSON data does not contain a string "name" property')) . "\n"
- );
+ return ResponseFactory::json(
+ array('error' => 'JSON data does not contain a string "name" property')
+ )->withStatus(422); // Unprocessable Entity
}
- return new Response(
- 200,
- array(
- 'Content-Type' => 'application/json'
- ),
- json_encode(array('message' => 'Hello ' . $input->name)) . "\n"
- );
+ return ResponseFactory::json(array('message' => 'Hello ' . $input->name));
});
$socket = new \React\Socket\Server(isset($argv[1]) ? $argv[1] : '0.0.0.0:0', $loop);
diff --git a/examples/61-server-hello-world-https.php b/examples/61-server-hello-world-https.php
index dfe3e941..2625a83a 100644
--- a/examples/61-server-hello-world-https.php
+++ b/examples/61-server-hello-world-https.php
@@ -2,7 +2,7 @@
use Psr\Http\Message\ServerRequestInterface;
use React\EventLoop\Factory;
-use React\Http\Message\Response;
+use React\Http\Message\ResponseFactory;
use React\Http\Server;
require __DIR__ . '/../vendor/autoload.php';
@@ -10,13 +10,7 @@
$loop = Factory::create();
$server = new Server($loop, function (ServerRequestInterface $request) {
- return new Response(
- 200,
- array(
- 'Content-Type' => 'text/plain'
- ),
- "Hello world!\n"
- );
+ return ResponseFactory::plain("Hello world\n");
});
$socket = new \React\Socket\Server(isset($argv[1]) ? $argv[1] : '0.0.0.0:0', $loop);
diff --git a/examples/63-server-streaming-request.php b/examples/63-server-streaming-request.php
index 45eb0dea..39280866 100644
--- a/examples/63-server-streaming-request.php
+++ b/examples/63-server-streaming-request.php
@@ -1,6 +1,7 @@
on('end', function () use ($resolve, &$bytes){
- $resolve(new React\Http\Message\Response(
- 200,
- array(
- 'Content-Type' => 'text/plain'
- ),
- "Received $bytes bytes\n"
- ));
+ $resolve(ResponseFactory::plain("Received $bytes bytes\n"));
});
// an error occures e.g. on invalid chunked encoded data or an unexpected 'end' event
$body->on('error', function (\Exception $exception) use ($resolve, &$bytes) {
- $resolve(new React\Http\Message\Response(
- 400,
- array(
- 'Content-Type' => 'text/plain'
- ),
- "Encountered error after $bytes bytes: {$exception->getMessage()}\n"
- ));
+ $resolve(ResponseFactory::plain("Encountered error after $bytes bytes: {$exception->getMessage()}\n")->withStatus(400));
});
});
}
diff --git a/examples/71-server-http-proxy.php b/examples/71-server-http-proxy.php
index b959b7bf..a70dc86c 100644
--- a/examples/71-server-http-proxy.php
+++ b/examples/71-server-http-proxy.php
@@ -5,7 +5,7 @@
use Psr\Http\Message\RequestInterface;
use React\EventLoop\Factory;
-use React\Http\Message\Response;
+use React\Http\Message\ResponseFactory;
use React\Http\Server;
use RingCentral\Psr7;
@@ -19,13 +19,7 @@
// `StreamingRequestMiddleware` to forward the incoming request as it comes in.
$server = new Server($loop, function (RequestInterface $request) {
if (strpos($request->getRequestTarget(), '://') === false) {
- return new Response(
- 400,
- array(
- 'Content-Type' => 'text/plain'
- ),
- 'This is a plain HTTP proxy'
- );
+ return ResponseFactory::plain('This is a plain HTTP proxy')->withStatus(400);
}
// prepare outgoing client request by updating request-target and Host header
@@ -39,13 +33,7 @@
// 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(
- 200,
- array(
- 'Content-Type' => 'text/plain'
- ),
- Psr7\str($outgoing)
- );
+ return ResponseFactory::plain(Psr7\str($outgoing));
});
$socket = new \React\Socket\Server(isset($argv[1]) ? $argv[1] : '0.0.0.0:0', $loop);
diff --git a/examples/72-server-http-connect-proxy.php b/examples/72-server-http-connect-proxy.php
index e786da76..46211805 100644
--- a/examples/72-server-http-connect-proxy.php
+++ b/examples/72-server-http-connect-proxy.php
@@ -6,6 +6,7 @@
use Psr\Http\Message\ServerRequestInterface;
use React\EventLoop\Factory;
use React\Http\Message\Response;
+use React\Http\Message\ResponseFactory;
use React\Http\Server;
use React\Socket\Connector;
use React\Socket\ConnectionInterface;
@@ -21,14 +22,7 @@
// doesn't have to store any payload data in memory at all.
$server = new Server($loop, function (ServerRequestInterface $request) use ($connector) {
if ($request->getMethod() !== 'CONNECT') {
- return new Response(
- 405,
- array(
- 'Content-Type' => 'text/plain',
- 'Allow' => 'CONNECT'
- ),
- 'This is a HTTP CONNECT (secure HTTPS) proxy'
- );
+ return ResponseFactory::plain('This is a HTTP CONNECT (secure HTTPS) proxy')->withStatus(405);
}
// try to connect to given target host
@@ -42,13 +36,7 @@ function (ConnectionInterface $remote) {
);
},
function ($e) {
- return new Response(
- 502,
- array(
- 'Content-Type' => 'text/plain'
- ),
- 'Unable to connect: ' . $e->getMessage()
- );
+ return ResponseFactory::plain('Unable to connect: ' . $e->getMessage())->withStatus(502);
}
);
});
diff --git a/src/Message/ResponseFactory.php b/src/Message/ResponseFactory.php
new file mode 100644
index 00000000..13772fb6
--- /dev/null
+++ b/src/Message/ResponseFactory.php
@@ -0,0 +1,80 @@
+ 'text/html; charset=utf-8',
+ ),
+ $body
+ );
+ }
+
+ /**
+ * @param mixed $body
+ * @return ResponseInterface
+ */
+ public static function json($body)
+ {
+ $json = @\json_encode($body);
+
+ if (\json_last_error() !== JSON_ERROR_NONE || ($json === null && $body !== null)) {
+ if (\function_exists('json_last_error_msg')) {
+ throw new \InvalidArgumentException('Error encoding JSON: ' . \json_last_error_msg());
+ }
+
+ throw new \InvalidArgumentException('Error encoding JSON');
+ }
+
+ return new Response(
+ 200,
+ array(
+ 'Content-Type' => 'application/json; charset=utf-8',
+ ),
+ $json
+ );
+ }
+
+ /**
+ * @param string|ReadableStreamInterface|StreamInterface $body
+ * @return ResponseInterface
+ */
+ public static function plain($body)
+ {
+ return new Response(
+ 200,
+ array(
+ 'Content-Type' => 'text/plain; charset=utf-8',
+ ),
+ $body
+ );
+ }
+
+ /**
+ * @param string|ReadableStreamInterface|StreamInterface $body
+ * @return ResponseInterface
+ */
+ public static function xml($body)
+ {
+ return new Response(
+ 200,
+ array(
+ 'Content-Type' => 'application/xml; charset=utf-8',
+ ),
+ $body
+ );
+ }
+}
diff --git a/tests/Message/ResponseFactoryTest.php b/tests/Message/ResponseFactoryTest.php
new file mode 100644
index 00000000..2c145f3d
--- /dev/null
+++ b/tests/Message/ResponseFactoryTest.php
@@ -0,0 +1,71 @@
+Hello world!