@@ -107,7 +107,7 @@ final class RedirectPlugin implements Plugin
107
107
* Configuration options:
108
108
* - preserve_header: True keeps all headers, false remove all of them, an array is interpreted as a list of header names to keep
109
109
* - use_default_for_multiple: Whether the location header must be directly used for a multiple redirection status code (300)
110
- * - strict: When true, redirect codes 300, 301, 302 will not modify request method and body.
110
+ * - strict: When true, redirect codes 300, 301, 302 will not modify request method and body
111
111
*/
112
112
public function __construct (array $ config = [])
113
113
{
@@ -226,13 +226,39 @@ private function createUri(ResponseInterface $redirectResponse, RequestInterface
226
226
$ location = $ redirectResponse ->getHeaderLine ('Location ' );
227
227
$ parsedLocation = parse_url ($ location );
228
228
229
- if (false === $ parsedLocation ) {
230
- throw new HttpException (sprintf ('Location %s could not be parsed ' , $ location ), $ originalRequest , $ redirectResponse );
229
+ if (false === $ parsedLocation || '' === $ location ) {
230
+ throw new HttpException (sprintf ('Location "%s" could not be parsed ' , $ location ), $ originalRequest , $ redirectResponse );
231
231
}
232
232
233
233
$ uri = $ originalRequest ->getUri ();
234
+
235
+ // Redirections can either use an absolute uri or a relative reference https://www.rfc-editor.org/rfc/rfc3986#section-4.2
236
+ // If relative, we need to check if we have an absolute path or not
237
+
238
+ $ path = array_key_exists ('path ' , $ parsedLocation ) ? $ parsedLocation ['path ' ] : '' ;
239
+ if (!array_key_exists ('host ' , $ parsedLocation ) && '/ ' !== $ location [0 ]) {
240
+ // the target is a relative-path reference, we need to merge it with the base path
241
+ $ originalPath = $ uri ->getPath ();
242
+ if ('' === $ path ) {
243
+ $ path = $ originalPath ;
244
+ } elseif (($ pos = strrpos ($ originalPath , '/ ' )) !== false ) {
245
+ $ path = substr ($ originalPath , 0 , $ pos + 1 ).$ path ;
246
+ } else {
247
+ $ path = '/ ' .$ path ;
248
+ }
249
+ /* replace '/./' or '/foo/../' with '/' */
250
+ $ re = ['#(/\./)# ' , '#/(?!\.\.)[^/]+/\.\./# ' ];
251
+ for ($ n = 1 ; $ n > 0 ; $ path = preg_replace ($ re , '/ ' , $ path , -1 , $ n )) {
252
+ if (null === $ path ) {
253
+ throw new HttpException (sprintf ('Failed to resolve Location %s ' , $ location ), $ originalRequest , $ redirectResponse );
254
+ }
255
+ }
256
+ }
257
+ if (null === $ path ) {
258
+ throw new HttpException (sprintf ('Failed to resolve Location %s ' , $ location ), $ originalRequest , $ redirectResponse );
259
+ }
234
260
$ uri = $ uri
235
- ->withPath (array_key_exists ( ' path ' , $ parsedLocation ) ? $ parsedLocation [ ' path ' ] : '' )
261
+ ->withPath ($ path )
236
262
->withQuery (array_key_exists ('query ' , $ parsedLocation ) ? $ parsedLocation ['query ' ] : '' )
237
263
->withFragment (array_key_exists ('fragment ' , $ parsedLocation ) ? $ parsedLocation ['fragment ' ] : '' )
238
264
;
@@ -247,6 +273,8 @@ private function createUri(ResponseInterface $redirectResponse, RequestInterface
247
273
248
274
if (array_key_exists ('port ' , $ parsedLocation )) {
249
275
$ uri = $ uri ->withPort ($ parsedLocation ['port ' ]);
276
+ } elseif (array_key_exists ('host ' , $ parsedLocation )) {
277
+ $ uri = $ uri ->withPort (null );
250
278
}
251
279
252
280
return $ uri ;
0 commit comments