Skip to content

Commit

Permalink
Adding PSR RequestHandlerInterface for Swoole
Browse files Browse the repository at this point in the history
  • Loading branch information
leocavalcante authored and Nyholm committed Jul 17, 2021
1 parent 834b864 commit c3b4c34
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 1 deletion.
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,30 @@ return function () {
};
```

### PSR

```php
// public/index.php

use Nyholm\Psr7\Response;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

require_once dirname(__DIR__).'/vendor/autoload_runtime.php';

class App implements RequestHandlerInterface {
public function handle(ServerRequestInterface $request): ResponseInterface {
$name = $request->getQueryParams()['name'] ?? 'World';
return new Response(200, ['Server' => 'swoole-runtime'], "Hello, $name!");
}
}

return function(): RequestHandlerInterface {
return new App();
};
```

### Symfony

```php
Expand Down
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
}
],
"require": {
"nyholm/psr7": "^1.4",
"psr/http-server-handler": "^1.0",
"symfony/runtime": "^5.3 || ^6.0"
},
"require-dev": {
Expand Down
81 changes: 81 additions & 0 deletions src/RequestHandlerRunner.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

namespace Runtime\Swoole;

use Psr\Http\Server\RequestHandlerInterface;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Symfony\Component\Runtime\RunnerInterface;

class RequestHandlerRunner implements RunnerInterface
{
/**
* @var int
*/
private const CHUNK_SIZE = 2097152; // 2MB
/**
* @var ServerFactory
*/
private $serverFactory;
/**
* @var RequestHandlerInterface
*/
private $application;

public function __construct(ServerFactory $serverFactory, RequestHandlerInterface $application)
{
$this->serverFactory = $serverFactory;
$this->application = $application;
}

public function run(): int
{
$this->serverFactory->createServer([$this, 'handle'])->start();

return 0;
}

public function handle(Request $request, Response $response): void
{
$psrRequest = (new \Nyholm\Psr7\ServerRequest(
$request->getMethod(),
$request->server['request_uri'] ?? '/',
array_change_key_case($request->server ?? [], CASE_UPPER),
$request->rawContent(),
'1.1',
$request->server ?? []
))
->withQueryParams($request->get ?? []);

$psrResponse = $this->application->handle($psrRequest);

$response->setStatusCode($psrResponse->getStatusCode(), $psrResponse->getReasonPhrase());

foreach ($psrResponse->getHeaders() as $name => $values) {
foreach ($values as $value) {
$response->setHeader($name, $value);
}
}

$body = $psrResponse->getBody();
$body->rewind();

if ($body->isReadable()) {
if ($body->getSize() <= self::CHUNK_SIZE) {
if ($contents = $body->getContents()) {
$response->write($contents);
}
} else {
while (!$body->eof() && ($contents = $body->read(self::CHUNK_SIZE))) {
$response->write($contents);
}
}

$response->end();
} else {
$response->end((string) $body);
}

$body->close();
}
}
5 changes: 5 additions & 0 deletions src/Runtime.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Runtime\Swoole;

use Illuminate\Contracts\Http\Kernel;
use Psr\Http\Server\RequestHandlerInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Runtime\RunnerInterface;
use Symfony\Component\Runtime\SymfonyRuntime;
Expand Down Expand Up @@ -37,6 +38,10 @@ public function getRunner(?object $application): RunnerInterface
return new LaravelRunner($this->serverFactory, $application);
}

if ($application instanceof RequestHandlerInterface) {
return new RequestHandlerRunner($this->serverFactory, $application);
}

return parent::getRunner($application);
}
}
3 changes: 2 additions & 1 deletion tests/E2E/StaticFileHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@

use PHPUnit\Framework\TestCase;
use function Swoole\Coroutine\Http\get;
use function Swoole\Coroutine\run;

class StaticFileHandlerTest extends TestCase
{
public function testSwooleServerHandlesStaticFiles(): void
{
\Co\run(static function (): void {
run(static function (): void {
self::assertSame("Static file\n", get('http://localhost:8001/file.txt')->getBody());
});
}
Expand Down
54 changes: 54 additions & 0 deletions tests/Unit/RequestHandlerRunnerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace Runtime\Swoole;

use Nyholm\Psr7\Stream;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\Http\Server;

class RequestHandlerRunnerTest extends TestCase
{
public function testRun(): void
{
$server = $this->createMock(Server::class);
$factory = $this->createMock(ServerFactory::class);
$application = $this->createMock(RequestHandlerInterface::class);

$factory->expects(self::once())->method('createServer')->willReturn($server);
$server->expects(self::once())->method('start');

$runner = new RequestHandlerRunner($factory, $application);

self::assertSame(0, $runner->run());
}

public function testHandle(): void
{
$factory = $this->createMock(ServerFactory::class);
$application = $this->createMock(RequestHandlerInterface::class);
$psrResponse = $this->createMock(ResponseInterface::class);
$request = $this->createMock(Request::class);
$response = $this->createMock(Response::class);

$request->expects(self::once())->method('getMethod')->willReturn('POST');
$request->expects(self::once())->method('rawContent')->willReturn('Test');

$application->expects(self::once())->method('handle')->willReturn($psrResponse);

$psrResponse->expects(self::once())->method('getHeaders')->willReturn([
'X-Test' => ['Swoole-Runtime'],
]);
$psrResponse->expects(self::once())->method('getBody')->willReturn(Stream::create('Test'));

$response->expects(self::once())->method('setHeader')->with('X-Test', 'Swoole-Runtime');
$response->expects(self::once())->method('write')->with('Test');
$response->expects(self::once())->method('end')->with(null);

$runner = new RequestHandlerRunner($factory, $application);
$runner->handle($request, $response);
}
}
13 changes: 13 additions & 0 deletions tests/Unit/RuntimeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

use Illuminate\Contracts\Http\Kernel;
use PHPUnit\Framework\TestCase;
use Psr\Http\Server\RequestHandlerInterface;
use Runtime\Swoole\CallableRunner;
use Runtime\Swoole\LaravelRunner;
use Runtime\Swoole\RequestHandlerRunner;
use Runtime\Swoole\Runtime;
use Runtime\Swoole\SymfonyRunner;
use Symfony\Component\HttpKernel\HttpKernelInterface;
Expand Down Expand Up @@ -47,6 +49,17 @@ public function testGetRunnerCreatesARunnerForLaravel(): void
self::assertInstanceOf(LaravelRunner::class, $runner);
}

public function testGetRunnerCreatesARunnerForRequestHandlers(): void
{
$options = [];
$runtime = new Runtime($options);

$application = $this->createMock(RequestHandlerInterface::class);
$runner = $runtime->getRunner($application);

self::assertInstanceOf(RequestHandlerRunner::class, $runner);
}

public function testGetRunnerFallbacksToClosureRunner(): void
{
$options = [];
Expand Down

0 comments on commit c3b4c34

Please sign in to comment.