Skip to content

0.34.x#226

Open
loks0n wants to merge 149 commits into0.33.xfrom
0.34.x
Open

0.34.x#226
loks0n wants to merge 149 commits into0.33.xfrom
0.34.x

Conversation

@loks0n
Copy link
Copy Markdown
Contributor

@loks0n loks0n commented Mar 13, 2026

No description provided.

Meldiron and others added 23 commits July 2, 2024 15:06
Headers can now be arrays (after recent changes allowing array headers).
The getSize() method was attempting to directly implode headers, causing
a warning when a header value was an array.

This fix properly handles both string and array header values by joining
array values with commas (standard HTTP header format) before calculating
the request size.

Added test case to verify the fix works correctly with array headers.
feat: remove validators and use utopia validators lib
* Use utopia-php/di for resource injection

* Move resource ownership into utopia-php/di

* Update DI branch dependency

* update getting started

* update

* update

* update appwrite base version

* update to use php 8.2

* fix: restore php 8.2 test runtime

* chore: use container scopes

* remove utopia keyword

* remove optional container in run

* remove optional container in run

* renaming

* remove public getContainer

* fix getcontainer

* fix getcontainer

* update

* remove tests

* make public

* remove tests

* add scoped request containers

* cleanup

* feat: request scopes

* fixes

---------

Co-authored-by: loks0n <22452787+loks0n@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 13, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d29c187e-8381-420b-ab45-9fec0cd3a823

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 0.34.x
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

#230)

* feat: split Swoole adapters, add compression support, and adopt utopia-php/servers

- Split Swoole adapter into Swoole (SWOOLE_PROCESS) and SwooleCoroutine (coroutine-based) servers
- Add response compression support with configurable min size and algorithm selection
- Migrate Hook to utopia-php/servers and Route now extends Servers\Hook
- Add View class for template rendering
- Add trusted IP header support and IP validation in Request
- Enhance Response with cookie management, content-type helpers, and chunked transfer
- Add utopia-php/servers and utopia-php/compression dependencies
- Fix server-swoole.php test server to work with non-coroutine Swoole adapter
- Disable Swoole cookie parsing to preserve raw Cookie headers

* fix: address Greptile review comments on PR #230

- Remove Content-Length before re-adding after compression to prevent duplicate headers
- Defer onStart callback into coroutine event loop for SwooleCoroutine adapter consistency
- Add null-coalescing fallback for preg_replace in View::render
- Add void return types to compression setter methods for API consistency

* add telemetry
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 6, 2026

Greptile Summary

This is a large structural PR for version 0.34.x that splits the Swoole adapter into separate Swoole (process-mode) and SwooleCoroutine (coroutine-mode) adapters, adds response compression, introduces OpenTelemetry-compatible telemetry (request duration, active requests, body sizes), migrates resource/DI ownership to utopia-php/di, and fixes null-path normalization during routing.

  • Unreachable dead code: the elseif (self::REQUEST_METHOD_OPTIONS == $method) block at Http.php:957–981 can never execute — OPTIONS requests already return $this at line 942 and never reach that branch
  • Swoole/Request::getReferer() silently ignores its $default parameter, forwarding '' to getHeader instead — callers relying on a custom fallback value will silently get an empty string
  • Request::getSize() reads php://input, which is only valid in PHP-FPM; neither Swoole adapter overrides it, so the newly-added http.server.request.body.size telemetry metric always records zero body bytes for all Swoole requests
  • (Previously flagged) Swoole/Response::sendStatus() passes a (string) cast to Swoole\Http\Response::status(), which expects int
  • (Previously flagged) Swoole/Server does not use try/finally to clean up the per-request DI container, unlike the SwooleCoroutine variant

Confidence Score: 3/5

Two P1 defects affect all Swoole users: getReferer silently drops caller-supplied defaults, and the new requestBodySize telemetry always records zero bytes for Swoole requests.

Both P1 issues are confirmed present-defects on the changed path. getReferer breaks the interface contract for any Swoole caller supplying a custom fallback. The getSize/php://input mismatch makes the newly-added telemetry metric wrong for all Swoole requests. The dead OPTIONS block is P2. These issues together warrant a score of 3.

