Skip to content
This repository was archived by the owner on Mar 22, 2018. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
var config = require('config');
var express = require('express');
var RasterizerService = require('./lib/rasterizerService');
var FileCleanerService = require('./lib/fileCleanerService');

process.on('uncaughtException', function (err) {
console.error("[uncaughtException]", err);
Expand All @@ -20,16 +19,18 @@ process.on('SIGINT', function () {
});

// web service
var env = process.env.NODE_ENV || 'development';
var app = express();
app.configure(function(){
app.use(express.static(__dirname + '/public'))
app.use(app.router);
app.set('rasterizerService', new RasterizerService(config.rasterizer).startService());
app.set('fileCleanerService', new FileCleanerService(config.cache.lifetime));
});
app.configure('development', function() {
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.use(express.static(__dirname + '/public'));
app.use(app.router);
app.set('rasterizerService', new RasterizerService(config.rasterizer).startService());
if ('development' == env) {
app.use(express.errorHandler({dumpExceptions: true, showStack: true}));
} else {
app.use(function errorHandler(err, req, res, next) {
res.status(500).send();
});
}
require('./routes')(app, config.server.useCors);
app.listen(config.server.port, config.server.host);
console.log('Express server listening on ' + config.server.host + ':' + config.server.port);
12 changes: 7 additions & 5 deletions config/default.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
rasterizer:
command: phantomjs # phantomjs executable
port: 3001 # internal service port. No need to allow inbound or outbound access to this port
options:
- '--ssl-protocol=tlsv1'
- '--ignore-ssl-errors=yes'
port: 6001 # internal service port. No need to allow inbound or outbound access to this port
host: '127.0.0.1' # host to listen on
path: '/tmp/' # where the screenshot files are stored
viewport: '1024x600' # browser window size. Height frows according to the content
cache:
lifetime: 60000 # one minute, set to 0 for no cache
ttl: 60000 # lifetime of screenshots
viewport: '1024x600' # browser window size. Height grows according to the content
server:
port: 3000 # main service port
port: 6000 # main service port
host: '127.0.0.1' # host to listen on
useCors: false # enable CORS support
54 changes: 0 additions & 54 deletions lib/fileCleanerService.js

This file was deleted.

42 changes: 28 additions & 14 deletions lib/rasterizerService.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

var spawn = require('child_process').spawn;
var request = require('request');

var utils = require('../lib/utils');
/**
* Rasterizer service.
*
Expand All @@ -16,13 +16,12 @@ var request = require('request');
* path: Destination of temporary images
* viewport: Width and height represent the viewport size (format: '1024x800')
*
* @param {Object} Server configuration
* @param {Object} config
* @api public
*/
var RasterizerService = function(config) {
this.isStopping = false;
this.config = config;
this.rasterizer;
this.pingDelay = 10000; // every 10 seconds
this.sleepTime = 30000; // three failed health checks, 30 seconds
this.lastHealthCheckDate = null;
Expand All @@ -31,30 +30,38 @@ var RasterizerService = function(config) {
self.isStopping = true;
self.killService();
});
}
};

RasterizerService.prototype.rasterizerExitHandler = function (code) {
RasterizerService.prototype.rasterizerExitHandler = function () {
if (this.isStopping) return;
console.log('phantomjs failed; restarting');
this.startService();
};

RasterizerService.prototype.startService = function() {
var rasterizer = spawn(this.config.command, ['scripts/rasterizer.js', this.config.path, this.config.host + ':' + this.config.port, this.config.viewport]);
var options = this.config.options || [];
var args = options.concat(['scripts/rasterizer.js', this.config.path, this.config.host + ':' + this.config.port, this.config.viewport]);
var cmd = this.config.command;
cmd = !cmd || cmd === 'phantomjs' ? require('phantomjs').path : cmd;
console.log('Spawning phantomjs from:', cmd);
var rasterizer = spawn(cmd, args);
rasterizer.stderr.on('data', function (data) {
console.log('phantomjs error: ' + data);
});
rasterizer.stdout.on('data', function (data) {
console.log('phantomjs output: ' + data);
});
rasterizer.on('exit', this.rasterizerExitHandler);
console.log('Rasterizer starting up: Erasing all screenshots in ', this.config.path);
utils.eraseOldFiles(this.config.path, 0);
this.rasterizer = rasterizer;
this.lastHealthCheckDate = Date.now();
this.pingServiceIntervalId = setInterval(this.pingService.bind(this), this.pingDelay);
this.checkHealthIntervalId = setInterval(this.checkHealth.bind(this), 1000);
console.log('Phantomjs internal server listening on port ' + this.config.port);
this.purgeOldFilesIntervalId = setInterval(this.purgeOldFiles.bind(this), 60000);
console.log('Phantomjs internal server listening on port', this.config.port);
return this;
}
};

RasterizerService.prototype.killService = function() {
if (this.rasterizer) {
Expand All @@ -63,16 +70,19 @@ RasterizerService.prototype.killService = function() {
this.rasterizer.kill();
clearInterval(this.pingServiceIntervalId);
clearInterval(this.checkHealthIntervalId);
clearInterval(this.purgeOldFilesIntervalId);
console.log('Rasterizer shutting down: Erasing all screenshots in', this.config.path);
utils.eraseOldFiles(this.config.path, 0);
console.log('Stopping Phantomjs internal server');
}
}
};

RasterizerService.prototype.restartService = function() {
if (this.rasterizer) {
this.killService();
this.startService();
}
}
};

RasterizerService.prototype.pingService = function() {
if (!this.rasterizer) {
Expand All @@ -83,21 +93,25 @@ RasterizerService.prototype.pingService = function() {
if (error || response.statusCode != 200) return;
self.lastHealthCheckDate = Date.now();
});
}
};

