From 8609f14ac5ae0c67fa284421c57b3468ad59b83d Mon Sep 17 00:00:00 2001 From: DmitriiKoziuk Date: Tue, 8 Aug 2023 11:54:31 +0300 Subject: [PATCH 1/2] Sub request gets parent request headers --- src/swoole/src/SymfonyHttpBridge.php | 34 ++++++++++++++--- .../tests/Unit/SymfonyHttpBridgeTest.php | 38 ++++++++++++++++++- 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/swoole/src/SymfonyHttpBridge.php b/src/swoole/src/SymfonyHttpBridge.php index ca26c2e..19f34a3 100644 --- a/src/swoole/src/SymfonyHttpBridge.php +++ b/src/swoole/src/SymfonyHttpBridge.php @@ -21,18 +21,15 @@ final class SymfonyHttpBridge { public static function convertSwooleRequest(Request $request): SymfonyRequest { - $sfRequest = new SymfonyRequest( + return new SymfonyRequest( $request->get ?? [], $request->post ?? [], [], $request->cookie ?? [], $request->files ?? [], - array_change_key_case($request->server ?? [], CASE_UPPER), + self::buildServer($request), $request->rawContent() ); - $sfRequest->headers = new HeaderBag($request->header ?? []); - - return $sfRequest; } public static function reflectSymfonyResponse(SymfonyResponse $sfResponse, Response $response): void @@ -62,4 +59,31 @@ public static function reflectSymfonyResponse(SymfonyResponse $sfResponse, Respo $response->end($sfResponse->getContent()); } } + + /** + * We must add the headers to the server otherwise they will not be available in sub-requests + */ + private static function buildServer(Request $request): array + { + $serverHeaders = []; + if (true === is_array($request->header)) { + foreach ($request->header as $name => $value) { + $name = strtoupper($name); + + if ( + false === in_array($name, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5']) + && false === str_starts_with($name, 'HTTP_') + ) { + $name = sprintf('HTTP_%s', $name); + } + + $serverHeaders[$name] = $value; + } + } + + return array_merge( + array_change_key_case($request->server ?? [], CASE_UPPER), + $serverHeaders + ); + } } diff --git a/src/swoole/tests/Unit/SymfonyHttpBridgeTest.php b/src/swoole/tests/Unit/SymfonyHttpBridgeTest.php index b2ef8da..9b0b356 100644 --- a/src/swoole/tests/Unit/SymfonyHttpBridgeTest.php +++ b/src/swoole/tests/Unit/SymfonyHttpBridgeTest.php @@ -40,7 +40,7 @@ public function testThatSwooleRequestIsConverted(): void $sfRequest = SymfonyHttpBridge::convertSwooleRequest($request); - $this->assertSame(['REQUEST_METHOD' => 'post'], $sfRequest->server->all()); + $this->assertSame(['REQUEST_METHOD' => 'post', 'HTTP_CONTENT-TYPE' => 'application/json'], $sfRequest->server->all()); $this->assertSame(['content-type' => ['application/json']], $sfRequest->headers->all()); $this->assertSame(['foo' => 'cookie'], $sfRequest->cookies->all()); $this->assertSame(['foo' => 'get'], $sfRequest->query->all()); @@ -148,4 +148,40 @@ public function testStreamedResponseWillRespondWithOneChunkAtATime(): void SymfonyHttpBridge::reflectSymfonyResponse($sfResponse, $response); } + + public function testThatSubRequestGetsParentRequestHeaders(): void + { + $request = $this->createMock(Request::class); + $request->server = ['request_method' => 'post']; + $request->header = ['host' => 'example.com', 'content-type' => 'application/json']; + $request->cookie = ['foo' => 'cookie']; + $request->get = ['foo' => 'get']; + $request->post = ['foo' => 'post']; + $request->files = [ + 'foo' => [ + 'name' => 'file', + 'type' => 'image/png', + 'tmp_name' => '/tmp/file', + 'error' => UPLOAD_ERR_CANT_WRITE, + 'size' => 0, + ], + ]; + $request->expects(self::once())->method('rawContent')->willReturn('{"foo": "body"}'); + + $sfRequest = SymfonyHttpBridge::convertSwooleRequest($request); + + $this->assertSame(['host' => ['example.com'], 'content-type' => ['application/json']], $sfRequest->headers->all()); + + $sfSubRequest = SymfonyRequest::create( + '/some-url', + 'get', + [], + $sfRequest->cookies->all(), + [], + $sfRequest->server->all() + ); + + $this->assertSame('example.com', $sfSubRequest->headers->get('host')); + $this->assertSame('application/json', $sfSubRequest->headers->get('content-type')); + } } From 659f26c0605098e0b4c117b00f0e90c0f119077f Mon Sep 17 00:00:00 2001 From: DmitriiKoziuk Date: Wed, 9 Aug 2023 10:23:20 +0300 Subject: [PATCH 2/2] Improvements after learning how fpm and symfony work with headers --- src/swoole/src/SymfonyHttpBridge.php | 19 +++++---------- .../tests/Unit/SymfonyHttpBridgeTest.php | 23 +++++++++++++------ 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/swoole/src/SymfonyHttpBridge.php b/src/swoole/src/SymfonyHttpBridge.php index 19f34a3..8479099 100644 --- a/src/swoole/src/SymfonyHttpBridge.php +++ b/src/swoole/src/SymfonyHttpBridge.php @@ -5,7 +5,6 @@ use Swoole\Http\Request; use Swoole\Http\Response; use Symfony\Component\HttpFoundation\BinaryFileResponse; -use Symfony\Component\HttpFoundation\HeaderBag; use Symfony\Component\HttpFoundation\Request as SymfonyRequest; use Symfony\Component\HttpFoundation\Response as SymfonyResponse; use Symfony\Component\HttpFoundation\StreamedResponse; @@ -66,19 +65,13 @@ public static function reflectSymfonyResponse(SymfonyResponse $sfResponse, Respo private static function buildServer(Request $request): array { $serverHeaders = []; - if (true === is_array($request->header)) { - foreach ($request->header as $name => $value) { - $name = strtoupper($name); - - if ( - false === in_array($name, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5']) - && false === str_starts_with($name, 'HTTP_') - ) { - $name = sprintf('HTTP_%s', $name); - } - - $serverHeaders[$name] = $value; + /** Inspired by https://github.com/symfony/symfony/blob/85366b4767b1761f40701f4ea6692d5280e0d58d/src/Symfony/Component/HttpFoundation/Request.php#L546-L553 */ + foreach ($request->header ?? [] as $name => $value) { + $name = strtoupper(str_replace('-', '_', $name)); + if (false === in_array($name, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5'])) { + $name = sprintf('HTTP_%s', $name); } + $serverHeaders[$name] = $value; } return array_merge( diff --git a/src/swoole/tests/Unit/SymfonyHttpBridgeTest.php b/src/swoole/tests/Unit/SymfonyHttpBridgeTest.php index 9b0b356..7d73680 100644 --- a/src/swoole/tests/Unit/SymfonyHttpBridgeTest.php +++ b/src/swoole/tests/Unit/SymfonyHttpBridgeTest.php @@ -23,7 +23,7 @@ public function testThatSwooleRequestIsConverted(): void { $request = $this->createMock(Request::class); $request->server = ['request_method' => 'post']; - $request->header = ['content-type' => 'application/json']; + $request->header = ['content-type' => 'application/json', 'x-upstream-use' => 'swoole', 'http-upstream' => 'swoole']; $request->cookie = ['foo' => 'cookie']; $request->get = ['foo' => 'get']; $request->post = ['foo' => 'post']; @@ -40,8 +40,17 @@ public function testThatSwooleRequestIsConverted(): void $sfRequest = SymfonyHttpBridge::convertSwooleRequest($request); - $this->assertSame(['REQUEST_METHOD' => 'post', 'HTTP_CONTENT-TYPE' => 'application/json'], $sfRequest->server->all()); - $this->assertSame(['content-type' => ['application/json']], $sfRequest->headers->all()); + $this->assertSame([ + 'REQUEST_METHOD' => 'post', + 'CONTENT_TYPE' => 'application/json', + 'HTTP_X_UPSTREAM_USE' => 'swoole', + 'HTTP_HTTP_UPSTREAM' => 'swoole', + ], $sfRequest->server->all()); + $this->assertSame([ + 'content-type' => ['application/json'], + 'x-upstream-use' => ['swoole'], + 'http-upstream' => ['swoole'], + ], $sfRequest->headers->all()); $this->assertSame(['foo' => 'cookie'], $sfRequest->cookies->all()); $this->assertSame(['foo' => 'get'], $sfRequest->query->all()); $this->assertSame(['foo' => 'post'], $sfRequest->request->all()); @@ -153,7 +162,7 @@ public function testThatSubRequestGetsParentRequestHeaders(): void { $request = $this->createMock(Request::class); $request->server = ['request_method' => 'post']; - $request->header = ['host' => 'example.com', 'content-type' => 'application/json']; + $request->header = ['host' => 'example.com', 'content-type' => 'application/json', 'x-upstream-use' => 'swoole', 'http-upstream' => 'swoole']; $request->cookie = ['foo' => 'cookie']; $request->get = ['foo' => 'get']; $request->post = ['foo' => 'post']; @@ -166,12 +175,10 @@ public function testThatSubRequestGetsParentRequestHeaders(): void 'size' => 0, ], ]; - $request->expects(self::once())->method('rawContent')->willReturn('{"foo": "body"}'); $sfRequest = SymfonyHttpBridge::convertSwooleRequest($request); - $this->assertSame(['host' => ['example.com'], 'content-type' => ['application/json']], $sfRequest->headers->all()); - + // Simulating creating sub-request $sfSubRequest = SymfonyRequest::create( '/some-url', 'get', @@ -183,5 +190,7 @@ public function testThatSubRequestGetsParentRequestHeaders(): void $this->assertSame('example.com', $sfSubRequest->headers->get('host')); $this->assertSame('application/json', $sfSubRequest->headers->get('content-type')); + $this->assertSame('swoole', $sfSubRequest->headers->get('x-upstream-use')); + $this->assertSame('swoole', $sfSubRequest->headers->get('http-upstream')); } }