Exports: Made pdf command timeout configurable
Added test to cover. For #5119
This commit is contained in:
		
							parent
							
								
									42264f402d
								
							
						
					
					
						commit
						6103a22feb
					
				| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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()}");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 = [
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue