Skip to content
Browse files

Switch to a single phantomjs process

  • Loading branch information...
1 parent 52c5bb5 commit e54d33e435ecea1e00187ca47e0daef290b98db4 @fzaninotto committed Apr 22, 2012
Showing with 98 additions and 131 deletions.
  1. +0 −2 Readme.md
  2. +21 −10 app.js
  3. +6 −5 config/default.yaml
  4. +0 −26 lib/rasterize.js
  5. BIN public/favicon.ico
  6. +67 −61 routes/index.js
  7. +0 −23 scripts/rasterize.js
  8. +4 −4 scripts/rasterizer.js
View
2 Readme.md
@@ -91,9 +91,7 @@ Every minute, this script will refresh the two screenshots `google.png` and `yah
## TODO
* Allow to configure phantomjs options through YAML config
-* Limit the number of phantomjs processes to avoid server explosion
* Implement a simple queuing system forcing the use of asynchronous screenshots when the number of browser processes reaches the limit
-* Use phantomjs server capabilities to avoid closing browser processes and send them demands continously
## License
View
31 app.js
@@ -1,22 +1,33 @@
/**
* Module dependencies.
*/
+var config = require('config');
+var spawn = require('child_process').spawn;
var express = require('express');
-// global variables
-config = require('config');
-app = express.createServer();
+// rasterizer
+var rastconfig = config.rasterizer;
+var rasterizer = spawn(rastconfig.command, ['scripts/rasterizer.js', rastconfig.path, rastconfig.port, rastconfig.viewport]);
+console.log('Phantomjs internal server listening on port ' + rastconfig.port);
+process.on('exit', function() {
+ console.log('Stopping Phantomjs internal server');
+ rasterizer.kill();
+});
+process.on('SIGINT', function () {
+ process.exit();
+});
+// web service
+var app = express.createServer();
app.configure(function(){
app.use(app.router);
+ app.use(express.static(__dirname + '/public'))
+ app.set('rasterizerPath', rastconfig.path);
+ app.set('rasterizerPort', rastconfig.port);
});
-
app.configure('development', function() {
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
-
-require('./routes');
-
-app.listen(3000);
-
-console.log("Express server listening on port 3000");
+require('./routes')(app);
+app.listen(config.server.port);
+console.log('Express server listening on port ' + config.server.port);
View
11 config/default.yaml
@@ -1,6 +1,7 @@
-browser:
+rasterizer:
command: phantomjs
- viewport:
- width: 1024
- height: 600
-tmpdir: '/tmp'
+ port: 3001
+ path: '/tmp/'
+ viewport: '1024x600'
+server:
+ port: 3000
View
26 lib/rasterize.js
@@ -1,26 +0,0 @@
-var exec = require('child_process').exec;
-
-/**
- * Rasterize the given `url` and callback `(err, stdout, stderr)`.
- *
- * Options:
- *
- * - command: phantomjs command
- * - path: output file path
- * - viewportWidth: viewport width
- * - viewportHeight: viewport height
- *
- * @param {String} url
- * @param {Object} options
- * @param {Function} callback
- */
-
-module.exports = function(url, options, fn){
- var cmd = [options.command];
- cmd.push(__dirname + '/../scripts/rasterize.js');
- cmd.push(url);
- cmd.push(options.path);
- cmd.push(options.viewportWidth + 'x' + options.viewportHeight);
- cmd = cmd.join(' ');
- exec(cmd, fn);
-};
View
BIN public/favicon.ico
Binary file not shown.
View
128 routes/index.js
@@ -1,71 +1,77 @@
-var rasterize = require('../lib/rasterize');
var utils = require('../lib/utils');
var join = require('path').join;
var fs = require('fs');
var request = require('request');
-/*
- * Usage
- */
-app.get('/', function(req, res) {
- res.send("\
-USAGE:\n\
-\n\
-# Take a screenshot\n\
-GET /www.google.com\n\
-# Return a 1024x600 PNG screenshot of the www.google.com homepage\n\
-\n\
-# Custom viewport size\n\
-GET /www.google.com?width=800&height=600\n\
-# Return a 800x600 PNG screenshot of the www.google.com homepage\n\
-\n\
-# Asynchronous call\n\
-GET /www.google.com?callback=http://www.myservice.com/screenshot/google\n\
-# Return an empty response immediately (HTTP 200 OK),\n\
-# then send a POST request to the callback URL when the screenshot is ready\n\
-# with the PNG image in the body.\n");
-});
+module.exports = function(app) {
+ /*
+ * Usage
+ */
+ app.get('/', function(req, res) {
+ res.send("\
+ USAGE:\n\
+ \n\
+ # Take a screenshot\n\
+ GET /www.google.com\n\
+ # Return a 1024x600 PNG screenshot of the www.google.com homepage\n\
+ \n\
+ # Custom viewport size\n\
+ GET /www.google.com?width=800&height=600\n\
+ # Return a 800x600 PNG screenshot of the www.google.com homepage\n\
+ \n\
+ # Asynchronous call\n\
+ GET /www.google.com?callback=http://www.myservice.com/screenshot/google\n\
+ # Return an empty response immediately (HTTP 200 OK),\n\
+ # then send a POST request to the callback URL when the screenshot is ready\n\
+ # with the PNG image in the body.\n");
+ });
-/*
- * GET screenshot.
- */
-app.get('/:url(*)', function(req, res, next){
- var url = utils.url(req.param('url'));
- if (!url) return res.send(400);
-
- var id = utils.md5(url + Date.now());
-
- var options = {
- command: config.browser.command,
- path: join(config.tmpdir, id + '.png'),
- viewportWidth: req.param('width', config.browser.viewport.width),
- viewportHeight: req.param('height', config.browser.viewport.height)
- };
+ /*
+ * GET screenshot.
+ */
+ app.get('/:url', function(req, res, next) {
+ if (req.param('url') == 'favicon.ico') return next();
+ var url = utils.url(req.param('url'));
+ if (!url) return res.send(400);
+ var id = utils.md5(url + Date.now());
+ var filename = id + '.png';
+ var path = join(app.settings.rasterizerPath, filename);
+ // required options
+ var options = {
+ uri: 'http://localhost:' + app.settings.rasterizerPort + '/',
+ headers: { url: url, filename: filename }
+ };
+ ['width', 'height', 'clipRect', 'javascriptEnabled', 'loadImages', 'localToRemoteUrlAccessEnabled', 'userAgent', 'userName', 'password'].forEach(function(name) {
+ if (req.param(name, false)) options.headers[name] = req.param(name);
+ });
- console.log('screenshot - rasterizing %s %dx%d', url, options.viewportWidth, options.viewportHeight);
+ console.log('screenshot - rasterizing %s', url);
- if (req.param('callback', false)) {
- // asynchronous
- var callback = utils.url(req.param('callback'));
- res.send('Will post screenshot of ' + url + ' to ' + callback + ' when processed');
- rasterize(url, options, function(err) {
- console.log('screenshot - streaming to %s', callback);
- var fileStream = fs.createReadStream(options.path);
- fileStream.pipe(request.post(callback, function(err) {
- console.log('Error while streaming screenshot: %s', err.message);
- }));
- fileStream.on('end', function() {
- fs.unlink(options.path);
+ if (req.param('callback', false)) {
+ // asynchronous
+ var callback = utils.url(req.param('callback'));
+ res.send('Will post screenshot of ' + url + ' to ' + callback + ' when processed');
+ request.get(options, function(err) {
+ // FIXME: call the callback with an error
+ if (err) return console.log(err.message);
+ console.log('screenshot - streaming to %s', callback);
+ var fileStream = fs.createReadStream(path);
+ fileStream.pipe(request.post(callback, function(err) {
+ console.log('Error while streaming screenshot: %s', err.message);
+ }));
+ fileStream.on('end', function() {
+ fs.unlink(path);
+ });
});
- });
- } else {
- // synchronous
- rasterize(url, options, function(err) {
- if (err) return next(err);
- console.log('screenshot - sending response');
- res.sendfile(options.path, function(err) {
- fs.unlink(options.path);
+ } else {
+ // synchronous
+ request.get(options, function(err) {
+ if (err) return next(err);
+ console.log('screenshot - sending response');
+ res.sendfile(path, function(err) {
+ fs.unlink(path);
+ });
});
- });
- }
-});
+ }
+ });
+};
View
23 scripts/rasterize.js
@@ -1,23 +0,0 @@
-var page = new WebPage();
-
-var url = phantom.args[0];
-if (!url) throw new Error('url required');
-var path = phantom.args[1];
-if (!path) throw new Error('output path required');
-
-var size = phantom.args[2] || '';
-size = size.split('x');
-
-page.viewportSize = {
- width: ~~size[0] || 1024,
- height: ~~size[1] || 600
-};
-
-page.open(url, function (status) {
- if (status == 'success') {
- page.render(path);
- phantom.exit();
- } else {
- throw new Error('failed to load ' + url);
- }
-});
View
8 scripts/rasterizer.js
@@ -33,7 +33,7 @@ server = require('webserver').create();
* url: http://www.google.com
*
* Optional headers:
- * path: google.png
+ * filename: google.png
* width: 1024
* height: 600
* clipRect: { "top": 14, "left": 3, "width": 400, "height": 300 }
@@ -58,11 +58,11 @@ service = server.listen(port, function(request, response) {
return;
}
var url = request.headers.url;
- var path = basePath + (request.headers.path || (url.replace(new RegExp('https?://'), '').replace(/\//g, '.') + '.png'));
+ var path = basePath + (request.headers.filename || (url.replace(new RegExp('https?://'), '').replace(/\//g, '.') + '.png'));
var page = new WebPage();
page.viewportSize = {
- width: request.headers.viewportWidth || defaultViewportSize.width,
- height: request.headers.viewportHeight || defaultViewportSize.height
+ width: request.headers.width || defaultViewportSize.width,
+ height: request.headers.height || defaultViewportSize.height
};
if (request.headers.clipRect) {
page.clipRect = JSON.parse(request.headers.clipRect);

0 comments on commit e54d33e

Please sign in to comment.
Something went wrong with that request. Please try again.