fix(auth): re-establish auth guard in fork child; suppress EPIPE on peer-gone write
The deferred-message fork path (processDeferredMessages -> forkWithSocketPair) never re-runs authenticateConnection, so the child inherited $connection->user (passing the need_auth gate) but a cleared Auth guard — auth()->user() / User::auth() returned null, crashing handlers that read the guard (learn-atc GlitchTip #529/#532/#535/#531/#530). Sync the guard from the mock's user per fork with Auth::setUser() (setUser, not login, to avoid re-firing the Login event on every message). Also guard SocketPairIpc::sendToParent with @socket_write: when the WS client disconnects mid-response the parent tears down its read end, so the child's next write races a closed pipe (EPIPE / "Broken pipe"). That benign warning was promoted to a reported ErrorException and flooded GlitchTip (#461). Mirrors the existing @fwrite in Broadcast/BroadcastClient.php. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1bd747835f
commit
ed15ef8c23
|
|
@ -151,7 +151,13 @@ class SocketPairIpc
|
|||
|
||||
// Newline-delimited framing
|
||||
$message = $data . "\n";
|
||||
$written = socket_write($this->sockets[1], $message, strlen($message));
|
||||
// Suppress the write warning: when the WS client disconnects mid-response
|
||||
// the parent tears down its read end, so the child's next write races a
|
||||
// closed pipe (EPIPE / "Broken pipe"). That warning is otherwise promoted
|
||||
// to a reported ErrorException and floods GlitchTip for a benign, expected
|
||||
// disconnect. The false-return is still handled by the caller. Mirrors the
|
||||
// existing @fwrite in Broadcast/BroadcastClient.php.
|
||||
$written = @socket_write($this->sockets[1], $message, strlen($message));
|
||||
|
||||
return $written === strlen($message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -619,6 +619,29 @@ class Handler implements MessageComponentInterface
|
|||
// Create mock that sends via socket pair
|
||||
$mock = new MockConnectionSocketPair($connection, $ipc);
|
||||
|
||||
// Re-establish this fork's Laravel auth guard from the
|
||||
// connection's user. authenticateConnection() logs the guard in,
|
||||
// but it runs in the PARENT before forking — deferred messages
|
||||
// fork later via processDeferredMessages() WITHOUT re-authenticating,
|
||||
// and the parent's guard may since have been cleared (scheduleLogout)
|
||||
// or reassigned to another connection. The mock still proxies the
|
||||
// correct $connection->user (so the need_auth gate passes), yet
|
||||
// auth()->user() / User::auth() would be null in this child —
|
||||
// crashing any handler that reads the guard instead of ->user.
|
||||
// Sync the guard to the connection here so every handler, deferred
|
||||
// or not, sees a consistent authenticated user. setUser() (not
|
||||
// login()) avoids re-firing the Login event on every message.
|
||||
try {
|
||||
if ($mock->user) {
|
||||
Auth::setUser($mock->user);
|
||||
} elseif (Auth::hasUser()) {
|
||||
Auth::logout();
|
||||
}
|
||||
} catch (\Throwable $authSyncError) {
|
||||
// Best-effort: the need_auth gate in controll_message() is
|
||||
// still the hard authorization check.
|
||||
}
|
||||
|
||||
$this->executeControllerWithDbResilience(
|
||||
$mock,
|
||||
$channel,
|
||||
|
|
|
|||
Loading…
Reference in New Issue