Skip to content

Instantly share code, notes, and snippets.

@ostretsov
Last active February 28, 2018 06:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ostretsov/7dd76ed02c9611717e858e5ab4070623 to your computer and use it in GitHub Desktop.
Save ostretsov/7dd76ed02c9611717e858e5ab4070623 to your computer and use it in GitHub Desktop.
TeX to PDF async server (aerys, amphp)
{
"require": {
"amphp/amp": "^2.0",
"amphp/aerys": "^0.7.4",
"amphp/process": "^0.3.1"
}
}
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
<?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