Skip to content

Commit 6103a22

Browse files
committed
Exports: Made pdf command timeout configurable
Added test to cover. For #5119
1 parent 42264f4 commit 6103a22

File tree

4 files changed

+34
-2
lines changed

4 files changed

+34
-2
lines changed

.env.example.complete

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,11 @@ EXPORT_PAGE_SIZE=a4
334334
# Example: EXPORT_PDF_COMMAND="/scripts/convert.sh {input_html_path} {output_pdf_path}"
335335
EXPORT_PDF_COMMAND=false
336336

337+
# Export PDF Command Timeout
338+
# The number of seconds that the export PDF command will run before a timeout occurs.
339+
# Only applies for the EXPORT_PDF_COMMAND option, not for DomPDF or wkhtmltopdf.
340+
EXPORT_PDF_COMMAND_TIMEOUT=15
341+
337342
# Set path to wkhtmltopdf binary for PDF generation.
338343
# Can be 'false' or a path path like: '/home/bins/wkhtmltopdf'
339344
# When false, BookStack will attempt to find a wkhtmltopdf in the application

app/Config/exports.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929
// Example: EXPORT_PDF_COMMAND="/scripts/convert.sh {input_html_path} {output_pdf_path}"
3030
'pdf_command' => env('EXPORT_PDF_COMMAND', false),
3131

32+
// The amount of time allowed for PDF generation command to run
33+
// before the process times out and is stopped.
34+
'pdf_command_timeout' => env('EXPORT_PDF_COMMAND_TIMEOUT', 15),
35+
3236
// 2024-04: Snappy/WKHTMLtoPDF now considered deprecated in regard to BookStack support.
3337
'snappy' => [
3438
'pdf_binary' => env('WKHTMLTOPDF', false),

app/Entities/Tools/PdfGenerator.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use BookStack\Exceptions\PdfExportException;
66
use Knp\Snappy\Pdf as SnappyPdf;
77
use Dompdf\Dompdf;
8+
use Symfony\Component\Process\Exception\ProcessTimedOutException;
89
use Symfony\Component\Process\Process;
910

1011
class PdfGenerator
@@ -85,9 +86,15 @@ protected function renderUsingCommand(string $html): string
8586

8687
file_put_contents($inputHtml, $html);
8788

89+
$timeout = intval(config('exports.pdf_command_timeout'));
8890
$process = Process::fromShellCommandline($command);
89-
$process->setTimeout(15);
90-
$process->run();
91+
$process->setTimeout($timeout);
92+
93+
try {
94+
$process->run();
95+
} catch (ProcessTimedOutException $e) {
96+
throw new PdfExportException("PDF Export via command failed due to timeout at {$timeout} second(s)");
97+
}
9198

9299
if (!$process->isSuccessful()) {
93100
throw new PdfExportException("PDF Export via command failed with exit code {$process->getExitCode()}, stdout: {$process->getOutput()}, stderr: {$process->getErrorOutput()}");

tests/Entity/ExportTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,22 @@ public function test_pdf_command_option_errors_if_command_returns_error_status()
529529
}, PdfExportException::class);
530530
}
531531

532+
public function test_pdf_command_timout_option_limits_export_time()
533+
{
534+
$page = $this->entities->page();
535+
$command = 'php -r \'sleep(4);\'';
536+
config()->set('exports.pdf_command', $command);
537+
config()->set('exports.pdf_command_timeout', 1);
538+
539+
$this->assertThrows(function () use ($page) {
540+
$start = time();
541+
$this->withoutExceptionHandling()->asEditor()->get($page->getUrl('/export/pdf'));
542+
543+
$this->assertTrue(time() < ($start + 3));
544+
}, PdfExportException::class,
545+
"PDF Export via command failed due to timeout at 1 second(s)");
546+
}
547+
532548
public function test_html_exports_contain_csp_meta_tag()
533549
{
534550
$entities = [

0 commit comments

Comments
 (0)