Repository navigation
refactor(activity): use curl for the activity log stream - #258
Merged
Merged
Conversation
Read activity logs with ext-curl (curl_multi), consistent with the API client, instead of a PHP stream opened with fopen(). - Add Util\CurlStream: a non-blocking reader for an HTTP response body, which waits for the response headers and treats non-2xx statuses as failures. - Replace Api::getStreamContext() with Api::getCurlOptions(), which uses the API client's CA bundle, api.skip_ssl, proxy and user agent settings. - Keep the existing behavior: retries for up to 2 minutes with a 0.5s interval and a 10s timeout per attempt, reconnecting on EOF. - Require ext-curl in composer.json. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Note
Reviewed — No blocking findings · 🔵 3 minor points
🔍 Full review · 7 files reviewed
🔵 Minor points
legacy/src/Service/ActivityMonitor.php:193— The PR removes the fixed 200ms sleep after an empty read, but leaves the one at line 216:if (empty($items)) { if (!$seal) { \usleep(self::STREAM_WAIT); } continue; }.parseLog()only handles complete lines and keeps the partial tail in$buffer. A chunk that ends mid-line therefore produces no items, so the loop sleeps 200ms before it reads the rest of the line, and curl chunks rarely end on line boundaries. The same happens for chunks that only contain duplicate items after a reconnect.read()already waits on the socket, so this sleep only adds latency.legacy/src/Service/ActivityMonitor.php:239— The stream is only closed on the normal exit path. EachCurlStreamholds a reference cycle: the curl handle's HEADERFUNCTION and WRITEFUNCTION closures capture$this. So__destructnever runs on refcount alone. If the loop throws, for example from$activity->refresh()or log parsing, the HTTP connection and curl handles stay alive until the cycle collector runs, if it runs before the process exits. A try/finally around the loop, or a weak or static callback, would release them.legacy/tests/Util/CurlStreamTest.php:74— The read loop intestRequestHeaders(while (!$stream->eof())) has no deadline, unlike the one intestStream. If the server never finishes the response (a stuck built-in server, or a regression whereperform()stops settingdone), the test spins forever and hangs the PHPUnit run rather than failing.
Verification
onHeadersetsheadersReceivedonly on the blank line after a status of 200 or above, so a100 Continueresponse is skipped; the regex also matchesHTTP/2 200.getCurlOptionsapplies the CA bundle the same way asConfig::getStreamContextOptions(CAPATH for a directory, CAINFO for a file), and also honorsapi.skip_ssl.- Every retry in
getLogStreamcloses the failedCurlStreambefore it creates a new one, and the EOF reconnect path inwaitAndLogcloses the old stream too. perform()returns early oncedoneis set, so callingclose()and then__destruct(withmultinull) is safe.
The new CurlStreamTest covers streaming, request headers, 404, header timeout and connection errors against a PHP built-in server. It runs in the CI make test-php step (Linux). No test covers the ActivityMonitor retry and reconnect loop or Api::getCurlOptions (proxy, no_proxy, CA and skip_ssl options).
Review details
- Commit: 7bc2ceb
- Model: claude-opus-5-5
Review 1 of 10 for this pull request · View the full run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Activity logs now use the same HTTP client settings as other API requests, and show new log lines sooner.
no_proxy, like the API client. Previously it used a single proxy for all URLs and ignoredno_proxy.Implementation:
curl_multi) instead of a PHP stream opened withfopen(), soallow_url_fopenis no longer needed.Util\CurlStream: a non-blocking reader for an HTTP response body. It waits for the response headers and treats non-2xx statuses as failures.Api::getCurlOptions()replacesApi::getStreamContext(), using the API client's CA bundle,api.skip_ssl, proxy and user agent settings.ext-curlis now required incomposer.json.Unlike
fopen(), redirects are not followed; the log endpoint does not redirect.🤖 Generated with Claude Code