Skip to content

Commit

Permalink
Fix wkhtmltopdf crash on large PDF
Browse files Browse the repository at this point in the history
  • Loading branch information
francoisjacquet committed Jan 5, 2020
1 parent b487768 commit 193606c
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 11 deletions.
4 changes: 4 additions & 0 deletions CHANGES.md
@@ -1,6 +1,10 @@
# CHANGES
## RosarioSIS Student Information System

Changes in 5.6.4
----------------
- Fix wkhtmltopdf crash on large PDF in Wkhtmltopdf.php

Changes in 5.6.3
----------------
- Accessibility HTML format Input in MassAssignFees.php & MassAssignPayments.php
Expand Down
62 changes: 51 additions & 11 deletions classes/Wkhtmltopdf.php
Expand Up @@ -192,19 +192,59 @@ public function getFilePath()
protected function _exec($cmd, $input = "")
{
$result = array('stdout' => '', 'stderr' => '', 'return' => '');

$proc = proc_open($cmd, array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $pipes);
fwrite($pipes[0], $input);
fclose($pipes[0]);

$result['stdout'] = stream_get_contents($pipes[1]);
fclose($pipes[1]);

$result['stderr'] = stream_get_contents($pipes[2]);
fclose($pipes[2]);

/**
* We need to asynchronously process streams, as simple sequential stream_get_contents() risks deadlocking if the 2nd pipe's OS pipe buffer fills up before the 1st is fully consumed.
* The input is probably subject to the same risk.
*/
foreach ($pipes as $pipe) {
stream_set_blocking($pipe, 0);
}
$indexPipes = function(array $pipes) { return array_combine(array_map('intval', $pipes), $pipes); };
$allWritables = $indexPipes(array($pipes[0]));
$allReadables = $indexPipes(array($pipes[1], $pipes[2]));
$readablesNames = array((int)$pipes[1] => 'stdout', (int)$pipes[2] => 'stderr');
do {
$readables = $allReadables;
$writables = $allWritables;
$exceptables = null;
$selectTime = microtime(true);
$nStreams = stream_select($readables, $writables, $exceptables, null, null);
$selectTime = microtime(true) - $selectTime;
if ($nStreams === false) {
throw new \Exception('Error reading/writing to WKHTMLTOPDF');
}
foreach ($writables as $writable) {
$nBytes = fwrite($writable, $input);
if ($nBytes === false) {
throw new \Exception('Error writing to WKHTMLTOPDF');
}
if ($nBytes == strlen($input)) {
fclose($writable);
unset($allWritables[(int)$writable]);
$input = '';
} else {
$input = substr($input, $nBytes);
}
}
if (count($readables) > 0) {
if ($selectTime < 30e3) {
usleep(30e3 - $selectTime); // up to 30ms padding, so we don't burn so much time/CPU reading just 1 byte at a time.
}
foreach ($readables as $readable) {
$in = fread($readable, 0x10000);
if ($in === false) {
throw new \Exception('Error reading from WKHTMLTOPDF '.$readablesNames[$readable]);
}
$result[$readablesNames[(int)$readable]] .= $in;
if (feof($readable)) {
fclose($readable);
unset($allReadables[(int)$readable]);
}
}
}
} while (count($allReadables) > 0 || count($allWritables) > 0);
$result['return'] = proc_close($proc);

return $result;
}

Expand Down

0 comments on commit 193606c

Please sign in to comment.