Skip to content

Commit 1472864

Browse files
authored
Merge pull request #4646 from MGatner/uri-internal
Internal URI handling
2 parents a4edd89 + 584bcd1 commit 1472864

File tree

5 files changed

+74
-58
lines changed

5 files changed

+74
-58
lines changed

system/HTTP/IncomingRequest.php

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use CodeIgniter\HTTP\Exceptions\HTTPException;
1515
use CodeIgniter\HTTP\Files\FileCollection;
1616
use CodeIgniter\HTTP\Files\UploadedFile;
17+
use CodeIgniter\HTTP\URI;
1718
use Config\App;
1819
use Config\Services;
1920
use InvalidArgumentException;
@@ -123,7 +124,7 @@ class IncomingRequest extends Request
123124
/**
124125
* Constructor
125126
*
126-
* @param object $config
127+
* @param App $config
127128
* @param URI $uri
128129
* @param string|null $body
129130
* @param UserAgent $userAgent
@@ -149,7 +150,7 @@ public function __construct($config, URI $uri = null, $body = 'php://input', Use
149150

150151
$this->populateHeaders();
151152

152-
// Get our current URI.
153+
// Determine the current URI
153154
// NOTE: This WILL NOT match the actual URL in the browser since for
154155
// everything this cares about (and the router, etc) is the portion
155156
// AFTER the script name. So, if hosted in a sub-folder this will
@@ -158,6 +159,12 @@ public function __construct($config, URI $uri = null, $body = 'php://input', Use
158159

159160
$this->detectURI($config->uriProtocol, $config->baseURL);
160161

162+
// Check if the baseURL scheme needs to be coerced into its secure version
163+
if ($config->forceGlobalSecureRequests && $this->uri->getScheme() === 'http')
164+
{
165+
$this->uri->setScheme('https');
166+
}
167+
161168
$this->validLocales = $config->supportedLocales;
162169

163170
$this->detectLocale($config);
@@ -610,11 +617,11 @@ protected function detectURI(string $protocol, string $baseURL)
610617

611618
// It's possible the user forgot a trailing slash on their
612619
// baseURL, so let's help them out.
613-
$baseURL = ! empty($baseURL) ? rtrim($baseURL, '/ ') . '/' : $baseURL;
620+
$baseURL = $baseURL === '' ? $baseURL : rtrim($baseURL, '/ ') . '/';
614621

615622
// Based on our baseURL provided by the developer
616623
// set our current domain name, scheme
617-
if (! empty($baseURL))
624+
if ($baseURL !== '')
618625
{
619626
$this->uri->setScheme(parse_url($baseURL, PHP_URL_SCHEME));
620627
$this->uri->setHost(parse_url($baseURL, PHP_URL_HOST));
@@ -758,12 +765,9 @@ protected function parseRequestURI(): string
758765

759766
parse_str($_SERVER['QUERY_STRING'], $_GET);
760767

761-
if ($uri === '/' || $uri === '')
762-
{
763-
return '/';
764-
}
768+
$uri = URI::removeDotSegments($uri);
765769

766-
return $this->removeRelativeDirectory($uri);
770+
return ($uri === '/' || $uri === '') ? '/' : ltrim($uri, '/');
767771
}
768772

769773
//--------------------------------------------------------------------
@@ -793,7 +797,9 @@ protected function parseQueryString(): string
793797

794798
parse_str($_SERVER['QUERY_STRING'], $_GET);
795799

796-
return $this->removeRelativeDirectory($uri);
800+
$uri = URI::removeDotSegments($uri);
801+
802+
return ($uri === '/' || $uri === '') ? '/' : ltrim($uri, '/');
797803
}
798804

799805
//--------------------------------------------------------------------
@@ -806,22 +812,13 @@ protected function parseQueryString(): string
806812
* @param string $uri
807813
*
808814
* @return string
815+
*
816+
* @deprecated Use URI::removeDotSegments() directly
809817
*/
810818
protected function removeRelativeDirectory(string $uri): string
811819
{
812-
$uris = [];
813-
$tok = strtok($uri, '/');
814-
while ($tok !== false)
815-
{
816-
if ((! empty($tok) || $tok === '0') && $tok !== '..')
817-
{
818-
$uris[] = $tok;
819-
}
820-
$tok = strtok('/');
821-
}
820+
$uri = URI::removeDotSegments($uri);
822821

823-
return implode('/', $uris);
822+
return $uri === '/' ? $uri : ltrim($uri, '/');
824823
}
825-
826-
// --------------------------------------------------------------------
827824
}

system/HTTP/URI.php

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
namespace CodeIgniter\HTTP;
1313

1414
use CodeIgniter\HTTP\Exceptions\HTTPException;
15-
use Config\App;
1615
use InvalidArgumentException;
1716

1817
/**
@@ -166,7 +165,7 @@ public static function createURIString(string $scheme = null, string $authority
166165
$uri .= $authority;
167166
}
168167

169-
if ($path !== '')
168+
if (isset($path) && $path !== '')
170169
{
171170
$uri .= substr($uri, -1, 1) !== '/' ? '/' . ltrim($path, '/') : ltrim($path, '/');
172171
}
@@ -465,7 +464,7 @@ public function showPassword(bool $val = true)
465464
*/
466465
public function getHost(): string
467466
{
468-
return $this->host;
467+
return $this->host ?? '';
469468
}
470469

471470
//--------------------------------------------------------------------
@@ -665,33 +664,45 @@ public function getTotalSegments(): int
665664
//--------------------------------------------------------------------
666665

667666
/**
668-
* Allow the URI to be output as a string by simply casting it to a string
669-
* or echoing out.
667+
* Formats the URI as a string.
668+
*
669+
* Warning: For backwards-compatability this method
670+
* assumes URIs with the same host as baseURL should
671+
* be relative to the project's configuration.
672+
* This aspect of __toString() is deprecated and should be avoided.
673+
*
674+
* @return string
670675
*/
671676
public function __toString(): string
672677
{
673-
// If hosted in a sub-folder, we will have additional
674-
// segments that show up prior to the URI path we just
675-
// grabbed from the request, so add it on if necessary.
676-
$config = config(App::class);
677-
$baseUri = new self($config->baseURL);
678-
$basePath = trim($baseUri->getPath(), '/') . '/';
679-
$path = $this->getPath();
680-
$trimPath = ltrim($path, '/');
681-
682-
if ($basePath !== '/' && strpos($trimPath, $basePath) !== 0)
683-
{
684-
$path = $basePath . $trimPath;
685-
}
678+
$path = $this->getPath();
679+
$scheme = $this->getScheme();
680+
681+
// Check if this is an internal URI
682+
$config = config('App');
683+
$baseUri = new self($config->baseURL);
686684

687-
// force https if needed
688-
if ($config->forceGlobalSecureRequests)
685+
// If the hosts matches then assume this should be relative to baseURL
686+
if ($this->getHost() === $baseUri->getHost())
689687
{
690-
$this->setScheme('https');
688+
// Check for additional segments
689+
$basePath = trim($baseUri->getPath(), '/') . '/';
690+
$trimPath = ltrim($path, '/');
691+
692+
if ($basePath !== '/' && strpos($trimPath, $basePath) !== 0)
693+
{
694+
$path = $basePath . $trimPath;
695+
}
696+
697+
// Check for forced HTTPS
698+
if ($config->forceGlobalSecureRequests)
699+
{
700+
$scheme = 'https';
701+
}
691702
}
692703

693704
return static::createURIString(
694-
$this->getScheme(), $this->getAuthority(), $path, // Absolute URIs should use a "/" for an empty path
705+
$scheme, $this->getAuthority(), $path, // Absolute URIs should use a "/" for an empty path
695706
$this->getQuery(), $this->getFragment()
696707
);
697708
}

tests/system/API/ResponseTraitTest.php

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use CodeIgniter\Test\CIUnitTestCase;
99
use CodeIgniter\Test\Mock\MockIncomingRequest;
1010
use CodeIgniter\Test\Mock\MockResponse;
11+
use Config\App;
1112
use stdClass;
1213

1314
class ResponseTraitTest extends CIUnitTestCase
@@ -30,7 +31,8 @@ protected function setUp(): void
3031

3132
protected function makeController(array $userConfig = [], string $uri = 'http://example.com', array $userHeaders = [])
3233
{
33-
$config = [
34+
$config = new App();
35+
foreach ([
3436
'baseURL' => 'http://example.com/',
3537
'uriProtocol' => 'REQUEST_URI',
3638
'defaultLocale' => 'en',
@@ -44,9 +46,10 @@ protected function makeController(array $userConfig = [], string $uri = 'http://
4446
'cookieHTTPOnly' => false,
4547
'proxyIPs' => [],
4648
'cookieSameSite' => 'Lax',
47-
];
48-
49-
$config = array_merge($config, $userConfig);
49+
] as $key => $value)
50+
{
51+
$config->$key = $value;
52+
}
5053

5154
if (is_null($this->request))
5255
{
@@ -472,7 +475,8 @@ public function testXMLFormatter()
472475

473476
public function testFormatByRequestNegotiateIfFormatIsNotJsonOrXML()
474477
{
475-
$config = [
478+
$config = new App();
479+
foreach ([
476480
'baseURL' => 'http://example.com/',
477481
'uriProtocol' => 'REQUEST_URI',
478482
'defaultLocale' => 'en',
@@ -486,10 +490,13 @@ public function testFormatByRequestNegotiateIfFormatIsNotJsonOrXML()
486490
'cookieHTTPOnly' => false,
487491
'proxyIPs' => [],
488492
'cookieSameSite' => 'Lax',
489-
];
493+
] as $key => $value)
494+
{
495+
$config->$key = $value;
496+
}
490497

491-
$request = new MockIncomingRequest((object) $config, new URI($config['baseURL']), null, new UserAgent());
492-
$response = new MockResponse((object) $config);
498+
$request = new MockIncomingRequest($config, new URI($config->baseURL), null, new UserAgent());
499+
$response = new MockResponse($config);
493500

494501
$controller = new class($request, $response)
495502
{

tests/system/HTTP/URITest.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,19 +1007,19 @@ public function testForceGlobalSecureRequests()
10071007

10081008
Factories::injectMock('config', 'App', $config);
10091009

1010-
$request = Services::request($config);
1011-
$request->uri = new URI('http://example.com/ci/v4/controller/method');
1010+
$uri = new URI('http://example.com/ci/v4/controller/method');
1011+
$request = new IncomingRequest($config, $uri, 'php://input', new UserAgent());
10121012

10131013
Services::injectMock('request', $request);
10141014

1015-
// going through request
1015+
// Detected by request
10161016
$this->assertEquals('https://example.com/ci/v4/controller/method', (string) $request->uri);
10171017

1018-
// standalone
1018+
// Standalone
10191019
$uri = new URI('http://example.com/ci/v4/controller/method');
10201020
$this->assertEquals('https://example.com/ci/v4/controller/method', (string) $uri);
10211021

1022-
$this->assertEquals($uri->getPath(), $request->uri->getPath());
1022+
$this->assertEquals(trim($uri->getPath(), '/'), trim($request->uri->getPath(), '/'));
10231023
}
10241024

10251025
public function testZeroAsURIPath()

user_guide_src/source/changelogs/v4.1.2.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Deprecations:
4141
- Deprecated ``ControllerTester`` to use the ``ControllerTestTrait`` instead.
4242
- Consolidated and deprecated ``ControllerResponse`` and ``FeatureResponse`` in favor of ``TestResponse``.
4343
- Deprecated ``Time::instance()``, use ``Time::createFromInstance()`` instead (now accepts ``DateTimeInterface``).
44+
- Deprecated ``IncomingRequest::removeRelativeDirectory()``, use ``URI::removeDotSegments()`` instead
4445

4546
Bugs Fixed:
4647

0 commit comments

Comments
 (0)