PHP + Fcgid trava se download for interrompido

PHP + Fcgid trava se download for interrompido

Observação:administradores, votem pela migração para ServerFault.

Estou usando uma configuração LAMP com PHP rodando em mod_fcgid. Para a maioria das solicitações isso funciona bem, mas percebi que quando faço o download de um arquivo, mas interrompo o download antes de ser concluído, o processo php-cgi que estava servindo o arquivo bloqueia a tentativa de gravar mais dados até que seja IPCCommTimeoutalcançado. Quando o tempo limite é atingido, o processo é interrompido e começa a atender outras solicitações novamente.

Existe alguma configuração disponível para fcgid que eu possa definir para abortar se nada estiver capturando a saída? Existe algo que eu possa fazer em PHP para lidar com isso?

O problema não ocorre se o download não for interrompido; na verdade, só percebi isso porque estava tentando transmitir um arquivo FLV usando gddflvplayer, que parece enviar uma breve solicitação para obter os primeiros quadros e depois outra para reproduzi-lo, e isso causa o mesmo problema.

Para sua informação, este é o caminho do processo cgi suspenso; fica assim até ser eventualmente interrompido, provavelmente pelo gerenciador de processos quando IPCCommTimeoutfor alcançado. Meu palpite é que ele está travando ao tentar gerar os resultados da readfile()chamada, mas o Apache não está mais ouvindo (pois a solicitação foi cancelada pelo usuário).

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

Os logs indicam que a solicitação eventualmente é colhida devido ao tempo limite

mod_fcgid: read data timeout in 240 seconds

O código de download é mais ou menos usado apenas readfilepara servir o arquivo, com alguns cabeçalhos envolvidos também (nota: neste código, Headeré mais ou menos apenas um wrapper header()para evitar problemas nos testes).

$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);

informação relacionada