From e6a29c747662e1f646859bfec16a2f1361371ab9 Mon Sep 17 00:00:00 2001 From: Daniel Schreiber Date: Wed, 13 Mar 2024 14:22:44 +0100 Subject: [PATCH 1/2] fix: handle encoded slashes for url parameters - fixes #552 --- flight/net/Route.php | 13 ++++++++++--- flight/net/Router.php | 3 +-- tests/RouterTest.php | 12 +++++++++++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/flight/net/Route.php b/flight/net/Route.php index 22c3cf0..4abf2ae 100644 --- a/flight/net/Route.php +++ b/flight/net/Route.php @@ -97,7 +97,7 @@ class Route /** * Checks if a URL matches the route pattern. Also parses named parameters in the URL. * - * @param string $url Requested URL + * @param string $url Requested URL (original format, not URL decoded) * @param bool $case_sensitive Case sensitive matching * * @return bool Match status @@ -127,11 +127,18 @@ class Route } } - $this->splat = strval(substr($url, $i + 1)); + $this->splat = urldecode(strval(substr($url, $i + 1))); } // Build the regex for matching - $regex = str_replace([')', '/*'], [')?', '(/?|/.*?)'], $this->pattern); + $pattern_utf_chars_encoded = preg_replace_callback( + '#(\\p{L}+)#u', + static function ($matches) { + return urlencode($matches[0]); + }, + $this->pattern + ); + $regex = str_replace([')', '/*'], [')?', '(/?|/.*?)'], $pattern_utf_chars_encoded); $regex = preg_replace_callback( '#@([\w]+)(:([^/\(\)]*))?#', diff --git a/flight/net/Router.php b/flight/net/Router.php index 346a791..f739a24 100644 --- a/flight/net/Router.php +++ b/flight/net/Router.php @@ -215,9 +215,8 @@ class Router */ public function route(Request $request) { - $url_decoded = urldecode($request->url); while ($route = $this->current()) { - if ($route->matchMethod($request->method) && $route->matchUrl($url_decoded, $this->case_sensitive)) { + if ($route->matchMethod($request->method) && $route->matchUrl($request->url, $this->case_sensitive)) { $this->executedRoute = $route; return $route; } diff --git a/tests/RouterTest.php b/tests/RouterTest.php index 1b52ae1..8ed272e 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -198,6 +198,16 @@ class RouterTest extends TestCase $this->check('123'); } + public function testUrlParametersWithEncodedSlash() + { + $this->router->map('/redirect/@id', function ($id) { + echo $id; + }); + $this->request->url = '/redirect/before%2Fafter'; + + $this->check('before/after'); + } + // Passing URL parameters matched with regular expression public function testRegExParameters() { @@ -390,7 +400,7 @@ class RouterTest extends TestCase $this->router->map('/категория/@name:[абвгдеёжзийклмнопрстуфхцчшщъыьэюя]+', function ($name) { echo $name; }); - $this->request->url = urlencode('/категория/цветя'); + $this->request->url = '/' . urlencode('категория') . '/' . urlencode('цветя'); $this->check('цветя'); } From 765887d388aff855cf4ffad8fef2df8ef3859025 Mon Sep 17 00:00:00 2001 From: Austin Collier Date: Fri, 15 Mar 2024 08:55:41 -0600 Subject: [PATCH 2/2] some additional testing --- tests/RouterTest.php | 62 +++++++++++++++++++++++++------ tests/server/LayoutMiddleware.php | 3 ++ tests/server/index.php | 15 ++++++++ 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/tests/RouterTest.php b/tests/RouterTest.php index 8ed272e..f18eaba 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -208,6 +208,57 @@ class RouterTest extends TestCase $this->check('before/after'); } + public function testUrlParametersWithRealSlash() + { + $this->router->map('/redirect/@id', function ($id) { + echo $id; + }); + $this->request->url = '/redirect/before/after'; + + $this->check('404'); + } + + public function testUrlParametersWithJapanese() + { + $this->router->map('/わたしはひとです', function () { + echo 'はい'; + }); + $this->request->url = '/わたしはひとです'; + + $this->check('はい'); + } + + public function testUrlParametersWithJapaneseAndParam() + { + $this->router->map('/わたしはひとです/@name', function ($name) { + echo $name; + }); + $this->request->url = '/'.urlencode('わたしはひとです').'/'.urlencode('ええ'); + + $this->check('ええ'); + } + + // Passing URL parameters matched with regular expression for a URL containing Cyrillic letters: + public function testRegExParametersCyrillic() + { + $this->router->map('/категория/@name:[абвгдеёжзийклмнопрстуфхцчшщъыьэюя]+', function ($name) { + echo $name; + }); + $this->request->url = '/'.urlencode('категория').'/'.urlencode('цветя'); + + $this->check('цветя'); + } + + public function testRegExOnlyCyrillicUrl() + { + $this->router->map('/категория/цветя', function () { + echo 'цветя'; + }); + $this->request->url = '/категория/цветя'; + + $this->check('цветя'); + } + // Passing URL parameters matched with regular expression public function testRegExParameters() { @@ -394,17 +445,6 @@ class RouterTest extends TestCase $this->check('404'); } - // Passing URL parameters matched with regular expression for a URL containing Cyrillic letters: - public function testRegExParametersCyrillic() - { - $this->router->map('/категория/@name:[абвгдеёжзийклмнопрстуфхцчшщъыьэюя]+', function ($name) { - echo $name; - }); - $this->request->url = '/' . urlencode('категория') . '/' . urlencode('цветя'); - - $this->check('цветя'); - } - public function testGetAndClearRoutes() { $this->router->map('/path1', [$this, 'ok']); diff --git a/tests/server/LayoutMiddleware.php b/tests/server/LayoutMiddleware.php index e0dacbc..b2e59bb 100644 --- a/tests/server/LayoutMiddleware.php +++ b/tests/server/LayoutMiddleware.php @@ -78,6 +78,9 @@ class LayoutMiddleware
  • Redirect
  • Stream
  • Overwrite Body
  • +
  • Slash in Param
  • +
  • UTF8 URL
  • +
  • UTF8 URL w/ Param
  • HTML; echo '
    '; diff --git a/tests/server/index.php b/tests/server/index.php index 040cbfc..d88ac85 100644 --- a/tests/server/index.php +++ b/tests/server/index.php @@ -124,6 +124,21 @@ Flight::group('', function () { Flight::route('/overwrite', function () { echo 'Route text: This route status is that it failed'; })->addMiddleware([new OverwriteBodyMiddleware()]); + + // Test 15: UTF8 Chars in url + Flight::route('/わたしはひとです', function () { + echo 'Route text: This route status is that it succeeded はい!!!'; + }); + + // Test 16: UTF8 Chars in url with utf8 params + Flight::route('/わたしはひとです/@name', function ($name) { + echo 'Route text: This route status is that it '.($name === 'ええ' ? 'succeeded' : 'failed').' URL Param: '.$name.''; + }); + + // Test 17: Slash in param + Flight::route('/redirect/@id', function ($id) { + echo 'Route text: This route status is that it '.($id === 'before/after' ? 'succeeded' : 'failed').' URL Param: '.$id.''; + }); }, [ new LayoutMiddleware() ]); // Test 9: JSON output (should not output any other html)