39
39
use Neomerx \Cors \Analyzer as CorsAnalyzer ;
40
40
use Neomerx \Cors \Contracts \AnalysisResultInterface as CorsAnalysisResultInterface ;
41
41
use Neomerx \Cors \Contracts \Constants \CorsResponseHeaders ;
42
- use Psr \Http \Server \MiddlewareInterface ;
43
- use Psr \Http \Server \RequestHandlerInterface ;
44
42
use Psr \Http \Message \ResponseInterface ;
45
43
use Psr \Http \Message \ServerRequestInterface ;
44
+ use Psr \Http \Server \MiddlewareInterface ;
45
+ use Psr \Http \Server \RequestHandlerInterface ;
46
46
use Psr \Log \LoggerInterface ;
47
47
use Tuupola \Http \Factory \ResponseFactory ;
48
48
use Tuupola \Middleware \Settings as CorsSettings ;
56
56
* "origin.server"?: null|string|array<string>,
57
57
* cache?: int,
58
58
* error?: null|callable,
59
- * logger?: null|LoggerInterface
59
+ * logger?: null|LoggerInterface,
60
60
* }
61
61
*/
62
62
final class CorsMiddleware implements MiddlewareInterface
63
63
{
64
64
use DoublePassTrait;
65
65
66
- /**
67
- * @var LoggerInterface|null
68
- */
66
+ /** @var int */
67
+ private const PORT_HTTP = 80 ;
68
+
69
+ /** @var int */
70
+ private const PORT_HTTPS = 443 ;
71
+
72
+ /** @var LoggerInterface|null */
69
73
private $ logger ;
70
74
71
75
/**
@@ -78,7 +82,7 @@ final class CorsMiddleware implements MiddlewareInterface
78
82
* "origin.server": null|string|array<string>,
79
83
* cache: int,
80
84
* error: null|callable,
81
- * logger: null|LoggerInterface
85
+ * logger: null|LoggerInterface,
82
86
* }
83
87
*/
84
88
private $ options = [
@@ -102,7 +106,7 @@ final class CorsMiddleware implements MiddlewareInterface
102
106
* "origin.server"?: null|string|array<string>,
103
107
* cache?: int,
104
108
* error?: null|callable,
105
- * logger?: null|LoggerInterface
109
+ * logger?: null|LoggerInterface,
106
110
* } $options
107
111
*/
108
112
public function __construct (array $ options = [])
@@ -216,32 +220,33 @@ private function buildSettings(ServerRequestInterface $request, ResponseInterfac
216
220
{
217
221
$ settings = new CorsSettings ();
218
222
219
- $ origin = array_fill_keys ($ this ->options ["origin " ], true );
220
- $ settings ->setRequestAllowedOrigins ($ origin );
223
+ $ serverOrigin = $ this ->determineServerOrigin ();
224
+
225
+ $ settings ->init (
226
+ $ serverOrigin ['scheme ' ],
227
+ $ serverOrigin ['host ' ],
228
+ $ serverOrigin ['port ' ]
229
+ );
230
+
231
+ $ settings ->setAllowedOrigins ($ this ->options ["origin " ]);
221
232
222
233
if (is_callable ($ this ->options ["methods " ])) {
223
234
$ methods = (array ) $ this ->options ["methods " ]($ request , $ response );
224
235
} else {
225
236
$ methods = (array ) $ this ->options ["methods " ];
226
237
}
227
238
228
- $ methods = array_fill_keys ($ methods , true );
229
- $ settings ->setRequestAllowedMethods ($ methods );
230
-
231
- $ headers = array_fill_keys ($ this ->options ["headers.allow " ], true );
239
+ $ settings ->setAllowedMethods ($ methods );
232
240
233
241
/* transform all headers to lowercase */
234
- $ headers = array_change_key_case ($ headers );
235
-
236
- $ settings ->setRequestAllowedHeaders ($ headers );
242
+ $ headers = array_change_key_case ($ this ->options ["headers.allow " ]);
237
243
238
- $ headers = array_fill_keys ($ this ->options ["headers.expose " ], true );
239
- $ settings ->setResponseExposedHeaders ($ headers );
244
+ $ settings ->setAllowedHeaders ($ headers );
240
245
241
- $ settings ->setRequestCredentialsSupported ($ this ->options ["credentials " ]);
246
+ $ settings ->setExposedHeaders ($ this ->options ["headers.expose " ]);
242
247
243
- if (is_string ( $ this ->options ["origin.server " ]) ) {
244
- $ settings ->setServerOrigin ( $ this -> options [ " origin.server " ] );
248
+ if ($ this ->options ["credentials " ] ) {
249
+ $ settings ->setCredentialsSupported ( );
245
250
}
246
251
247
252
$ settings ->setPreFlightCacheMaxAge ($ this ->options ["cache " ]);
@@ -250,13 +255,48 @@ private function buildSettings(ServerRequestInterface $request, ResponseInterfac
250
255
}
251
256
252
257
/**
253
- * Edge cannot handle multiple Access-Control-Expose-Headers headers
258
+ * Try to determine the server origin uri fragments
259
+ *
260
+ * @return array{scheme: string, host: string, port: int}
261
+ */
262
+ private function determineServerOrigin (): array
263
+ {
264
+ // set some default
265
+ $ url = [
266
+ 'scheme ' => 'https ' ,
267
+ 'host ' => '' ,
268
+ 'port ' => self ::PORT_HTTPS ,
269
+ ];
270
+
271
+ // load details from server origin
272
+ if (is_string ($ this ->options ["origin.server " ])) {
273
+ /** @var false|array{scheme: string, host: string, port?: int} $url_chunks */
274
+ $ url_chunks = parse_url ($ this ->options ["origin.server " ]);
275
+ if ($ url_chunks !== false ) {
276
+ $ url = $ url_chunks ;
277
+ }
278
+
279
+ if (!array_key_exists ('port ' , $ url )) {
280
+ $ url ['port ' ] = $ url ['scheme ' ] === 'https ' ? self ::PORT_HTTPS : self ::PORT_HTTP ;
281
+ }
282
+ }
283
+
284
+ return $ url ;
285
+ }
286
+
287
+ /**
288
+ * Edge cannot handle Access-Control-Expose-Headers having a trailing whitespace after the comma
289
+ *
290
+ * @see https://github.com/tuupola/cors-middleware/issues/40
254
291
*/
255
292
private function fixHeaders (array $ headers ): array
256
293
{
257
294
if (isset ($ headers [CorsResponseHeaders::EXPOSE_HEADERS ])) {
258
- $ headers [CorsResponseHeaders::EXPOSE_HEADERS ] =
259
- implode (", " , $ headers [CorsResponseHeaders::EXPOSE_HEADERS ]);
295
+ $ headers [CorsResponseHeaders::EXPOSE_HEADERS ] = str_replace (
296
+ ' ' ,
297
+ '' ,
298
+ $ headers [CorsResponseHeaders::EXPOSE_HEADERS ]
299
+ );
260
300
}
261
301
262
302
return $ headers ;
0 commit comments