fix(devtools): refuse a webserver port the dev server does not own - #165
fix(devtools): refuse a webserver port the dev server does not own#165Decipher wants to merge 1 commit into
Conversation
|
Warning Review limit reachedNext included review available in 59 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (4)
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. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## fix/oauth-consent-permission #165 +/- ##
=============================================================
Coverage 90.70% 90.70%
=============================================================
Files 13 13
Lines 2217 2217
Branches 104 104
=============================================================
Hits 2011 2011
Misses 201 201
Partials 5 5 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
.devtools/startreported success when its own PHP server never bound.Reproduced
A foreign process on the dev port, then
.devtools/start:Exit code 0. Meanwhile,
/tmp/quickstart-drupal-php-server.log:and the pid in the pidfile was already dead. So the script wrote a wrong
BASE_URLinto../.envfor the Nuxt side and printed a login linkpointing at a stranger's server.
Why
Both checks asked the port a question only the port could answer:
fsockopen($host, $port), which succeeds against whateverholds the port;
get_headers()on the same address, likewise.Neither could tell our server from someone else's.
stop_webserver()doesnot close the gap, and should not: it deliberately kills only
php -Sprocesses rather than shooting at whatever it finds on the port.
The fix
Two independent nets.
stop_webserver()has cleared ourown server away, fail if the port is still answering, naming the process
that holds it. A killed server needs a moment to release the port, so it
gets a 3-second grace period before it counts as somebody else's.
the pidfile and stops as soon as that pid is gone, printing the server
log. A bind that fails with the port free (a race, a permission problem)
used to burn the full timeout and then report "Unable to start inbuilt
PHP server" with no reason; it now reports the reason in about a second.
New helpers:
port_is_open(),port_holder(),pid_is_running().Also fixed while in there: the failure path passed the server log as
sprintf's format string, so a
%in a PHP error message would havebroken the error report.
Guardrails, verified in both directions
New
test_start_guardrailsjob. It needs PHP and the scripts only, sinceboth cases fail before Drupal is touched, so it skips assemble and
provision and runs in seconds.
../.envwas not writtenA third assertion checks the squatter is still alive afterwards. A guard
that clears the port by killing whatever holds it would pass every other
check here while being worse than the bug;
stop_webserver()targets onlyphp -Sprocesses, and this is what pins that.Against the fixed scripts all three pass. The happy path was re-run on a
real provisioned backend and is unchanged.
Proving the red direction, and how not to. Non-zero exit is not proof a
guard fired: a crash is also non-zero. A stripped pre-fix copy placed outside
.devtools/dies onrequire_once __DIR__ . '/helpers.php'and exits 255before reaching any of the code under test, which an exit-code assertion
reads as the guard working. So the copy has to sit inside
.devtools/alongside its own
helpers.php, and the assertion has to be on the message.Done that way here, the pre-fix scripts print
[ OK ] Server started successfullyand reachENVIRONMENT READYwith exit 0, which is the realfailure the guardrail is written against.
The committed guardrail is already message-based in the green direction:
grep -q 'already in use by another process'fails on a crash as well as ona false success, so a broken script cannot pass it quietly.
Not in this change
find_free_port()still tests a port by connecting to it rather than bybinding it, so it can hand back a port that is unbindable on the target
interface. That is now a loud failure rather than a false success, which is
why it is left alone here.
startalso kills whatever server the pidfile names, whatever port isbeing requested, so running it with a different
WEBSERVER_PORTstops theserver from the previous run. That is defensible as "one server per
checkout", and unchanged, but it is a sharp edge worth knowing about.