@@ -37,85 +37,93 @@ protected function serverConfigHelp(): string {
3737 * Get all possible URLs that need to be checked for a local request test.
3838 * This takes all `trusted_domains` and the CLI overwrite URL into account.
3939 *
40- * @param string $url The relative URL to test starting with a /
41- * @return string[] List of possible absolute URLs
40+ * @param string $url The absolute path (absolute URL without host but with web-root) to test starting with a /
41+ * @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')
42+ * @return list<string> List of possible absolute URLs
4243 */
43- protected function getTestUrls (string $ url , bool $ removeWebroot ): array {
44- $ testUrls = [] ;
44+ protected function getTestUrls (string $ url , bool $ isRootRequest = false ): array {
45+ $ url = ' / ' . ltrim ( $ url , ' / ' ) ;
4546
4647 $ webroot = rtrim ($ this ->urlGenerator ->getWebroot (), '/ ' );
48+ if ($ isRootRequest === false && $ webroot !== '' && str_starts_with ($ url , $ webroot )) {
49+ // The URL contains the web-root but also the base url does so,
50+ // so we need to remove the web-root from the URL.
51+ $ url = substr ($ url , strlen ($ webroot ));
52+ }
4753
48- /* Try overwrite.cli.url first, it’s supposed to be how the server contacts itself */
49- $ cliUrl = $ this -> config -> getSystemValueString ( ' overwrite.cli.url ' , '' ) ;
54+ // Base URLs to test
55+ $ baseUrls = [] ;
5056
57+ // Try overwrite.cli.url first, it’s supposed to be how the server contacts itself
58+ $ cliUrl = $ this ->config ->getSystemValueString ('overwrite.cli.url ' , '' );
5159 if ($ cliUrl !== '' ) {
52- $ cliUrl = $ this ->normalizeUrl (
60+ // The CLI URL already contains the web-root, so we need to normalize it if requested
61+ $ baseUrls [] = $ this ->normalizeUrl (
5362 $ cliUrl ,
54- $ webroot ,
55- $ removeWebroot
63+ $ isRootRequest
5664 );
57-
58- $ testUrls [] = $ cliUrl . $ url ;
5965 }
6066
61- /* Try URL generator second */
62- $ baseUrl = $ this ->normalizeUrl (
67+ // Try URL generator second
68+ // The base URL also contains the webroot so also normalize it
69+ $ baseUrls [] = $ this ->normalizeUrl (
6370 $ this ->urlGenerator ->getBaseUrl (),
64- $ webroot ,
65- $ removeWebroot
71+ $ isRootRequest
6672 );
6773
68- if ($ baseUrl !== $ cliUrl ) {
69- $ testUrls [] = $ baseUrl . $ url ;
70- }
71-
7274 /* Last resort: trusted domains */
73- $ hosts = $ this ->config ->getSystemValue ('trusted_domains ' , []);
74- foreach ($ hosts as $ host ) {
75+ $ trustedDomains = $ this ->config ->getSystemValue ('trusted_domains ' , []);
76+ foreach ($ trustedDomains as $ host ) {
7577 if (str_contains ($ host , '* ' )) {
7678 /* Ignore domains with a wildcard */
7779 continue ;
7880 }
79- $ hosts [] = ' https:// ' . $ host . $ url ;
80- $ hosts [] = ' http:// ' . $ host . $ url ;
81+ $ baseUrls [] = $ this -> normalizeUrl ( " https:// $ host$ webroot " , $ isRootRequest ) ;
82+ $ baseUrls [] = $ this -> normalizeUrl ( " http:// $ host$ webroot " , $ isRootRequest ) ;
8183 }
8284
83- return $ testUrls ;
85+ return array_map ( fn ( string $ host ) => $ host . $ url , array_values ( array_unique ( $ baseUrls ))) ;
8486 }
8587
8688 /**
8789 * Strip a trailing slash and remove the webroot if requested.
90+ * @param string $url The URL to normalize. Should be an absolute URL containing scheme, host and optionally web-root.
91+ * @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
8892 */
89- protected function normalizeUrl (string $ url , string $ webroot , bool $ removeWebroot ): string {
90- $ url = rtrim ($ url , '/ ' );
91- if ($ removeWebroot && str_ends_with ($ url , $ webroot )) {
92- $ url = substr ($ url , -strlen ($ webroot ));
93+ protected function normalizeUrl (string $ url , bool $ removeWebroot ): string {
94+ if ($ removeWebroot ) {
95+ $ segments = parse_url ($ url );
96+ $ port = isset ($ segments ['port ' ]) ? (': ' . $ segments ['port ' ]) : '' ;
97+ return $ segments ['scheme ' ] . ':// ' . $ segments ['host ' ] . $ port ;
9398 }
9499 return rtrim ($ url , '/ ' );
95100 }
96101
97102 /**
98103 * Run a HTTP request to check header
99104 * @param string $method The HTTP method to use
100- * @param string $url The relative URL to check
101- * @param array{ignoreSSL?: bool, httpErrors?: bool, options?: array} $options Additional options, like
102- * [
103- * // Ignore invalid SSL certificates (e.g. self signed)
104- * 'ignoreSSL' => true,
105- * // Ignore requests with HTTP errors (will not yield if request has a 4xx or 5xx response)
106- * 'httpErrors' => true,
107- * ]
105+ * @param string $url The absolute path (URL with webroot but without host) to check, can be the output of `IURLGenerator`
106+ * @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.
107+ * @param array{ignoreSSL?: bool, httpErrors?: bool, options?: array} $options HTTP client related options, like
108+ * [
109+ * // Ignore invalid SSL certificates (e.g. self signed)
110+ * 'ignoreSSL' => true,
111+ * // Ignore requests with HTTP errors (will not yield if request has a 4xx or 5xx response)
112+ * 'httpErrors' => true,
113+ * // Additional options for the HTTP client (see `IClient`)
114+ * 'options' => [],
115+ * ]
108116 *
109117 * @return Generator<int, IResponse>
110118 */
111- protected function runRequest (string $ method , string $ url , array $ options = [], bool $ removeWebroot = false ): Generator {
119+ protected function runRequest (string $ method , string $ url , array $ options = [], bool $ isRootRequest = false ): Generator {
112120 $ options = array_merge (['ignoreSSL ' => true , 'httpErrors ' => true ], $ options );
113121
114122 $ client = $ this ->clientService ->newClient ();
115123 $ requestOptions = $ this ->getRequestOptions ($ options ['ignoreSSL ' ], $ options ['httpErrors ' ]);
116124 $ requestOptions = array_merge ($ requestOptions , $ options ['options ' ] ?? []);
117125
118- foreach ($ this ->getTestUrls ($ url , $ removeWebroot ) as $ testURL ) {
126+ foreach ($ this ->getTestUrls ($ url , $ isRootRequest ) as $ testURL ) {
119127 try {
120128 yield $ client ->request ($ method , $ testURL , $ requestOptions );
121129 } catch (\Throwable $ e ) {
@@ -126,7 +134,7 @@ protected function runRequest(string $method, string $url, array $options = [],
126134
127135 /**
128136 * Run a HEAD request to check header
129- * @param string $url The relative URL to check
137+ * @param string $url The relative URL to check (e.g. output of IURLGenerator)
130138 * @param bool $ignoreSSL Ignore SSL certificates
131139 * @param bool $httpErrors Ignore requests with HTTP errors (will not yield if request has a 4xx or 5xx response)
132140 * @return Generator<int, IResponse>
0 commit comments