src/Http/Adapter/Swoole/Request.php (getReferer default bug, missing getSize override), src/Http/Request.php (getSize reads php://input), src/Http/Http.php (dead OPTIONS elseif block)

Important Files Changed

Filename Overview
src/Http/Http.php Major refactor with telemetry and compression; contains unreachable OPTIONS dead-code block at lines 957-981
src/Http/Adapter/Swoole/Request.php getReferer ignores $default param; getSize() inherits php://input read that is invalid in Swoole context
src/Http/Request.php Abstract base; getSize() reads php://input which is only valid for FPM — Swoole subclasses must override
src/Http/Adapter/Swoole/Server.php Split from shared adapter; missing try/finally for per-request container cleanup (previously flagged)
src/Http/Adapter/Swoole/Response.php New split adapter; sendStatus passes (string) cast to int-expecting Swoole API (previously flagged)
src/Http/Adapter/SwooleCoroutine/Server.php New coroutine adapter; port cast issue previously flagged; try/finally lifecycle cleanup is correct
src/Http/Adapter/FPM/Server.php Simple FPM single-request adapter; correct lifecycle
src/Http/Response.php Compression and chunk support added correctly

Greploops — Automatically fix all review issues by running /greploops in Claude Code. It iterates: fix, push, re-review, repeat until 5/5 confidence.
Use the Greptile plugin for Claude Code to query reviews, search comments, and manage custom context directly from your terminal.

Reviews (2): Last reviewed commit: "[codex] Normalize null request paths dur..." | Re-trigger Greptile

array $settings = [],
?Container $container = null
) {
$this->server = new SwooleServer($host, $port, false, true);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Missing (int) cast for $port

The Swoole\Coroutine\Http\Server constructor expects int $port. $port is typed as ?string here and is passed without any cast. If $port is null (the default), PHP coerces null0 in non-strict mode, which silently opens the server on a random port. The non-coroutine Swoole\Server adapter (line 20 of Swoole/Server.php) correctly applies (int) $port to handle this.

Suggested change
$this->server = new SwooleServer($host, $port, false, true);
$this->server = new SwooleServer($host, (int) $port, false, true);

*/
protected function sendStatus(int $statusCode): void
{
$this->swoole->status((string) $statusCode);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unnecessary (string) cast in sendStatus

Swoole\Http\Response::status() has the signature status(int $http_status_code, string $reason = ''): bool. $statusCode is already typed as int by the parent abstract method — casting it to string before passing it in is at best a no-op (PHP coerces it back) and could cause a TypeError if Swoole ever enforces strict typing internally. The cast should be removed.

Suggested change
$this->swoole->status((string) $statusCode);
$this->swoole->status($statusCode);

Comment on lines +29 to +36
$this->server->on('request', function (SwooleRequest $request, SwooleResponse $response) use ($callback) {
$requestContainer = new Container($this->container);
$requestContainer->set('swooleRequest', fn () => $request);
$requestContainer->set('swooleResponse', fn () => $response);

Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY] = $requestContainer;

\call_user_func($callback, new Request($request), new Response($response));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing try/finally cleanup for coroutine context

The SwooleCoroutine\Server variant wraps the callback invocation in a try/finally to unset(Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY]) after each request, ensuring stale per-request containers don't linger if an exception escapes. The process-mode Swoole\Server does not do this:

Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY] = $requestContainer;
\call_user_func($callback, new Request($request), new Response($response));
// No cleanup

Although Swoole destroys each request coroutine's context automatically when the coroutine exits, adding the matching try/finally here keeps the two adapters consistent and guards against any future change that reuses coroutines across requests:

Suggested change
$this->server->on('request', function (SwooleRequest $request, SwooleResponse $response) use ($callback) {
$requestContainer = new Container($this->container);
$requestContainer->set('swooleRequest', fn () => $request);
$requestContainer->set('swooleResponse', fn () => $response);
Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY] = $requestContainer;
\call_user_func($callback, new Request($request), new Response($response));
Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY] = $requestContainer;
try {
\call_user_func($callback, new Request($request), new Response($response));
} finally {
unset(Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY]);
}

Comment on lines 353 to 365
public function getSize(): int
{
return \mb_strlen(\implode("\n", $this->generateHeaders()), '8bit') + \mb_strlen(\file_get_contents('php://input'), '8bit');
$headers = $this->generateHeaders();
$headerStrings = [];
foreach ($headers as $key => $value) {
if (\is_array($value)) {
$headerStrings[] = $key . ': ' . \implode(', ', $value);
} else {
$headerStrings[] = $key . ': ' . $value;
}
}
return \mb_strlen(\implode("\n", $headerStrings), '8bit') + \mb_strlen(\file_get_contents('php://input'), '8bit');
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 getSize() reads php://input — incorrect for Swoole adapters

The base getSize() calls file_get_contents('php://input') to measure the request body size. This works under PHP-FPM, but in Swoole the request body is only accessible via $swoole->rawContent() — PHP's php://input stream is empty in that context.

Neither Swoole/Request.php nor SwooleCoroutine/Request.php override getSize(). Because Http::run() now calls $this->requestBodySize->record($request->getSize(), ...) for the new telemetry, all Swoole requests will report zero bytes for the body in the http.server.request.body.size metric.

Fix: override getSize() in Swoole/Request.php to use rawContent() for the body component:

public function getSize(): int
{
    $headers = $this->generateHeaders();
    $headerStrings = [];
    foreach ($headers as $key => $value) {
        $headerStrings[] = is_array($value)
            ? $key . ': ' . implode(', ', $value)
            : $key . ': ' . $value;
    }
    return mb_strlen(implode("\n", $headerStrings), '8bit')
         + mb_strlen($this->swoole->rawContent(), '8bit');
}

SwooleCoroutine/Request.php extends Swoole/Request.php, so it will inherit the fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.