@@ -54,85 +54,93 @@ protected function serverConfigHelp(): string {
5454 * Get all possible URLs that need to be checked for a local request test.
5555 * This takes all `trusted_domains` and the CLI overwrite URL into account.
5656 *
57- * @param string $url The relative URL to test starting with a /
58- * @return string[] List of possible absolute URLs
57+ * @param string $url The absolute path (absolute URL without host but with web-root) to test starting with a /
58+ * @param bool $isRootRequest Set to remove the web-root from URL and host (e.g. when requesting a path in the domain root like '/.well-known')
59+ * @return list<string> List of possible absolute URLs
5960 */
60- protected function getTestUrls (string $ url , bool $ removeWebroot ): array {
61- $ testUrls = [] ;
61+ protected function getTestUrls (string $ url , bool $ isRootRequest = false ): array {
62+ $ url = ' / ' . ltrim ( $ url , ' / ' ) ;
6263
6364 $ webroot = rtrim ($ this ->urlGenerator ->getWebroot (), '/ ' );
65+ if ($ isRootRequest === false && $ webroot !== '' && str_starts_with ($ url , $ webroot )) {
66+ // The URL contains the web-root but also the base url does so,
67+ // so we need to remove the web-root from the URL.
68+ $ url = substr ($ url , strlen ($ webroot ));
69+ }
6470
65- /* Try overwrite.cli.url first, it’s supposed to be how the server contacts itself */
66- $ cliUrl = $ this -> config -> getSystemValueString ( ' overwrite.cli.url ' , '' ) ;
71+ // Base URLs to test
72+ $ baseUrls = [] ;
6773
74+ // Try overwrite.cli.url first, it’s supposed to be how the server contacts itself
75+ $ cliUrl = $ this ->config ->getSystemValueString ('overwrite.cli.url ' , '' );
6876 if ($ cliUrl !== '' ) {
69- $ cliUrl = $ this ->normalizeUrl (
77+ // The CLI URL already contains the web-root, so we need to normalize it if requested
78+ $ baseUrls [] = $ this ->normalizeUrl (
7079 $ cliUrl ,
71- $ webroot ,
72- $ removeWebroot
80+ $ isRootRequest
7381 );
74-
75- $ testUrls [] = $ cliUrl . $ url ;
7682 }
7783
78- /* Try URL generator second */
79- $ baseUrl = $ this ->normalizeUrl (
84+ // Try URL generator second
85+ // The base URL also contains the webroot so also normalize it
86+ $ baseUrls [] = $ this ->normalizeUrl (
8087 $ this ->urlGenerator ->getBaseUrl (),
81- $ webroot ,
82- $ removeWebroot
88+ $ isRootRequest
8389 );
8490
85- if ($ baseUrl !== $ cliUrl ) {
86- $ testUrls [] = $ baseUrl . $ url ;
87- }
88-
8991 /* Last resort: trusted domains */
90- $ hosts = $ this ->config ->getSystemValue ('trusted_domains ' , []);
91- foreach ($ hosts as $ host ) {
92+ $ trustedDomains = $ this ->config ->getSystemValue ('trusted_domains ' , []);
93+ foreach ($ trustedDomains as $ host ) {
9294 if (str_contains ($ host , '* ' )) {
9395 /* Ignore domains with a wildcard */
9496 continue ;
9597 }
96- $ hosts [] = ' https:// ' . $ host . $ url ;
97- $ hosts [] = ' http:// ' . $ host . $ url ;
98+ $ baseUrls [] = $ this -> normalizeUrl ( " https:// $ host$ webroot " , $ isRootRequest ) ;
99+ $ baseUrls [] = $ this -> normalizeUrl ( " http:// $ host$ webroot " , $ isRootRequest ) ;
98100 }
99101
100- return $ testUrls ;
102+ return array_map ( fn ( string $ host ) => $ host . $ url , array_values ( array_unique ( $ baseUrls ))) ;
101103 }
102104
103105 /**
104106 * Strip a trailing slash and remove the webroot if requested.
107+ * @param string $url The URL to normalize. Should be an absolute URL containing scheme, host and optionally web-root.
108+ * @param bool $removeWebroot If set the web-root is removed from the URL and an absolute URL with only the scheme and host (optional port) is returned
105109 */
106- protected function normalizeUrl (string $ url , string $ webroot , bool $ removeWebroot ): string {
107- $ url = rtrim ($ url , '/ ' );
108- if ($ removeWebroot && str_ends_with ($ url , $ webroot )) {
109- $ url = substr ($ url , -strlen ($ webroot ));
110+ protected function normalizeUrl (string $ url , bool $ removeWebroot ): string {
111+ if ($ removeWebroot ) {
112+ $ segments = parse_url ($ url );
113+ $ port = isset ($ segments ['port ' ]) ? (': ' . $ segments ['port ' ]) : '' ;
114+ return $ segments ['scheme ' ] . ':// ' . $ segments ['host ' ] . $ port ;
110115 }
111116 return rtrim ($ url , '/ ' );
112117 }
113118
114119 /**
115120 * Run a HTTP request to check header
116121 * @param string $method The HTTP method to use
117- * @param string $url The relative URL to check
118- * @param array{ignoreSSL?: bool, httpErrors?: bool, options?: array} $options Additional options, like
119- * [
120- * // Ignore invalid SSL certificates (e.g. self signed)
121- * 'ignoreSSL' => true,
122- * // Ignore requests with HTTP errors (will not yield if request has a 4xx or 5xx response)
123- * 'httpErrors' => true,
124- * ]
122+ * @param string $url The absolute path (URL with webroot but without host) to check, can be the output of `IURLGenerator`
123+ * @param bool $isRootRequest If set the webroot is removed from URLs to make the request target the host's root. Example usage are the /.well-known URLs in the root path.
124+ * @param array{ignoreSSL?: bool, httpErrors?: bool, options?: array} $options HTTP client related options, like
125+ * [
126+ * // Ignore invalid SSL certificates (e.g. self signed)
127+ * 'ignoreSSL' => true,
128+ * // Ignore requests with HTTP errors (will not yield if request has a 4xx or 5xx response)
129+ * 'httpErrors' => true,
130+ * // Additional options for the HTTP client (see `IClient`)
131+ * 'options' => [],
132+ * ]
125133 *
126134 * @return Generator<int, IResponse>
127135 */
128- protected function runRequest (string $ method , string $ url , array $ options = [], bool $ removeWebroot = false ): Generator {
136+ protected function runRequest (string $ method , string $ url , array $ options = [], bool $ isRootRequest = false ): Generator {
129137 $ options = array_merge (['ignoreSSL ' => true , 'httpErrors ' => true ], $ options );
130138
131139 $ client = $ this ->clientService ->newClient ();
132140 $ requestOptions = $ this ->getRequestOptions ($ options ['ignoreSSL ' ], $ options ['httpErrors ' ]);
133141 $ requestOptions = array_merge ($ requestOptions , $ options ['options ' ] ?? []);
134142
135- foreach ($ this ->getTestUrls ($ url , $ removeWebroot ) as $ testURL ) {
143+ foreach ($ this ->getTestUrls ($ url , $ isRootRequest ) as $ testURL ) {
136144 try {
137145 yield $ client ->request ($ method , $ testURL , $ requestOptions );
138146 } catch (\Throwable $ e ) {
@@ -143,7 +151,7 @@ protected function runRequest(string $method, string $url, array $options = [],
143151
144152 /**
145153 * Run a HEAD request to check header
146- * @param string $url The relative URL to check
154+ * @param string $url The relative URL to check (e.g. output of IURLGenerator)
147155 * @param bool $ignoreSSL Ignore SSL certificates
148156 * @param bool $httpErrors Ignore requests with HTTP errors (will not yield if request has a 4xx or 5xx response)
149157 * @return Generator<int, IResponse>
0 commit comments