|
14 | 14 | use React\Http\Io\MiddlewareRunner; |
15 | 15 | use React\Http\Io\RequestHeaderParser; |
16 | 16 | use React\Http\Io\ServerRequest; |
| 17 | +use React\Promise; |
17 | 18 | use React\Promise\CancellablePromiseInterface; |
18 | | -use React\Promise\Promise; |
| 19 | +use React\Promise\PromiseInterface; |
19 | 20 | use React\Socket\ConnectionInterface; |
20 | 21 | use React\Socket\ServerInterface; |
21 | 22 | use React\Stream\ReadableStreamInterface; |
@@ -229,22 +230,43 @@ public function handleRequest(ConnectionInterface $conn, ServerRequestInterface |
229 | 230 | $conn->write("HTTP/1.1 100 Continue\r\n\r\n"); |
230 | 231 | } |
231 | 232 |
|
| 233 | + // execute request handler callback |
232 | 234 | $callback = $this->callback; |
233 | | - $cancel = null; |
234 | | - $promise = new Promise(function ($resolve, $reject) use ($callback, $request, &$cancel) { |
235 | | - $cancel = $callback($request); |
236 | | - $resolve($cancel); |
237 | | - }); |
| 235 | + try { |
| 236 | + $response = $callback($request); |
| 237 | + } catch (\Exception $error) { |
| 238 | + // request handler callback throws an Exception |
| 239 | + $response = Promise\reject($error); |
| 240 | + } catch (\Throwable $error) { // @codeCoverageIgnoreStart |
| 241 | + // request handler callback throws a PHP7+ Error |
| 242 | + $response = Promise\reject($error); // @codeCoverageIgnoreEnd |
| 243 | + } |
238 | 244 |
|
239 | 245 | // cancel pending promise once connection closes |
240 | | - if ($cancel instanceof CancellablePromiseInterface) { |
241 | | - $conn->on('close', function () use ($cancel) { |
242 | | - $cancel->cancel(); |
| 246 | + if ($response instanceof CancellablePromiseInterface) { |
| 247 | + $conn->on('close', function () use ($response) { |
| 248 | + $response->cancel(); |
243 | 249 | }); |
244 | 250 | } |
245 | 251 |
|
| 252 | + // happy path: request body is known to be empty => immediately end stream |
| 253 | + if ($contentLength === 0) { |
| 254 | + $stream->emit('end'); |
| 255 | + $stream->close(); |
| 256 | + } |
| 257 | + |
| 258 | + // happy path: response returned, handle and return immediately |
| 259 | + if ($response instanceof ResponseInterface) { |
| 260 | + return $this->handleResponse($conn, $request, $response); |
| 261 | + } |
| 262 | + |
| 263 | + // did not return a promise? this is an error, convert into one for rejection below. |
| 264 | + if (!$response instanceof PromiseInterface) { |
| 265 | + $response = Promise\resolve($response); |
| 266 | + } |
| 267 | + |
246 | 268 | $that = $this; |
247 | | - $promise->then( |
| 269 | + $response->then( |
248 | 270 | function ($response) use ($that, $conn, $request) { |
249 | 271 | if (!$response instanceof ResponseInterface) { |
250 | 272 | $message = 'The response callback is expected to resolve with an object implementing Psr\Http\Message\ResponseInterface, but resolved with "%s" instead.'; |
@@ -272,13 +294,6 @@ function ($error) use ($that, $conn, $request) { |
272 | 294 | return $that->writeError($conn, 500, $request); |
273 | 295 | } |
274 | 296 | ); |
275 | | - |
276 | | - if ($contentLength === 0) { |
277 | | - // If Body is empty or Content-Length is 0 and won't emit further data, |
278 | | - // 'data' events from other streams won't be called anymore |
279 | | - $stream->emit('end'); |
280 | | - $stream->close(); |
281 | | - } |
282 | 297 | } |
283 | 298 |
|
284 | 299 | /** @internal */ |
|
0 commit comments