• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

eliashaeussler / typo3-warming / 15509771334

07 Jun 2025 04:43PM UTC coverage: 86.535% (-4.1%) from 90.617%
15509771334

Pull #871

github

eliashaeussler
[FEATURE] Collect url metadata during cache warmup
Pull Request #871: [FEATURE] Collect url metadata during cache warmup

104 of 188 new or added lines in 9 files covered. (55.32%)

1 existing line in 1 file now uncovered.

1446 of 1671 relevant lines covered (86.54%)

9.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.0
/Classes/Http/Client/Handler/SubRequestHandler.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the TYPO3 CMS extension "warming".
7
 *
8
 * Copyright (C) 2021-2025 Elias Häußler <elias@haeussler.dev>
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, either version 2 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22
 */
23

24
namespace EliasHaeussler\Typo3Warming\Http\Client\Handler;
25

26
use EliasHaeussler\Typo3Warming\Domain;
27
use GuzzleHttp\Exception;
28
use GuzzleHttp\Promise;
29
use GuzzleHttp\Utils;
30
use Psr\Http\Message;
31
use Symfony\Component\DependencyInjection;
32
use TYPO3\CMS\Core;
33
use TYPO3\CMS\Frontend;
34

35
/**
36
 * SubRequestHandler
37
 *
38
 * @author Elias Häußler <elias@haeussler.dev>
39
 * @license GPL-2.0-or-later
40
 */
41
#[DependencyInjection\Attribute\Autoconfigure(public: true)]
42
final class SubRequestHandler
43
{
44
    /**
45
     * @var list<string>
46
     */
47
    private readonly array $supportedBaseUrls;
48

49
    /**
50
     * @var callable(Message\RequestInterface, array<string, mixed>): Promise\PromiseInterface
51
     */
52
    private $fallbackHandler;
53

54
    public function __construct(
7✔
55
        private readonly Frontend\Http\Application $application,
56
        Domain\Repository\SiteRepository $siteRepository,
57
    ) {
58
        $this->supportedBaseUrls = $this->resolveBaseUrls($siteRepository);
7✔
59
        $this->fallbackHandler = Utils::chooseHandler();
7✔
60
    }
61

62
    /**
63
     * @param array<string, mixed> $options
64
     */
65
    public function __invoke(Message\RequestInterface $request, array $options): Promise\PromiseInterface
3✔
66
    {
67
        if (!$this->isSupportedRequestUrl($request->getUri())) {
3✔
68
            return ($this->fallbackHandler)($request, $options);
1✔
69
        }
70

71
        $initialTsfe = $GLOBALS['TSFE'] ?? null;
2✔
72
        $GLOBALS['TSFE'] = null;
2✔
73

74
        try {
75
            return $this->sendSubRequest($request);
2✔
76
        } finally {
77
            $GLOBALS['TSFE'] = $initialTsfe;
2✔
78
        }
79
    }
80

81
    private function sendSubRequest(Message\RequestInterface $request): Promise\PromiseInterface
2✔
82
    {
83
        $response = null;
2✔
84
        $subRequest = new Core\Http\ServerRequest(
2✔
85
            $request->getUri(),
2✔
86
            $request->getMethod(),
2✔
87
            $request->getBody(),
2✔
88
            $request->getHeaders(),
2✔
89
        );
2✔
90

91
        try {
92
            return Promise\Create::promiseFor(
2✔
93
                $this->application->handle($subRequest),
2✔
94
            );
2✔
95
        } catch (Core\Http\ImmediateResponseException $exception) {
1✔
NEW
96
            $response = $exception->getResponse();
×
97
        } catch (Core\Error\Http\StatusException $exception) {
1✔
NEW
98
            $response = new Core\Http\Response(
×
NEW
99
                statusCode: 500,
×
NEW
100
                headers: $this->parseHeaderLines($exception->getStatusHeaders()),
×
UNCOV
101
            );
×
102
        } catch (\Exception $exception) {
1✔
103
        }
104

105
        return Promise\Create::rejectionFor(
1✔
106
            new Exception\RequestException($exception->getMessage(), $request, $response),
1✔
107
        );
1✔
108
    }
109

110
    private function isSupportedRequestUrl(Message\UriInterface $uri): bool
3✔
111
    {
112
        $requestUrl = (string)$uri;
3✔
113

114
        foreach ($this->supportedBaseUrls as $baseUrl) {
3✔
115
            if (str_starts_with($requestUrl, $baseUrl)) {
3✔
116
                return true;
2✔
117
            }
118
        }
119

120
        return false;
1✔
121
    }
122

123
    /**
124
     * @return list<string>
125
     */
126
    private function resolveBaseUrls(Domain\Repository\SiteRepository $siteRepository): array
7✔
127
    {
128
        $sites = $siteRepository->findAll();
7✔
129
        $baseUrls = [];
7✔
130

131
        foreach ($sites as $site) {
7✔
132
            $baseUrls[] = (string)$site->getBase();
7✔
133
        }
134

135
        return $baseUrls;
7✔
136
    }
137

138
    /**
139
     * @param array<string> $statusHeaders
140
     * @return array<string, list<string>>
141
     */
NEW
142
    private function parseHeaderLines(array $statusHeaders): array
×
143
    {
NEW
144
        $headers = [];
×
145

NEW
146
        foreach ($statusHeaders as $headerLine) {
×
NEW
147
            if (str_contains($headerLine, ':')) {
×
NEW
148
                [$headerName, $headerValue] = explode(':', $headerLine, 2);
×
NEW
149
                $headers[$headerName] ??= [];
×
NEW
150
                $headers[$headerName][] = trim($headerValue);
×
151
            }
152
        }
153

NEW
154
        return $headers;
×
155
    }
156
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc