From f4f4b4b1aeb59e23cce52b12d2c062940ce97d88 Mon Sep 17 00:00:00 2001 From: yinheli Date: Tue, 24 Sep 2024 09:50:00 +0800 Subject: [PATCH] Dynamic Access-Control-Expose-Headers Support - Update CorsService to merge existing exposed headers with configured - Add deduplication for exposed headers https://github.com/fruitcake/php-cors/issues/29 --- src/CorsService.php | 11 ++++++++++- tests/CorsTest.php | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/CorsService.php b/src/CorsService.php index 35a3f73..ae1b7e8 100644 --- a/src/CorsService.php +++ b/src/CorsService.php @@ -260,8 +260,17 @@ private function configureAllowCredentials(Response $response, Request $request) private function configureExposedHeaders(Response $response, Request $request): void { + $existingExposedHeaders = $response->headers->get('Access-Control-Expose-Headers'); + $exposedHeaders = $existingExposedHeaders ? array_map('trim', explode(',', $existingExposedHeaders)) : []; + if ($this->exposedHeaders) { - $response->headers->set('Access-Control-Expose-Headers', implode(', ', $this->exposedHeaders)); + $exposedHeaders = array_merge($this->exposedHeaders, $exposedHeaders); + } + + $uniqueExposedHeaders = array_unique(array_filter($exposedHeaders)); + + if (!empty($uniqueExposedHeaders)) { + $response->headers->set('Access-Control-Expose-Headers', implode(', ', $uniqueExposedHeaders)); } } diff --git a/tests/CorsTest.php b/tests/CorsTest.php index 8838a32..3634ebb 100644 --- a/tests/CorsTest.php +++ b/tests/CorsTest.php @@ -535,6 +535,43 @@ public function itDoesntSetAccessControlAllowOriginWithoutOrigin(): void $this->assertFalse($response->headers->has('Access-Control-Allow-Origin')); } + /** + * @test + */ + public function itConfiguresExposedHeadersWhenResponseHasNoExistingHeaders(): void + { + $app = $this->createStackedApp([], [ + 'Access-Control-Expose-Headers' => 'X-Custom-1, X-Custom-2', + ]); + $request = $this->createValidActualRequest(); + + $response = $app->handle($request); + + $this->assertTrue($response->headers->has('Access-Control-Expose-Headers')); + $this->assertEquals('X-Custom-1, X-Custom-2', $response->headers->get('Access-Control-Expose-Headers')); + } + + /** + * @test + */ + public function itMergesExposedHeadersWhenResponseHasExistingHeaders(): void + { + $app = $this->createStackedApp( + [ + 'exposedHeaders' => ['X-Option-1', 'X-Option-2'], + ], + [ + 'Access-Control-Expose-Headers' => 'X-Custom-1', + ] + ); + $request = $this->createValidActualRequest(); + + $result = $app->handle($request); + + $this->assertTrue($result->headers->has('Access-Control-Expose-Headers')); + $this->assertEquals('X-Option-1, X-Option-2, X-Custom-1', $result->headers->get('Access-Control-Expose-Headers')); + } + private function createValidActualRequest(): Request { $request = new Request();