Fix SVG glyphs and make the service more robust

Change-Id: I14d3ca2fdc89f8662b2e823b6c891c79db5bab5a
This commit is contained in:
Gabriel Wicke 2013-10-08 17:30:54 -07:00
parent fd8da050fb
commit 6c7f4e3b38
4 changed files with 86 additions and 69 deletions

View File

@ -57,7 +57,7 @@ window.engine = (new (function() {
// displayed like an image on some different page. // displayed like an image on some different page.
this.merge = function(svg) { this.merge = function(svg) {
var uses, var uses,
copied, copied = {},
k, k,
id, id,
texts, texts,
@ -69,19 +69,18 @@ window.engine = (new (function() {
// clone and copy all used paths into local defs. // clone and copy all used paths into local defs.
// xlink:href in uses FIX // xlink:href in uses FIX
uses = svg.getElementsByTagName('use'); uses = svg.getElementsByTagName('use');
copied = {};
for ( k = 0; k < uses.length; ++k) { for ( k = 0; k < uses.length; ++k) {
id = uses[k].getAttribute('href'); id = uses[k].getAttribute('href');
if (id && copied[id]) { if (id && copied[id]) {
uses[k].setAttribute('xlink:href', id); uses[k].setAttribute('xlink:href', id);
// Already copied, skip // Already copied, skip
continue; continue;
} }
defs.appendChild( defs.appendChild(
document.getElementById(id.substr(1)).cloneNode(true) document.getElementById(id.substr(1)).cloneNode(true)
); );
uses[k].setAttribute('xlink:href', id); uses[k].setAttribute('xlink:href', id);
copied[id] = true; copied[id] = true;
} }
// check for errors in svg. // check for errors in svg.
@ -94,6 +93,7 @@ window.engine = (new (function() {
svg.style.position = 'static'; svg.style.position = 'static';
tmpDiv = document.createElement('div'); tmpDiv = document.createElement('div');
tmpDiv.appendChild(defs);
tmpDiv.appendChild(svg); tmpDiv.appendChild(svg);
return tmpDiv.innerHTML; return tmpDiv.innerHTML;
}; };
@ -125,7 +125,7 @@ window.engine = (new (function() {
}); });
})); }));
} catch (err) { } catch (err) {
cb([latex, err.message, '-']); cb([latex, err, err]);
} }
} }
}; };

View File

@ -39,7 +39,8 @@ page.onCallback = function(data) {
data[0].length + 'B query, ERR ' + data[1][0] + t; data[0].length + 'B query, ERR ' + data[1][0] + t;
out = JSON.stringify({err:data[1][0],svg:data[1],mml:data[2],'log':log,'sucess':false}); out = JSON.stringify({err:data[1][0],svg:data[1],mml:data[2],'log':log,'sucess':false});
resp.write(out); resp.write(out);
//console.log(log); console.log(log);
phantom.exit(1);
} }
resp.close(); resp.close();
}; };

View File

