PHP + Fcgid hängt, wenn der Download unterbrochen wird

PHP + Fcgid hängt, wenn der Download unterbrochen wird

Notiz:Administratoren, bitte stimmen Sie für die Migration zu ServerFault.

Ich verwende ein LAMP-Setup mit PHP mod_fcgid. Bei den meisten Anfragen funktioniert das gut, aber ich habe festgestellt, dass, wenn ich eine Datei herunterlade, den Download aber unterbreche, bevor er abgeschlossen ist, der PHP-CGI-Prozess, der die Datei bedient, den Versuch blockiert, weitere Daten zu schreiben, bis das IPCCommTimeoutTimeout erreicht ist. Sobald das Timeout erreicht ist, wird der Prozess unterbrochen und beginnt wieder mit der Bearbeitung anderer Anfragen.

Gibt es eine Einstellung für fcgid, die ich so einrichten kann, dass es abgebrochen wird, wenn die Ausgabe nicht erfasst wird? Kann ich in PHP etwas tun, um damit umzugehen?

Das Problem tritt nicht auf, wenn der Download nicht unterbrochen wird. Tatsächlich ist es mir nur aufgefallen, weil ich versucht habe, eine FLV-Datei mit gddflvplayer zu streamen. Dieser scheint eine kurze Anfrage zu senden, um die ersten paar Frames abzurufen, und dann eine weitere, um sie abzuspielen, was dasselbe Problem verursacht.

Zu Ihrer Information, dies ist die Strace des hängenden CGI-Prozesses; er bleibt so, bis er schließlich unterbrochen wird, vermutlich vom Prozessmanager, wenn IPCCommTimeouterreicht wird. Ich vermute, dass er beim Versuch, die Ergebnisse des readfile()Aufrufs auszugeben, hängen bleibt, aber Apache hört nicht mehr zu (da die Anforderung vom Benutzer abgebrochen wurde).

root@some-machine:~# strace -p 24837
Process 24837 attached - interrupt to quit
write(3, "\5|A\313%\35\337\376\275\237\230\266\242\371\37YjzD<\322\215\357\336:M\362P\335\242\214\341"..., 17432

Die Protokolle zeigen, dass die Anfrage aufgrund des Timeouts letztendlich abgelehnt wird.

mod_fcgid: read data timeout in 240 seconds

Der Download-Code dient mehr oder weniger nur readfilezum Bereitstellen der Datei, wobei auch einige Header beteiligt sind (Hinweis: In diesem Code Headerhandelt es sich mehr oder weniger nur um einen Wrapper, header()um Probleme bei Tests zu vermeiden).

$filepath    = '/some/path/foo.flv';
$filename    = 'foo.flv';
$disposition = 'inline';

$h = Header::get();
$h->send('Pragma: public');
$h->send('Content-Transfer-Encoding: binary');
$h->send('Content-type: ' . FileSystem::get()->getMimeType($filepath));
$h->send('Content-Length: ' . FileSystem::get()->getFileSize($filepath));
$h->send('Content-Disposition: ' . $disposition . '; filename="' . $filename . '"');
$h->send('Content-transfer-encoding: 8bit');
$h->send('Expires: 0');
$h->send('Pragma: cache');
$h->send('Cache-Control: private');

flush();
readfile($filepath);

verwandte Informationen