diff --git a/README.md b/README.md index 9bad8ad4..a9b80583 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,31 @@ $loop->run(); ``` See also the [examples](examples). + +## Request body parsing example + +As of `v0.5` `react/http` supports request body parsing and comes with a handy factory: + +```php +$loop = React\EventLoop\Factory::create(); +$socket = new React\Socket\Server($loop); + +$http = new React\Http\Server($socket); +$http->on('request', function ($request, $response) { + $parser = React\Http\StreamingBodyParser\Factory::create($request); + $parser->on('body', function ($bodyString) {}); + $parser->on('post', function ($fieldName, $fileValue) {}); + $parser->on('file', function ($fieldName, $fileObject) {}); + $parser->on('end', function () use ($response) { + $response->writeHead(200, array('Content-Type' => 'text/plain')); + $response->end("Hello World!\n"); + }); +}); + +$socket->listen(1337); +$loop->run(); +``` + +### Current supported request body parsers + +* RawBodyParser - Emit raw body chunks as they come in, no parsing for `post` or `file`. This is the default parser. diff --git a/src/StreamingBodyParser/Factory.php b/src/StreamingBodyParser/Factory.php new file mode 100644 index 00000000..9a4d9fbe --- /dev/null +++ b/src/StreamingBodyParser/Factory.php @@ -0,0 +1,21 @@ +request = $request; + $this->request->on('data', [$this, 'body']); + $this->request->on('end', [$this, 'end']); + } + + /** + * @param string $data + * + * @internal + */ + public function body($data) + { + $this->emit('body', [$data]); + } + + /** + * @internal + */ + public function end() + { + $this->emit('end'); + } + + /** + * Cancel 'parsing' the request body + */ + public function cancel() + { + $this->request->removeListener('data', [$this, 'body']); + $this->request->removeListener('end', [$this, 'end']); + $this->emit('end'); + } +} diff --git a/tests/StreamingBodyParser/FactoryTest.php b/tests/StreamingBodyParser/FactoryTest.php new file mode 100644 index 00000000..138f1985 --- /dev/null +++ b/tests/StreamingBodyParser/FactoryTest.php @@ -0,0 +1,26 @@ + 123, + ]); + $parser = Factory::create($request); + $this->assertInstanceOf('React\Http\StreamingBodyParser\RawBodyParser', $parser); + } + + public function testNoHeaders() + { + $request = new Request('POST', 'http://example.com/'); + $parser = Factory::create($request); + $this->assertInstanceOf('React\Http\StreamingBodyParser\RawBodyParser', $parser); + } +} diff --git a/tests/StreamingBodyParser/RawBodyParserTest.php b/tests/StreamingBodyParser/RawBodyParserTest.php new file mode 100644 index 00000000..8202b9f8 --- /dev/null +++ b/tests/StreamingBodyParser/RawBodyParserTest.php @@ -0,0 +1,45 @@ + 3, + ]); + $parser = new RawBodyParser($request); + $parser->on('end', $this->expectCallableNever()); + $parser->on('body', function ($rawBody) use (&$body) { + $body = $rawBody; + }); + $request->emit('data', ['abc']); + $this->assertSame('abc', $body); + $request->emit('data', ['def']); + $this->assertSame('def', $body); + } + + public function testEndForward() + { + $request = new Request('POST', 'http://example.com/'); + $parser = new RawBodyParser($request); + $parser->on('end', $this->expectCallableOnce()); + $parser->on('body', $this->expectCallableNever()); + $request->emit('end'); + } + + public function testEndOnCancel() + { + $request = new Request('POST', 'http://example.com/'); + $parser = new RawBodyParser($request); + $parser->on('end', $this->expectCallableOnce()); + $parser->on('body', $this->expectCallableNever()); + $parser->cancel(); + } +}