@ -10,6 +10,7 @@ var express = require('express'),
http = require('http'), http = require('http'),
fs = require('fs'), fs = require('fs'),
child_process = require('child_process'), child_process = require('child_process'),
request = require('request'),
querystring = require('querystring'); querystring = require('querystring');
var config; var config;
@ -40,28 +41,32 @@ console.log( ' - ' + instanceName + ' loading...' );
var restarts = 10; var restarts = 10;
var backend, var backend,
backendStarting = false,
backendPort, backendPort,
requestQueue = []; requestQueue = [];
var startBackend = function () { // forward declaration
var handleRequests;
var backendCB = function () {
backendStarting = false;
handleRequests();
};
var startBackend = function (cb) {
if (backend) { if (backend) {
backend.kill(); backend.removeAllListeners();
backend.kill('SIGKILL');
} }
var backendCB = function (err, stdout, stderr) {
if (err) {
restarts--;
if (restarts > 0) {
startBackend();
}
console.error(err.toString());
process.exit(1);
}
};
backendPort = Math.floor(9000 + Math.random() * 50000); backendPort = Math.floor(9000 + Math.random() * 50000);
console.error(instanceName + ': Starting backend on port ' + backendPort); console.error(instanceName + ': Starting backend on port ' + backendPort);
backend = child_process.exec('phantomjs main.js ' + backendPort, backendCB); backend = child_process.spawn('phantomjs', ['main.js', backendPort]);
backend.stdout.pipe(process.stdout); backend.stdout.pipe(process.stderr);
backend.stderr.pipe(process.stderr); backend.stderr.pipe(process.stderr);
backend.on('close', startBackend);
backendStarting = true;
// give backend 1 seconds to start up
setTimeout(backendCB, 1000);
}; };
startBackend(); startBackend();
@ -86,57 +91,67 @@ app.get(/^\/robots.txt$/, function ( req, res ) {
res.end( "User-agent: *\nDisallow: /\n" ); res.end( "User-agent: *\nDisallow: /\n" );
}); });
var handleRequests = function() {
// Call the next request on the queue
if (requestQueue.length) {
requestQueue[0]();
}
};
function handleRequest(req, res, tex) { function handleRequest(req, res, tex) {
// do the backend request // do the backend request
var query = new Buffer(querystring.stringify({tex:tex})), var reqbody = new Buffer(querystring.stringify({tex: tex})),
options = { options = {
hostname: 'localhost', method: 'POST',
port: backendPort.toString(), uri: 'http://localhost:' + backendPort.toString() + '/',
path: '/', body: reqbody,
method: 'POST', // Work around https://github.com/ariya/phantomjs/issues/11421 by
headers: { // setting explicit upper-case headers (request sends them lowercase
'Content-Length': query.length, // by default) and manually encoding the body.
'Content-Type': 'application/x-www-form-urlencoded', headers: {
'Connection': 'close' 'Content-Length': reqbody.length,
}, 'Content-Type': 'application/x-www-form-urlencoded'
agent: false },
}; timeout: 2000
var chunks = []; };
//console.log(options); request(options, function (err, response, body) {
var httpreq = http.request(options, function(httpres) { body = new Buffer(body);
httpres.on('data', function(chunk) { if (err || response.statusCode !== 200) {
chunks.push(chunk); var errBuf;
}); if (err) {
httpres.on('end', function() { errBuf = new Buffer(JSON.stringify({
var buf = Buffer.concat(chunks); tex: tex,
res.writeHead(200, log: err.toString(),
{ success: false
'Content-type': 'application/json', }));
'Content-length': buf.length } else {
}); errBuf = body;
res.write(buf); }
res.end(); res.writeHead(500,
{
'Content-Type': 'application/json',
'Content-Length': errBuf.length
});
res.end(errBuf);
// don't retry the request
requestQueue.shift(); requestQueue.shift();
handleRequests(); startBackend();
}); return handleRequests();
}
res.writeHead(200,
{
'Content-Type': 'application/json',
'Content-length': body.length
});
res.end(body);
requestQueue.shift();
handleRequests();
}); });
httpreq.on('error', function(err) {
console.log('error', err.toString());
res.writeHead(500);
return res.end(JSON.stringify({error: "Backend error: " + err.toString()}));
});
httpreq.end(query);
} }
handleRequests = function() {
// Call the next request on the queue
if (!backendStarting && requestQueue.length) {
requestQueue[0]();
}
};
app.post(/^\/$/, function ( req, res ) { app.post(/^\/$/, function ( req, res ) {
// First some rudimentary input validation // First some rudimentary input validation
if (!req.body.tex) { if (!req.body.tex) {

View File

@ -4,6 +4,7 @@
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"querystring": "0.x.x", "querystring": "0.x.x",
"express": "2.5.x" "express": "2.5.x",
"request": "2.x.x"
} }
} }