Last active
February 28, 2018 06:04
-
-
Save ostretsov/7dd76ed02c9611717e858e5ab4070623 to your computer and use it in GitHub Desktop.
TeX to PDF async server (aerys, amphp)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"require": { | |
"amphp/amp": "^2.0", | |
"amphp/aerys": "^0.7.4", | |
"amphp/process": "^0.3.1" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
FROM php:7.2.2-cli-stretch | |
# tex | |
RUN apt-get update && apt-get install -y texlive-full\ | |
biber\ | |
tar\ | |
libarchive-tools | |
# composer | |
RUN curl -sS https://getcomposer.org/installer | php -- --filename=composer --install-dir=/bin | |
EXPOSE 1337 | |
# CMD & other stuff is up to you |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* (c) Artem Ostretsov <artem@ostretsov.ru> | |
* Created at 15.02.18 15:23. | |
*/ | |
use Aerys\{Bootable, Host, Request, Response, function router}; | |
date_default_timezone_set('Europe/Moscow'); | |
$router = router() | |
->route('POST', '/', function(Request $request, Response $response) { | |
// reset on timeout | |
$timeout = (int) getenv('TIMEOUT') ?: 3 * 60; // in seconds | |
$request->getLocalVar('logger')->debug($request->getLocalVar('request_microtime').': request timeout: '.$timeout); | |
Amp\Loop::delay($timeout * 1000, function () use ($request, $response) { | |
if (Response::NONE == $response->state()) { | |
$response->setStatus(408); | |
$response->end(); | |
$request->getLocalVar('logger')->debug($request->getLocalVar('request_microtime').': processing was stopped on timeout'); | |
} | |
}); | |
// parse POST body | |
parse_str(yield $request->getBody(), $params); | |
// param validation | |
$TeXFile = $params['tex_file'] ?? false; | |
if (!$TeXFile) { | |
$response->setStatus(400); | |
$message = '"tex_file" parameter is required!'; | |
$response->end($message); | |
$request->getLocalVar('logger')->warning($request->getLocalVar('request_microtime').': '.$message); | |
return; | |
} | |
if (!preg_match('/\.tex$/', $TeXFile)) { | |
$response->setStatus(400); | |
$message = '"tex_file" parameter must be a filepath with "tex" extension!'; | |
$response->end($message); | |
$request->getLocalVar('logger')->warning($request->getLocalVar('request_microtime').': '.$message); | |
return; | |
} | |
if (!file_exists($TeXFile)) { | |
$response->setStatus(404); | |
$message = sprintf('File "%s" not found!', $TeXFile); | |
$response->end($message); | |
$request->getLocalVar('logger')->warning($request->getLocalVar('request_microtime').': '.$message); | |
return; | |
} | |
$PDFFile = str_replace('.tex', '.pdf', $TeXFile); | |
if (file_exists($PDFFile)) { | |
$request->getLocalVar('logger')->debug($request->getLocalVar('request_microtime').': unlink '.$PDFFile); | |
unlink($PDFFile); | |
} | |
// generate PDF | |
$attempts = 2; // sometimes it's required to execute parsing twice to generate PDF | |
while ($attempts > 0) { | |
// timeout for abandoned processes | |
$command = sprintf('timeout %ds /usr/bin/pdflatex -interaction=batchmode -output-directory=%s %s', $timeout, dirname($TeXFile), $TeXFile); | |
$request->getLocalVar('logger')->debug($request->getLocalVar('request_microtime').': '.$command); | |
$process = new Amp\Process\Process($command, dirname($TeXFile)); | |
$process->start(); | |
yield new Amp\ByteStream\Message($process->getStdout()); | |
--$attempts; | |
} | |
if (Response::NONE == $response->state()) { | |
// change PDF and other files permissions | |
$response->write(''); // to prevent resetting on timeout | |
$command = sprintf('chown `stat -c "%%u:%%g" %s` %s/%s*', $TeXFile, dirname($TeXFile), pathinfo($TeXFile, PATHINFO_FILENAME)); | |
$request->getLocalVar('logger')->debug($request->getLocalVar('request_microtime').': '.$command); | |
$process = new Amp\Process\Process($command); | |
$process->start(); | |
yield new Amp\ByteStream\Message($process->getStdout()); | |
$response->end(); | |
} | |
$request->getLocalVar('logger')->debug($request->getLocalVar('request_microtime').': request is processed'); | |
}); | |
return (new Host) | |
->expose('*', 1337) | |
->use(new class implements Bootable { | |
private $logger; | |
function boot(Aerys\Server $server, Psr\Log\LoggerInterface $logger) { | |
$this->logger = $logger; | |
} | |
function __invoke(Aerys\Request $request, Aerys\Response $response) { | |
$requestMicrotime = microtime(true) * 10000; | |
$this->logger->debug($requestMicrotime.': request is recieved'); | |
$request->setLocalVar('logger', $this->logger); | |
$request->setLocalVar('request_microtime', $requestMicrotime); | |
} | |
}) | |
->use($router); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment