Repository: codeigniter4/CodeIgniter4 (PHP)

Issue:
Bug: enabled forceGlobalSecureRequests prevents uri schemes besides http/https
[HTTP/URI.php#L599](https://github.com/codeigniter4/CodeIgniter4/blob/develop/system/HTTP/URI.php#L599) assumes the only URI schemes are `http` and `https` and therefore ignores other URI schemes when using `forceGlobalSecureRequests`.

This is allread tackled in [Helpers/url_helper.php#L65](https://github.com/codeigniter4/CodeIgniter4/blob/develop/system/Helpers/url_helper.php#L65) and  [HTTP/IncomingRequest.php#L411](https://github.com/codeigniter4/CodeIgniter4/blob/develop/system/HTTP/IncomingRequest.php#L411).
Since this is required in multiple places and therfore error-prone, maybe consider outsourcing this to one tested static method.
Even introduced this differing behaviors in the same commit https://github.com/codeigniter4/CodeIgniter4/commit/89d588fab6dadc904c76859b4a8a86a3e8d11189



```php
d( (string) new URI('ftp://localhost') )
// Wrong Output -> "https://localhost"
// Expected Output -> "ftp://localhost"
```


Discussion:
What's your exact problem?
Why do you use ftp in HTTP/URI?
I need to process URIs progrmatically/dynamically so using HTTP/Uri as the OOP abstraction layer only makes sense in my eyes.
In my spcific case this is not limited to ftp, there are many URI schemes involved, even custom application uri schemes.

Take `skype://foobar` as a example, this is per-se a valid URI and therefore should not be broken if im not wrong.
To take another perspective, if im wrong, why are there exclusions in the url helper and incomming requests..
Thank you for explanation.
Because of language barrier, it is sometimes difficult to me to understand what's saying.

At first I thought the URI class represents URIs in HTTP, because the name is `HTTP\URI`,
but according to the user guide, it seems to represent all URIs.
https://codeigniter.com/user_guide/libraries/uri.html

So this is a bug.

It seems the URI class is very tightly coupled with the CodeIgniter's HTTP processing.
To be honest, I don't recommend to use it for general URI processing...
The part that is tightly coupled seems to be only `__toString()`.

@element-code Fixed and the fix will be included in v4.2.0.

Due to the historical reason,
when the scheme is http(s) and the hostname matches baseURL's hostname,
the URI class may still change the URL.

https://github.com/codeigniter4/CodeIgniter4/blob/200a04eab04262ce79a62b4a776c748f857b73cd/system/HTTP/URI.php#L585
https://github.com/codeigniter4/CodeIgniter4/blob/200a04eab04262ce79a62b4a776c748f857b73cd/system/HTTP/URI.php#L596-L602

Thanks for the work and the quick solve @kenjis 👍 



Patch:
diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php
index 70305ae5864c..e0a6ff9f2386 100644
--- a/system/HTTP/URI.php
+++ b/system/HTTP/URI.php
@@ -581,12 +581,34 @@ public function __toString(): string
         $path   = $this->getPath();
         $scheme = $this->getScheme();
 
+        // If the hosts matches then assume this should be relative to baseURL
+        [$scheme, $path] = $this->changeSchemeAndPath($scheme, $path);
+
+        return static::createURIString(
+            $scheme,
+            $this->getAuthority(),
+            $path, // Absolute URIs should use a "/" for an empty path
+            $this->getQuery(),
+            $this->getFragment()
+        );
+    }
+
+    /**
+     * Change the path (and scheme) assuming URIs with the same host as baseURL
+     * should be relative to the project's configuration.
+     *
+     * @deprecated This method will be deleted.
+     */
+    private function changeSchemeAndPath(string $scheme, string $path): array
+    {
         // Check if this is an internal URI
         $config  = config('App');
         $baseUri = new self($config->baseURL);
 
-        // If the hosts matches then assume this should be relative to baseURL
-        if ($this->getHost() === $baseUri->getHost()) {
+        if (
+            substr($this->getScheme(), 0, 4) === 'http'
+            && $this->getHost() === $baseUri->getHost()
+        ) {
             // Check for additional segments
             $basePath = trim($baseUri->getPath(), '/') . '/';
             $trimPath = ltrim($path, '/');
@@ -601,13 +623,7 @@ public function __toString(): string
             }
         }
 
-        return static::createURIString(
-            $scheme,
-            $this->getAuthority(),
-            $path, // Absolute URIs should use a "/" for an empty path
-            $this->getQuery(),
-            $this->getFragment()
-        );
+        return [$scheme, $path];
     }
 
     /**


Test patch:
diff --git a/tests/system/HTTP/URITest.php b/tests/system/HTTP/URITest.php
index 8536fcc7c801..a4faacfbdd80 100644
--- a/tests/system/HTTP/URITest.php
+++ b/tests/system/HTTP/URITest.php
@@ -985,4 +985,20 @@ public function testCreateURIString()
 
         $this->assertSame($expected, $uri);
     }
+
+    /**
+     * @see https://github.com/codeigniter4/CodeIgniter4/issues/5728
+     */
+    public function testForceGlobalSecureRequestsAndNonHTTPProtocol()
+    {
+        $config                            = new App();
+        $config->forceGlobalSecureRequests = true;
+        $config->baseURL                   = 'https://localhost/';
+        Factories::injectMock('config', 'App', $config);
+
+        $expected = 'ftp://localhost/path/to/test.txt';
+        $uri      = new URI($expected);
+
+        $this->assertSame($expected, (string) $uri);
+    }
 }