Exports: Made pdf command timeout configurable
Some checks failed
lint-php / build (push) Has been cancelled
analyse-php / build (push) Has been cancelled
test-migrations / build (8.1) (push) Has been cancelled
test-migrations / build (8.2) (push) Has been cancelled
test-migrations / build (8.3) (push) Has been cancelled
test-php / build (8.1) (push) Has been cancelled
test-php / build (8.2) (push) Has been cancelled
test-php / build (8.3) (push) Has been cancelled

Added test to cover.
For #5119
This commit is contained in:
Dan Brown 2024-09-27 16:33:58 +01:00
parent 42264f402d
commit 6103a22feb
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
4 changed files with 34 additions and 2 deletions

View File

@ -334,6 +334,11 @@ EXPORT_PAGE_SIZE=a4
# Example: EXPORT_PDF_COMMAND="/scripts/convert.sh {input_html_path} {output_pdf_path}" # Example: EXPORT_PDF_COMMAND="/scripts/convert.sh {input_html_path} {output_pdf_path}"
EXPORT_PDF_COMMAND=false EXPORT_PDF_COMMAND=false
# Export PDF Command Timeout
# The number of seconds that the export PDF command will run before a timeout occurs.
# Only applies for the EXPORT_PDF_COMMAND option, not for DomPDF or wkhtmltopdf.
EXPORT_PDF_COMMAND_TIMEOUT=15
# Set path to wkhtmltopdf binary for PDF generation. # Set path to wkhtmltopdf binary for PDF generation.
# Can be 'false' or a path path like: '/home/bins/wkhtmltopdf' # Can be 'false' or a path path like: '/home/bins/wkhtmltopdf'
# When false, BookStack will attempt to find a wkhtmltopdf in the application # When false, BookStack will attempt to find a wkhtmltopdf in the application

View File

@ -29,6 +29,10 @@ return [
// Example: EXPORT_PDF_COMMAND="/scripts/convert.sh {input_html_path} {output_pdf_path}" // Example: EXPORT_PDF_COMMAND="/scripts/convert.sh {input_html_path} {output_pdf_path}"
'pdf_command' => env('EXPORT_PDF_COMMAND', false), 'pdf_command' => env('EXPORT_PDF_COMMAND', false),
// The amount of time allowed for PDF generation command to run
// before the process times out and is stopped.
'pdf_command_timeout' => env('EXPORT_PDF_COMMAND_TIMEOUT', 15),
// 2024-04: Snappy/WKHTMLtoPDF now considered deprecated in regard to BookStack support. // 2024-04: Snappy/WKHTMLtoPDF now considered deprecated in regard to BookStack support.
'snappy' => [ 'snappy' => [
'pdf_binary' => env('WKHTMLTOPDF', false), 'pdf_binary' => env('WKHTMLTOPDF', false),

View File

@ -5,6 +5,7 @@ namespace BookStack\Entities\Tools;
use BookStack\Exceptions\PdfExportException; use BookStack\Exceptions\PdfExportException;
use Knp\Snappy\Pdf as SnappyPdf; use Knp\Snappy\Pdf as SnappyPdf;
use Dompdf\Dompdf; use Dompdf\Dompdf;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
class PdfGenerator class PdfGenerator
@ -85,9 +86,15 @@ class PdfGenerator
file_put_contents($inputHtml, $html); file_put_contents($inputHtml, $html);
$timeout = intval(config('exports.pdf_command_timeout'));
$process = Process::fromShellCommandline($command); $process = Process::fromShellCommandline($command);
$process->setTimeout(15); $process->setTimeout($timeout);
try {
$process->run(); $process->run();
} catch (ProcessTimedOutException $e) {
throw new PdfExportException("PDF Export via command failed due to timeout at {$timeout} second(s)");
}
if (!$process->isSuccessful()) { if (!$process->isSuccessful()) {
throw new PdfExportException("PDF Export via command failed with exit code {$process->getExitCode()}, stdout: {$process->getOutput()}, stderr: {$process->getErrorOutput()}"); throw new PdfExportException("PDF Export via command failed with exit code {$process->getExitCode()}, stdout: {$process->getOutput()}, stderr: {$process->getErrorOutput()}");

View File

@ -529,6 +529,22 @@ class ExportTest extends TestCase
}, PdfExportException::class); }, PdfExportException::class);
} }
public function test_pdf_command_timout_option_limits_export_time()
{
$page = $this->entities->page();
$command = 'php -r \'sleep(4);\'';
config()->set('exports.pdf_command', $command);
config()->set('exports.pdf_command_timeout', 1);
$this->assertThrows(function () use ($page) {
$start = time();
$this->withoutExceptionHandling()->asEditor()->get($page->getUrl('/export/pdf'));
$this->assertTrue(time() < ($start + 3));
}, PdfExportException::class,
"PDF Export via command failed due to timeout at 1 second(s)");
}
public function test_html_exports_contain_csp_meta_tag() public function test_html_exports_contain_csp_meta_tag()
{ {
$entities = [ $entities = [