From 6113356d8d419d474fc38f3757e998900fe39f26 Mon Sep 17 00:00:00 2001 From: Enrico Zimuel Date: Wed, 30 May 2018 17:17:49 +0200 Subject: [PATCH 1/4] Support swoole_http_request --- src/ServerRequestFactory.php | 65 +++++++++++++++++++++++++++++++ test/ServerRequestFactoryTest.php | 31 +++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/src/ServerRequestFactory.php b/src/ServerRequestFactory.php index 57cdc167..5924f68f 100644 --- a/src/ServerRequestFactory.php +++ b/src/ServerRequestFactory.php @@ -9,7 +9,9 @@ use InvalidArgumentException; use Psr\Http\Message\UploadedFileInterface; +use RuntimeException; use stdClass; +use swoole_http_request; use UnexpectedValueException; use function array_change_key_case; @@ -99,6 +101,69 @@ public static function fromGlobals( ); } + /** + * Create a request from a Swoole request + * + * @param swoole_http_request $request + * @return ServerRequest + * @throws RuntimeException if Swoole is not installed + */ + + public static function fromSwoole(swoole_http_request $request) + { + if (! extension_loaded('swoole')) { + throw new Exception\RuntimeException('Swoole extension is not installed!'); + } + $get = $request->get ?? []; + $post = $request->post ?? []; + $cookie = $request->cookie ?? []; + $files = $request->files ?? []; + + $server = [ + 'REQUEST_METHOD' => $request->server['request_method'], + 'REQUEST_URI' => $request->server['request_uri'], + 'PATH_INFO' => $request->server['path_info'], + 'REQUEST_TIME' => $request->server['request_time'], + 'GATEWAY_INTERFACE' => 'swoole/' . SWOOLE_VERSION, + // Server + 'SERVER_PROTOCOL' => $request->header['server_protocol'] ?? $request->server['server_protocol'], + 'REQUEST_SCHEMA' => $request->header['request_scheme'] ?? + explode('/', $request->server['server_protocol'])[0], + 'SERVER_NAME' => $request->header['server_name'] ?? $request->header['host'], + 'SERVER_ADDR' => $request->header['host'], + 'SERVER_PORT' => $request->header['server_port'] ?? $request->server['server_port'], + 'REMOTE_ADDR' => $request->server['remote_addr'] ?? $request->header['host'], + 'REMOTE_PORT' => $request->header['remote_port'] ?? $request->server['remote_port'], + 'QUERY_STRING' => $request->server['query_string'] ?? '', + // Headers + 'HTTP_HOST' => $request->header['host'], + 'HTTP_USER_AGENT' => $request->header['user-agent'] ?? '', + 'HTTP_ACCEPT' => $request->header['accept'] ?? '*/*', + 'HTTP_ACCEPT_LANGUAGE' => $request->header['accept-language'] ?? '', + 'HTTP_ACCEPT_ENCODING' => $request->header['accept-encoding'] ?? '', + 'HTTP_CONNECTION' => $request->header['connection'] ?? '', + 'HTTP_CACHE_CONTROL' => $request->header['cache-control'] ?? '', + ]; + + $headers = []; + foreach ($request->header as $name => $value) { + $headers[str_replace('-', '_', $name)] = $value; + } + + return new ServerRequest( + $server, + static::normalizeFiles($files), + static::marshalUriFromServer($server, $headers), + $server['REQUEST_METHOD'], + $request->rawContent(), + $headers, + $cookie, + $get, + $post, + static::marshalProtocolVersion($server) + ); + } + /** * Access a value in an array, returning a default value if not found * diff --git a/test/ServerRequestFactoryTest.php b/test/ServerRequestFactoryTest.php index 63f202e1..36b9f06b 100644 --- a/test/ServerRequestFactoryTest.php +++ b/test/ServerRequestFactoryTest.php @@ -10,6 +10,7 @@ use PHPUnit\Framework\TestCase; use ReflectionMethod; use ReflectionProperty; +use swoole_http_request; use UnexpectedValueException; use Zend\Diactoros\ServerRequest; use Zend\Diactoros\ServerRequestFactory; @@ -580,4 +581,34 @@ public function marshalProtocolVersionProvider() 'HTTP/2' => ['HTTP/2', '2'], ]; } + + public function testFromSwoole() + { + if (! extension_loaded('swoole')) { + $this->markTestSkipped('The Swoole extesion is not available'); + } + + $swooleRequest = $this->createMock(swoole_http_request::class); + $swooleRequest->header['host'] = 'localhost:9501'; + $swooleRequest->server['request_method'] = 'GET'; + $swooleRequest->server['request_uri'] = '/'; + $swooleRequest->server['path_info'] = '/'; + $swooleRequest->server['request_time'] = time(); + $swooleRequest->server['server_protocol'] = 'HTTP/1.1'; + $swooleRequest->server['server_port'] = 9501; + $swooleRequest->server['remote_port'] = 45314; + $swooleRequest->method('rawContent')->willReturn('php://input'); + + $request = ServerRequestFactory::fromSwoole($swooleRequest); + $this->assertInstanceOf(ServerRequest::class, $request); + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('/', $request->getRequestTarget()); + $this->assertEquals('/', $request->getUri()->getPath()); + $this->assertEquals('1.1', $request->getProtocolVersion()); + $this->assertEquals('', $request->getBody()); + $this->assertEquals([], $request->getCookieParams()); + $this->assertEquals([], $request->getQueryParams()); + $this->assertEquals([], $request->getUploadedFiles()); + $this->assertEquals(['host' => ['localhost:9501']], $request->getHeaders()); + } } From a51f767f97f51912acddec472ab98f9115c22020 Mon Sep 17 00:00:00 2001 From: Enrico Zimuel Date: Wed, 30 May 2018 17:48:39 +0200 Subject: [PATCH 2/4] Remove ?? usage for PHP 5.6 support --- src/ServerRequestFactory.php | 47 +++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/src/ServerRequestFactory.php b/src/ServerRequestFactory.php index 5924f68f..ec81fc9c 100644 --- a/src/ServerRequestFactory.php +++ b/src/ServerRequestFactory.php @@ -114,10 +114,10 @@ public static function fromSwoole(swoole_http_request $request) if (! extension_loaded('swoole')) { throw new Exception\RuntimeException('Swoole extension is not installed!'); } - $get = $request->get ?? []; - $post = $request->post ?? []; - $cookie = $request->cookie ?? []; - $files = $request->files ?? []; + $get = isset($request->get) ? $request->get : []; + $post = isset($request->post) ? $request->post : []; + $cookie = isset($request->cookie) ? $request->cookie : []; + $files = isset($request->files) ? $request->files : []; $server = [ 'REQUEST_METHOD' => $request->server['request_method'], @@ -126,23 +126,36 @@ public static function fromSwoole(swoole_http_request $request) 'REQUEST_TIME' => $request->server['request_time'], 'GATEWAY_INTERFACE' => 'swoole/' . SWOOLE_VERSION, // Server - 'SERVER_PROTOCOL' => $request->header['server_protocol'] ?? $request->server['server_protocol'], - 'REQUEST_SCHEMA' => $request->header['request_scheme'] ?? + 'SERVER_PROTOCOL' => isset($request->header['server_protocol']) ? + $request->header['server_protocol'] : $request->server['server_protocol'], + 'REQUEST_SCHEMA' => isset($request->header['request_scheme']) ? + $request->header['request_scheme'] : explode('/', $request->server['server_protocol'])[0], - 'SERVER_NAME' => $request->header['server_name'] ?? $request->header['host'], + 'SERVER_NAME' => isset($request->header['server_name']) ? + $request->header['server_name'] : $request->header['host'], 'SERVER_ADDR' => $request->header['host'], - 'SERVER_PORT' => $request->header['server_port'] ?? $request->server['server_port'], - 'REMOTE_ADDR' => $request->server['remote_addr'] ?? $request->header['host'], - 'REMOTE_PORT' => $request->header['remote_port'] ?? $request->server['remote_port'], - 'QUERY_STRING' => $request->server['query_string'] ?? '', + 'SERVER_PORT' => isset($request->header['server_port']) ? + $request->header['server_port'] : $request->server['server_port'], + 'REMOTE_ADDR' => isset($request->server['remote_addr']) ? + $request->server['remote_addr'] : $request->header['host'], + 'REMOTE_PORT' => isset($request->header['remote_port']) ? + $request->header['remote_port'] : $request->server['remote_port'], + 'QUERY_STRING' => isset($request->server['query_string']) ? + $request->server['query_string'] : '', // Headers 'HTTP_HOST' => $request->header['host'], - 'HTTP_USER_AGENT' => $request->header['user-agent'] ?? '', - 'HTTP_ACCEPT' => $request->header['accept'] ?? '*/*', - 'HTTP_ACCEPT_LANGUAGE' => $request->header['accept-language'] ?? '', - 'HTTP_ACCEPT_ENCODING' => $request->header['accept-encoding'] ?? '', - 'HTTP_CONNECTION' => $request->header['connection'] ?? '', - 'HTTP_CACHE_CONTROL' => $request->header['cache-control'] ?? '', + 'HTTP_USER_AGENT' => isset($request->header['user-agent']) ? + $request->header['user-agent'] : '', + 'HTTP_ACCEPT' => isset($request->header['accept']) ? + $request->header['accept'] : '*/*', + 'HTTP_ACCEPT_LANGUAGE' => isset($request->header['accept-language']) ? + $request->header['accept-language'] : '', + 'HTTP_ACCEPT_ENCODING' => isset($request->header['accept-encoding']) ? + $request->header['accept-encoding'] : '', + 'HTTP_CONNECTION' => isset($request->header['connection']) ? + $request->header['connection'] : '', + 'HTTP_CACHE_CONTROL' => isset($request->header['cache-control']) ? + $request->header['cache-control'] : '', ]; $headers = []; From 6ffa7f651075674b2e4495ebc47f9bda4b8851b0 Mon Sep 17 00:00:00 2001 From: Enrico Zimuel Date: Thu, 31 May 2018 17:17:56 +0200 Subject: [PATCH 3/4] Reduced the params conversion from Swoole to PSR-7 --- src/ServerRequestFactory.php | 42 ++++-------------------------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/src/ServerRequestFactory.php b/src/ServerRequestFactory.php index ec81fc9c..ecabc070 100644 --- a/src/ServerRequestFactory.php +++ b/src/ServerRequestFactory.php @@ -119,44 +119,10 @@ public static function fromSwoole(swoole_http_request $request) $cookie = isset($request->cookie) ? $request->cookie : []; $files = isset($request->files) ? $request->files : []; - $server = [ - 'REQUEST_METHOD' => $request->server['request_method'], - 'REQUEST_URI' => $request->server['request_uri'], - 'PATH_INFO' => $request->server['path_info'], - 'REQUEST_TIME' => $request->server['request_time'], - 'GATEWAY_INTERFACE' => 'swoole/' . SWOOLE_VERSION, - // Server - 'SERVER_PROTOCOL' => isset($request->header['server_protocol']) ? - $request->header['server_protocol'] : $request->server['server_protocol'], - 'REQUEST_SCHEMA' => isset($request->header['request_scheme']) ? - $request->header['request_scheme'] : - explode('/', $request->server['server_protocol'])[0], - 'SERVER_NAME' => isset($request->header['server_name']) ? - $request->header['server_name'] : $request->header['host'], - 'SERVER_ADDR' => $request->header['host'], - 'SERVER_PORT' => isset($request->header['server_port']) ? - $request->header['server_port'] : $request->server['server_port'], - 'REMOTE_ADDR' => isset($request->server['remote_addr']) ? - $request->server['remote_addr'] : $request->header['host'], - 'REMOTE_PORT' => isset($request->header['remote_port']) ? - $request->header['remote_port'] : $request->server['remote_port'], - 'QUERY_STRING' => isset($request->server['query_string']) ? - $request->server['query_string'] : '', - // Headers - 'HTTP_HOST' => $request->header['host'], - 'HTTP_USER_AGENT' => isset($request->header['user-agent']) ? - $request->header['user-agent'] : '', - 'HTTP_ACCEPT' => isset($request->header['accept']) ? - $request->header['accept'] : '*/*', - 'HTTP_ACCEPT_LANGUAGE' => isset($request->header['accept-language']) ? - $request->header['accept-language'] : '', - 'HTTP_ACCEPT_ENCODING' => isset($request->header['accept-encoding']) ? - $request->header['accept-encoding'] : '', - 'HTTP_CONNECTION' => isset($request->header['connection']) ? - $request->header['connection'] : '', - 'HTTP_CACHE_CONTROL' => isset($request->header['cache-control']) ? - $request->header['cache-control'] : '', - ]; + $server = []; + foreach ($request->server as $key => $value) { + $server[strtoupper($key)] = $value; + } $headers = []; foreach ($request->header as $name => $value) { From 08a46aa9ac1ded059f26c86f7f479f8dc03be403 Mon Sep 17 00:00:00 2001 From: Enrico Zimuel Date: Tue, 5 Jun 2018 17:35:32 +0200 Subject: [PATCH 4/4] Added Swoole emitter --- src/Response/SwooleEmitter.php | 84 ++++++++++++++++++++++++++++ test/Response/SwooleEmitterTest.php | 87 +++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 src/Response/SwooleEmitter.php create mode 100644 test/Response/SwooleEmitterTest.php diff --git a/src/Response/SwooleEmitter.php b/src/Response/SwooleEmitter.php new file mode 100644 index 00000000..431501e3 --- /dev/null +++ b/src/Response/SwooleEmitter.php @@ -0,0 +1,84 @@ +swooleResponse = $swooleResponse; + } + + /** + * Emits a response for the Swoole environment. + * + * @param ResponseInterface $response + */ + public function emit(ResponseInterface $response) + { + $this->emitStatusCode($response); + $this->emitHeaders($response); + $this->emitBody($response); + } + + /** + * Emit the message body. + * + * @param ResponseInterface $response + */ + private function emitBody(ResponseInterface $response) + { + $body = $response->getBody(); + $body->rewind(); + if ($body->getSize() > static::CHUNK_SIZE) { + while (! $body->eof()) { + $this->swooleResponse->write($body->read(static::CHUNK_SIZE)); + } + $this->swooleResponse->end(); + } else { + $this->swooleResponse->end($body->getContents()); + } + } + + /** + * Emit the headers + * + * @param ResponseInterface $response + */ + private function emitHeaders(ResponseInterface $response) + { + foreach ($response->getHeaders() as $name => $values) { + $name = $this->filterHeader($name); + $this->swooleResponse->header($name, implode(', ', $values)); + } + } + + /** + * Emit the status code + * + * @param ResponseInterface $response + */ + private function emitStatusCode(ResponseInterface $response) + { + $this->swooleResponse->status($response->getStatusCode()); + } +} diff --git a/test/Response/SwooleEmitterTest.php b/test/Response/SwooleEmitterTest.php new file mode 100644 index 00000000..7218540f --- /dev/null +++ b/test/Response/SwooleEmitterTest.php @@ -0,0 +1,87 @@ +swooleResponse = $this->prophesize(swoole_http_response::class); + $this->emitter = new SwooleEmitter($this->swooleResponse->reveal()); + } + + public function testEmit() + { + $response = (new Response()) + ->withStatus(200) + ->withAddedHeader('Content-Type', 'text/plain'); + $response->getBody()->write('Content!'); + + $this->emitter->emit($response); + + $this->swooleResponse->status(200)->shouldHaveBeenCalled(); + $this->swooleResponse->header('Content-Type', 'text/plain') + ->shouldHaveBeenCalled(); + $this->swooleResponse->end('Content!')->shouldHaveBeenCalled(); + } + + public function testMultipleHeaders() + { + $response = (new Response()) + ->withStatus(200) + ->withHeader('Content-Type', 'text/plain') + ->withHeader('Content-Length', '256'); + + $this->emitter->emit($response); + + $this->swooleResponse->status(200)->shouldHaveBeenCalled(); + $this->swooleResponse->header('Content-Type', 'text/plain') + ->shouldHaveBeenCalled(); + $this->swooleResponse->header('Content-Length', '256') + ->shouldHaveBeenCalled(); + } + + public function testMultipleSetCookieHeaders() + { + $response = (new Response()) + ->withStatus(200) + ->withAddedHeader('Set-Cookie', 'foo=bar') + ->withAddedHeader('Set-Cookie', 'bar=baz'); + + $this->emitter->emit($response); + + $this->swooleResponse->status(200)->shouldHaveBeenCalled(); + $this->swooleResponse->header('Set-Cookie', 'foo=bar, bar=baz') + ->shouldHaveBeenCalled(); + } + + public function testEmitWithBigContentBody() + { + $content = base64_encode(random_bytes(SwooleEmitter::CHUNK_SIZE)); // CHUNK_SIZE * 1.33333 + $response = (new Response()) + ->withStatus(200) + ->withAddedHeader('Content-Type', 'text/plain'); + $response->getBody()->write($content); + + $this->emitter->emit($response); + + $this->swooleResponse->status(200)->shouldHaveBeenCalled(); + $this->swooleResponse->header('Content-Type', 'text/plain') + ->shouldHaveBeenCalled(); + $this->swooleResponse->write(substr($content, 0, SwooleEmitter::CHUNK_SIZE)) + ->shouldHaveBeenCalled(); + $this->swooleResponse->write(substr($content, SwooleEmitter::CHUNK_SIZE)) + ->shouldHaveBeenCalled(); + $this->swooleResponse->end()->shouldHaveBeenCalled(); + } +}