RasterizerService.prototype.checkHealth = function() {
if (Date.now() - this.lastHealthCheckDate > this.sleepTime) {
console.log('Phantomjs process is sleeping. Restarting.');
this.restartService();
}
}
};

RasterizerService.prototype.purgeOldFiles = function() {
utils.eraseOldFiles(this.config.path, this.config.ttl);
};

RasterizerService.prototype.getPort = function() {
return this.config.port;
}
};

RasterizerService.prototype.getPath = function() {
return this.config.path;
}
};

module.exports = RasterizerService;
51 changes: 46 additions & 5 deletions lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

var crypto = require('crypto');
var crypto = require('crypto'),
fs = require('fs'),
path = require('path');

/**
* MD5 the given `str`.
Expand All @@ -17,6 +18,46 @@ exports.md5 = function(str) {
*/

exports.url = function(url){
if (~url.indexOf('://')) return url;
return 'http://' + url;
};
return (url.indexOf('://') >= 0) ? url : 'http://' + url;
};

/**
* Synchronously delete all files in [dirPath] older than [ttl]. Does not modify subdirectories.
*/

exports.eraseOldFiles = function(dirPath, ttl) {
ttl = ttl || 0;
var expireTime = Date.now() - ttl;
fs.readdirSync(dirPath).forEach(function(file) {
if (file.indexOf('screenshot_') === 0) {
try {
var filePath = path.join(dirPath, file);
var stats = fs.statSync(filePath);
if (stats.isFile() && expireTime > stats.mtime.getTime()) {
console.log('Erasing file: ' + filePath + ' (older than ' + ttl + 'ms)');
fs.unlinkSync(filePath);
}
} catch(e) {
if (e.code === 'ENOENT') {
console.log('Tried erasing file ' + filePath + ' but ENOENT thrown, skipping...');
} else {
throw e;
}
}
}
})
};

/**
* Delete file asynchronously if it exists.
*/

exports.eraseFile = function(filePath) {
if (filePath) {
fs.lstat(filePath, function(err, stats) {
if (!err && stats.isFile()) {
fs.unlink(filePath);
}
});
}
};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"dependencies": {
"express": "3.x",
"config": "0.4.15",
"request": "2.9.153"
"request": "2.9.153",
"phantomjs": "^1.9.15"
}
}
Loading