BVB Source Codes

prerender Show server.js Source code

Return Download prerender: download server.js Source code - Download prerender Source code - Type:.js
  1. var phridge = require('phridge')
  2.   , _ = require('lodash')
  3.   , util = require('./util.js')
  4.   , zlib = require('zlib')
  5.   , blockedResources = require('./resources/blocked-resources.json');
  6.  
  7. var COOKIES_ENABLED = process.env.COOKIES_ENABLED || false;
  8.  
  9. var PAGE_DONE_CHECK_TIMEOUT = process.env.PAGE_DONE_CHECK_TIMEOUT || 300;
  10.  
  11. var RESOURCE_DOWNLOAD_TIMEOUT = process.env.RESOURCE_DOWNLOAD_TIMEOUT || 10 * 1000;
  12.  
  13. var WAIT_AFTER_LAST_REQUEST = process.env.WAIT_AFTER_LAST_REQUEST || 500;
  14.  
  15. var JS_CHECK_TIMEOUT = process.env.JS_CHECK_TIMEOUT || 300;
  16.  
  17. var JS_TIMEOUT = process.env.JS_TIMEOUT || 10 * 1000;
  18.  
  19. var NO_JS_EXECUTION_TIMEOUT = process.env.NO_JS_EXECUTION_TIMEOUT || 3000;
  20.  
  21. var EVALUATE_JAVASCRIPT_CHECK_TIMEOUT = process.env.EVALUATE_JAVASCRIPT_CHECK_TIMEOUT || 300;
  22.  
  23. var NUM_ITERATIONS = process.env.NUM_ITERATIONS || 40;
  24.  
  25. var NUM_SOFT_ITERATIONS = process.env.NUM_SOFT_ITERATIONS || 30;
  26.  
  27. var server = exports = module.exports = {};
  28.  
  29. server.init = function(options) {
  30.     this.plugins = this.plugins || [];
  31.     this.options = options;
  32.  
  33.     return this;
  34. };
  35.  
  36. server.start = function() {
  37.     if(!this.options.isMaster) {
  38.         this.createPhantom();
  39.     }
  40. };
  41.  
  42. server.use = function(plugin) {
  43.     this.plugins.push(plugin);
  44.     if (typeof plugin.init === 'function') plugin.init(this);
  45. };
  46.  
  47. server._pluginEvent = function(methodName, args, callback) {
  48.     var _this = this
  49.       , index = 0
  50.       , next;
  51.  
  52.     next = function() {
  53.         var layer = _this.plugins[index++];
  54.         if (!layer) return callback();
  55.  
  56.         var method = layer[methodName];
  57.  
  58.         if (method) {
  59.             method.apply(layer, args);
  60.         } else {
  61.             next();
  62.         }
  63.     };
  64.  
  65.     args.push(next);
  66.     next();
  67. };
  68.  
  69. server.createPhantom = function() {
  70.     var _this = this;
  71.  
  72.     var args = {'--load-images': false, '--ignore-ssl-errors': true, '--ssl-protocol': 'tlsv1.2'};
  73.  
  74.     if(this.options.phantomArguments && !_.isEmpty(this.options.phantomArguments)) {
  75.         args = _.clone(this.options.phantomArguments);
  76.     }
  77.  
  78.     util.log('starting phantom...');
  79.  
  80.     if(this.options.onStdout) {
  81.       phridge.config.stdout = this.options.onStdout;
  82.     }
  83.  
  84.     if(this.options.onStderr) {
  85.       phridge.config.stderr = this.options.onStderr;
  86.     }
  87.  
  88.     phridge.spawn(args).then(_.bind(_this.onPhantomCreate, _this));
  89. };
  90.  
  91. server.onPhantomCreate = function(phantom) {
  92.     var _this = this;
  93.  
  94.     util.log('started phantom');
  95.     this.phantom = phantom;
  96.     this.phantom.id = Math.random().toString(36);
  97.     this.phantom.requestsInFlight = 0;
  98.     this.phantom.iteration = 0;
  99.  
  100.     //send the current phantomjs pid to the cluster master in order to make sure phantomjs is properly killed if this worker dies
  101.     process.send({phantomjsPid: this.phantom.childProcess.pid});
  102.  
  103.     this.phantom.on('unexpectedExit', function(err) {
  104.         util.log('phantom crashed, restarting...');
  105.  
  106.         function restartPhantom() {
  107.             process.nextTick(_.bind(_this.createPhantom, _this));
  108.         }
  109.  
  110.         _this._disposeAll(restartPhantom);
  111.     });
  112. };
  113.  
  114. server.onRequest = function(req, res) {
  115.     var _this = this;
  116.  
  117.     // Create a partial out of the _send method for the convenience of plugins
  118.     res.send = _.bind(this._send, this, req, res);
  119.  
  120.     req.prerender = {
  121.         url: util.getUrl(req),
  122.         start: new Date()
  123.     };
  124.  
  125.     util.log('getting', req.prerender.url);
  126.  
  127.     this._pluginEvent("beforePhantomRequest", [req, res], function() {
  128.         _this.createPage(req, res);
  129.     });
  130. };
  131.  
  132. server.createPage = function(req, res) {
  133.     var _this = this;
  134.  
  135.     if(!this.phantom) {
  136.         setTimeout(function(){
  137.             _this.createPage(req, res);
  138.         }, 50);
  139.     } else {
  140.         req.prerender.phantomId = this.phantom.id;
  141.         req.prerender.page = this.phantom.createPage();
  142.  
  143.         this.phantom.requestsInFlight++;
  144.         this.onPhantomPageCreate(req, res);
  145.     }
  146. };
  147.  
  148. server.onPhantomPageCreate = function(req, res) {
  149.     var _this = this;
  150.  
  151.     req.prerender.stage = 0;
  152.     req.prerender.pendingRequests = 0;
  153.  
  154.     this.phantom.run((req.prerender.cookiesEnabled || _this.options.cookiesEnabled || COOKIES_ENABLED), function(cookiesEnabled) {
  155.         this.cookiesEnabled = cookiesEnabled;
  156.     });
  157.  
  158.     if(req.prerender.isPageClosed) {
  159.         return res.send(504);
  160.     }
  161.  
  162.     this.clearLocalStorage(req, res);
  163.     this.clearMemoryCache(req, res);
  164.  
  165.     req.prerender.page.run(_this.options.blockedResources || blockedResources, !!_this.options.logRequests, function(blockedResources, logRequests, resolve, reject) {
  166.  
  167.         var _this = this;
  168.         this.prerender = {
  169.             resourcesRequested: [],
  170.             resourcesReceived: [],
  171.             resourcesTimeout: [],
  172.             lastResourceReceived: null
  173.         };
  174.  
  175.         this.onResourceRequested = function(requestData, request) {
  176.             for(var i = 0,l = blockedResources.length; i < l; i++) {
  177.                 var regex = new RegExp(blockedResources[i], 'gi');
  178.                 if(regex.test(requestData.url)) {
  179.                     request.abort();
  180.                     requestData.aborted = true;
  181.                     break;
  182.                 }
  183.             }
  184.  
  185.             if(!requestData.aborted) {
  186.                 _this.prerender.resourcesRequested.push(requestData);
  187.  
  188.                 if(logRequests) {
  189.                     console.log(new Date().toISOString(), '+', _this.prerender.resourcesRequested.length - _this.prerender.resourcesReceived.length - _this.prerender.resourcesTimeout.length, requestData.url);
  190.                 }
  191.             }
  192.         };
  193.  
  194.         this.onResourceReceived = function(response) {
  195.             _this.prerender.lastResourceReceived = new Date();
  196.  
  197.  
  198.             if(response.id === 1) {
  199.                 _this.prerender.headers = response.headers;
  200.                 _this.prerender.statusCode = response.status;
  201.                 _this.prerender.redirectURL = response.redirectURL;
  202.             }
  203.  
  204.             if ('end' === response.stage) {
  205.                 if(response.url) {
  206.                     _this.prerender.resourcesReceived.push(response);
  207.  
  208.                     if(logRequests) {
  209.                         console.log(new Date().toISOString(), '-', _this.prerender.resourcesRequested.length - _this.prerender.resourcesReceived.length - _this.prerender.resourcesTimeout.length, response.url);
  210.                     }
  211.                 }
  212.  
  213.                 if (response.id === 1) {
  214.                     _this.prerender.statusCode = response.status;
  215.                 }
  216.             }
  217.         };
  218.  
  219.         this.onResourceTimeout = function(request) {
  220.             if(request.url) {
  221.                 _this.prerender.resourcesTimeout.push(request);
  222.             }
  223.         }
  224.  
  225.         this.onResourceError = function(resourceError) {
  226.             if(resourceError.url && logRequests) {
  227.                 console.log('error loading URL:', JSON.stringify(resourceError));
  228.             }
  229.         }
  230.  
  231.         this.viewportSize = { width: 1440, height: 718 };
  232.         this.settings.userAgent = this.settings.userAgent + ' Prerender (+https://github.com/prerender/prerender)';
  233.  
  234.         resolve();
  235.  
  236.     });
  237.  
  238.     req.prerender.page.run(function(resolve) {
  239.         this.onClosing = function() {
  240.             resolve(true);
  241.         };
  242.     }).then(function(isPageClosed) {
  243.         req.prerender.isPageClosed = isPageClosed;
  244.     });
  245.  
  246.     // Fire off a middleware event, then download all of the assets
  247.     _this._pluginEvent("onPhantomPageCreate", [_this.phantom, req, res], function() {
  248.         req.prerender.downloadStarted = req.prerender.lastResourceReceived = new Date();
  249.  
  250.         req.prerender.downloadChecker = setInterval(function() {
  251.             _this.checkIfPageIsDoneLoading(req, res);
  252.         }, (req.prerender.pageDoneCheckTimeout || _this.options.pageDoneCheckTimeout || PAGE_DONE_CHECK_TIMEOUT));
  253.  
  254.         if(req.prerender.isPageClosed) {
  255.             return res.send(504);
  256.         }
  257.  
  258.         var urlToFetch = req.prerender.url;
  259.         if(_this.shouldEncodeURLBeforeBrowserFetch(req)) {
  260.           urlToFetch = encodeURI(req.prerender.url).replace('%2523', '%23')
  261.         }
  262.  
  263.         req.prerender.page.run(urlToFetch, function(url, resolve, reject) {
  264.  
  265.             this.open(url, function(status) {
  266.                 resolve(status);
  267.             });
  268.         }).then(function(status) {
  269.             req.prerender.status = status;
  270.         });
  271.     });
  272. };
  273.  
  274. // Called occasionally to check if a page is completely loaded
  275. server.checkIfPageIsDoneLoading = function(req, res) {
  276.     var _this = this;
  277.  
  278.     if(req.prerender.stage >= 2) return;
  279.  
  280.     if (!this.phantom || this.phantom.id !== req.prerender.phantomId) {
  281.         util.log('PhantomJS restarted in the middle of this request. Aborting...')
  282.         clearInterval(req.prerender.downloadChecker);
  283.         req.prerender.downloadChecker = null;
  284.         return res.send(504);
  285.     }
  286.  
  287.     if(req.prerender.isPageClosed) {
  288.         util.log('PhantomJS page was closed in the middle of this request. Aborting...')
  289.         clearInterval(req.prerender.downloadChecker);
  290.         req.prerender.downloadChecker = null;
  291.         return res.send(504);
  292.     }
  293.  
  294.     req.prerender.page.run(function(resolve) {
  295.         resolve(this.prerender);
  296.  
  297.     }).then(function(response) {
  298.         req.prerender.pendingRequests = response.resourcesRequested.length - response.resourcesReceived.length - response.resourcesTimeout.length;
  299.         req.prerender.lastResourceReceived = new Date(response.lastResourceReceived);
  300.         req.prerender.headers = response.headers;
  301.         req.prerender.statusCode = response.statusCode;
  302.         req.prerender.redirectURL = response.redirectURL;
  303.  
  304.         var match = _.find(req.prerender.headers, { name: 'Location' });
  305.         if (match) {
  306.             req.prerender.redirectURL = util.normalizeUrl(match.value);
  307.         }
  308.  
  309.         if(req.prerender.statusCode && req.prerender.statusCode >= 300 && req.prerender.statusCode <= 399) {
  310.             // Finish up if we got a redirect status code
  311.             clearInterval(req.prerender.downloadChecker);
  312.             req.prerender.downloadChecker = null;
  313.  
  314.             if(req.prerender.stage >= 2) return;
  315.             return res.send(req.prerender.statusCode);
  316.         }
  317.  
  318.         var timedOut = new Date().getTime() - req.prerender.downloadStarted.getTime() > (req.prerender.resourceDownloadTimeout || _this.options.resourceDownloadTimeout || RESOURCE_DOWNLOAD_TIMEOUT)
  319.           , timeSinceLastRequest = new Date().getTime() - req.prerender.lastResourceReceived.getTime();
  320.  
  321.         if(req.prerender.status === 'fail' && !_this.overridePageFailure(req)) {
  322.             clearInterval(req.prerender.downloadChecker);
  323.             req.prerender.downloadChecker = null;
  324.  
  325.             req.prerender.statusCode = 504;
  326.             return res.send(req.prerender.statusCode);
  327.         }
  328.  
  329.         // Check against the current stage to make sure we don't finish more than
  330.         // once, and check against a bunch of states that would signal finish - if
  331.         // resource downloads have timed out, if the page has errored out, or if
  332.         // there are no pending requests left
  333.         if(req.prerender.stage < 1 && (req.prerender.status !== null && req.prerender.pendingRequests <= 0 && (timeSinceLastRequest > (req.prerender.waitAfterLastRequest || _this.options.waitAfterLastRequest || WAIT_AFTER_LAST_REQUEST)) || timedOut)) {
  334.             req.prerender.stage = 1;
  335.             clearInterval(req.prerender.downloadChecker);
  336.             req.prerender.downloadChecker = null;
  337.             req.prerender.downloadFinished = new Date();
  338.  
  339.             req.prerender.timeoutChecker = setInterval(_.bind(_this.checkIfJavascriptTimedOut, _this, req, res), (req.prerender.jsCheckTimeout || _this.options.jsCheckTimeout || JS_CHECK_TIMEOUT));
  340.             _this.evaluateJavascriptOnPage(req, res);
  341.         }
  342.     }).catch(function(err) {
  343.         console.log(err);
  344.     });
  345. };
  346.  
  347. // sometimes older versions of phantomjs would report a "fail" for a page even though the page still loaded correctly.
  348. // this is to let you override that failure and just continue on based on URL pattern or anything like that
  349. server.overridePageFailure = function(req) {
  350.     return false;
  351. };
  352.  
  353. // this is to let you override the encodeURI before fetching with PhantomJS.
  354. // useful for cases where you might want to allow certain encoded slashes in the url
  355. server.shouldEncodeURLBeforeBrowserFetch = function(req) {
  356.     return true;
  357. };
  358.  
  359. // Checks to see if the execution of javascript has timed out
  360. server.checkIfJavascriptTimedOut = function(req, res) {
  361.  
  362.     var timeout = new Date().getTime() - req.prerender.downloadFinished.getTime() > (req.prerender.jsTimeout || this.options.jsTimeout || JS_TIMEOUT);
  363.     var lastJsExecutionWasLessThanTwoSecondsAgo = req.prerender.lastJavascriptExecution && (new Date().getTime() - req.prerender.lastJavascriptExecution.getTime() < 2000);
  364.     var noJsExecutionInFirstSecond = !req.prerender.lastJavascriptExecution && (new Date().getTime() - req.prerender.downloadFinished.getTime() > (req.prerender.noJsExecutionTimeout || this.options.noJsExecutionTimeout || NO_JS_EXECUTION_TIMEOUT));
  365.  
  366.     if (!this.phantom || this.phantom.id !== req.prerender.phantomId) {
  367.         util.log('PhantomJS restarted in the middle of this request. Aborting...');
  368.         clearInterval(req.prerender.timeoutChecker);
  369.         req.prerender.timeoutChecker = null;
  370.  
  371.         res.send(504);
  372.  
  373.     } else if (timeout && lastJsExecutionWasLessThanTwoSecondsAgo) {
  374.         util.log('Timed out. Sending request with HTML on the page');
  375.         clearInterval(req.prerender.timeoutChecker);
  376.         req.prerender.timeoutChecker = null;
  377.  
  378.         this.onPageEvaluate(req, res);
  379.     } else if ((timeout && req.prerender.stage < 2) || noJsExecutionInFirstSecond) {
  380.         util.log('Experiencing infinite javascript loop. Killing phantomjs...');
  381.         clearInterval(req.prerender.timeoutChecker);
  382.         req.prerender.timeoutChecker = null;
  383.  
  384.         res.send(504, {abort: true});
  385.     }
  386. };
  387.  
  388. // Evaluates the javascript on the page
  389. server.evaluateJavascriptOnPage = function(req, res) {
  390.     var _this = this;
  391.  
  392.     if(req.prerender.stage >= 2) return;
  393.  
  394.     if(req.prerender.isPageClosed) {
  395.         clearInterval(req.prerender.timeoutChecker);
  396.         req.prerender.timeoutChecker = null;
  397.         return res.send(504);
  398.     }
  399.  
  400.     req.prerender.page.run(function(resolve, reject) {
  401.  
  402.         var obj = this.evaluate(function() {
  403.             try {
  404.                 var doctype = ''
  405.                   , html = document && document.getElementsByTagName('html');
  406.  
  407.                 if(document.doctype) {
  408.                     doctype = "<!DOCTYPE "
  409.                          + document.doctype.name
  410.                          + (document.doctype.publicId ? ' PUBLIC "' + document.doctype.publicId + '"' : '')
  411.                          + (!document.doctype.publicId && document.doctype.systemId ? ' SYSTEM' : '')
  412.                          + (document.doctype.systemId ? ' "' + document.doctype.systemId + '"' : '')
  413.                          + '>';
  414.                 }
  415.  
  416.                 if (html && html[0]) {
  417.                     return {
  418.                         html: doctype + html[0].outerHTML,
  419.                         shouldWaitForPrerenderReady: typeof window.prerenderReady === 'boolean',
  420.                         prerenderReady: window.prerenderReady
  421.                     };
  422.                 }
  423.  
  424.             } catch (e) { }
  425.  
  426.             return {
  427.                 html: '',
  428.                 shouldWaitForPrerenderReady: false,
  429.                 prerenderReady: window.prerenderReady
  430.             }
  431.  
  432.         });
  433.  
  434.         resolve(obj);
  435.  
  436.     }).then(function(obj) {
  437.         // Update the evaluated HTML
  438.         req.prerender.documentHTML = obj.html;
  439.         req.prerender.lastJavascriptExecution = new Date();
  440.  
  441.         if(!obj.shouldWaitForPrerenderReady || (obj.shouldWaitForPrerenderReady && obj.prerenderReady)) {
  442.             clearInterval(req.prerender.timeoutChecker);
  443.             req.prerender.timeoutChecker = null;
  444.  
  445.             _this.onPageEvaluate(req, res);
  446.         } else {
  447.             setTimeout(_.bind(_this.evaluateJavascriptOnPage, _this, req, res), (req.prerender.evaluateJavascriptCheckTimeout || _this.options.evaluateJavascriptCheckTimeout || EVALUATE_JAVASCRIPT_CHECK_TIMEOUT));
  448.         };
  449.     }).catch(function(err) {
  450.         util.log('error evaluating javascript', err);
  451.         setTimeout(_.bind(_this.evaluateJavascriptOnPage, _this, req, res), (req.prerender.evaluateJavascriptCheckTimeout || _this.options.evaluateJavascriptCheckTimeout || EVALUATE_JAVASCRIPT_CHECK_TIMEOUT));
  452.     });
  453. };
  454.  
  455. // Called when we're done evaluating the javascript on the page
  456. server.onPageEvaluate = function(req, res) {
  457.  
  458.     if(req.prerender.stage >= 2) return;
  459.  
  460.     req.prerender.stage = 2;
  461.  
  462.     if (!req.prerender.documentHTML) {
  463.         res.send(req.prerender.statusCode || 404);
  464.     } else {
  465.         this._pluginEvent("afterPhantomRequest", [req, res], function() {
  466.             res.send(req.prerender.statusCode || 200);
  467.         });
  468.     }
  469. };
  470.  
  471. server.clearLocalStorage = function(req, res) {
  472.     if(!req.prerender.page || req.prerender.isPageClosed) {
  473.         return;
  474.     }
  475.  
  476.     req.prerender.page.run(function() {
  477.         try {
  478.             if(localStorage && typeof localStorage.clear == 'function') {
  479.                 localStorage.clear();
  480.             }
  481.         } catch (e) {}
  482.     });
  483. };
  484.  
  485. server.clearMemoryCache = function(req, res) {
  486.     if(!req.prerender.page || req.prerender.isPageClosed) {
  487.         return;
  488.     }
  489.  
  490.     req.prerender.page.run(function() {
  491.         try {
  492.             if(this.clearMemoryCache && typeof this.clearMemoryCache == 'function') {
  493.                 this.clearMemoryCache();
  494.             }
  495.         } catch (e) {}
  496.     });
  497. };
  498.  
  499. server._send = function(req, res, statusCode, options) {
  500.     var _this = this;
  501.  
  502.     if(req.prerender.page) {
  503.         req.prerender.page.dispose().then(function() {
  504.             req.prerender.page = null;
  505.         });
  506.     }
  507.     req.prerender.stage = 2;
  508.  
  509.     req.prerender.documentHTML = options || req.prerender.documentHTML;
  510.     req.prerender.statusCode = statusCode || req.prerender.statusCode;
  511.  
  512.     if(req.prerender.statusCode) {
  513.         req.prerender.statusCode = parseInt(req.prerender.statusCode);
  514.     }
  515.  
  516.     if (options && typeof options === 'object' && !Buffer.isBuffer(options)) {
  517.         req.prerender.documentHTML = options.documentHTML;
  518.         req.prerender.redirectURL = options.redirectURL;
  519.     }
  520.  
  521.     this._pluginEvent("beforeSend", [req, res], function() {
  522.  
  523.         if (req.prerender.headers && req.prerender.headers.length) {
  524.             req.prerender.headers.forEach(function(header) {
  525.                 try {
  526.                     res.setHeader(header.name, header.value);
  527.                 } catch(e) {
  528.                     util.log('unable to set header:', header.name);
  529.                 }
  530.             });
  531.         }
  532.  
  533.         if (req.prerender.redirectURL && !(_this.options.followRedirect || process.env.FOLLOW_REDIRECT)) {
  534.             res.setHeader('Location', req.prerender.redirectURL);
  535.         }
  536.  
  537.         res.setHeader('Content-Type', 'text/html;charset=UTF-8');
  538.  
  539.         if(req.headers['accept-encoding'] && req.headers['accept-encoding'].indexOf('gzip') >= 0) {
  540.             res.setHeader('Content-Encoding', 'gzip');
  541.             zlib.gzip(req.prerender.documentHTML, function(err, result) {
  542.                 req.prerender.documentHTML = result;
  543.                 _this._sendResponse.apply(_this, [req, res, options]);
  544.             });
  545.         } else {
  546.             res.removeHeader('Content-Encoding');
  547.             _this._sendResponse.apply(_this, [req, res, options]);
  548.         }
  549.     });
  550. };
  551.  
  552. server._sendResponse = function(req, res, options) {
  553.  
  554.     if (req.prerender.documentHTML) {
  555.         if(Buffer.isBuffer(req.prerender.documentHTML)) {
  556.             res.setHeader('Content-Length', req.prerender.documentHTML.length);
  557.         } else {
  558.             res.setHeader('Content-Length', Buffer.byteLength(req.prerender.documentHTML, 'utf8'));
  559.         }
  560.     }
  561.  
  562.     if (!req.prerender.documentHTML) {
  563.         res.removeHeader('Content-Length');
  564.     }
  565.  
  566.     //if the original server had a chunked encoding, we should remove it since we aren't sending a chunked response
  567.     res.removeHeader('Transfer-Encoding');
  568.     //if the original server wanted to keep the connection alive, let's close it
  569.     res.removeHeader('Connection');
  570.     //getting 502s for sites that return these headers
  571.     res.removeHeader('X-Content-Security-Policy');
  572.     res.removeHeader('Content-Security-Policy');
  573.  
  574.     res.writeHead(req.prerender.statusCode || 504);
  575.  
  576.     if (req.prerender.documentHTML) res.write(req.prerender.documentHTML);
  577.  
  578.     res.end();
  579.  
  580.     if(req.prerender.phantomId && this.phantom && this.phantom.id === req.prerender.phantomId) {
  581.         this.phantom.requestsInFlight--;
  582.     }
  583.  
  584.     var ms = new Date().getTime() - req.prerender.start.getTime();
  585.     util.log('got', req.prerender.statusCode, 'in', ms + 'ms', 'for', req.prerender.url);
  586.  
  587.     if(this.shouldKillPhantomJS(req) || (options && options.abort)) {
  588.         req.prerender.isPageClosed = true;
  589.         server._killPhantomJS();
  590.     }
  591. };
  592.  
  593. server.shouldKillPhantomJS = function(req) {
  594.     if(!this.phantom || this.phantom.id !== req.prerender.phantomId) {
  595.         return false;
  596.     }
  597.  
  598.     ++this.phantom.iteration;
  599.  
  600.     if(this.phantom.iteration >= (this.options.iterations || NUM_ITERATIONS)) {
  601.         return true;
  602.     }
  603.  
  604.     if(this.phantom.iteration >= (this.options.softIterations || NUM_SOFT_ITERATIONS) && this.phantom.requestsInFlight <= 0) {
  605.         return true;
  606.     }
  607.  
  608.     return false;
  609. }
  610.  
  611. server._killPhantomJS = function() {
  612.     var _this = this;
  613.  
  614.     function restartPhantom() {
  615.         util.log("phantomjs terminated");
  616.         process.nextTick(_.bind(_this.createPhantom, _this));
  617.     }
  618.  
  619.     this._disposeAll(restartPhantom);
  620. };
  621.  
  622. //Check and see if PhantomJS didn't get disposed so we can forcefully kill it
  623. server._disposeAll = function(callback) {
  624.  
  625.     var _this = this
  626.       , disposed = false
  627.       , phantomPid = this.phantom && this.phantom.childProcess && this.phantom.childProcess.pid;
  628.  
  629.     this.phantom = null;
  630.  
  631.     setTimeout(function() {
  632.  
  633.         if(disposed) {
  634.             return;
  635.         }
  636.  
  637.         if(!disposed) {
  638.             console.log('unable to dispose PhantomJS. Forcing kill...');
  639.  
  640.             try {
  641.                 if(phantomPid) {
  642.                     process.kill(phantomPid, 'SIGKILL');
  643.                 }
  644.  
  645.                 callback();
  646.             } catch(e) {
  647.                 console.log('error force killing phantomjs pid', e);
  648.                 process.kill(_this.options.worker.process.pid, 'SIGKILL');
  649.             }
  650.         }
  651.     }, 10000);
  652.  
  653.  
  654.     phridge.disposeAll().then(function() {
  655.         disposed = true;
  656.         callback();
  657.     }).catch(function(err) {
  658.         util.log('error disposing all phantomjs instances:', err);
  659.     });
  660. };
  661.  
  662. server.exit = function() {
  663.     var _this = this;
  664.  
  665.     function terminatePhantom() {
  666.         util.log("phantomjs terminated");
  667.         process.exit(0);
  668.     }
  669.  
  670.     this._disposeAll(terminatePhantom);
  671. };
  672.  
downloadserver.js Source code - Download prerender Source code
Related Source Codes/Software:
libsodium - A modern and easy-to-use crypto library. 2017-01-09
Side-Menu.Android - Side menu with some categories to choose. ... 2017-01-09
docker-gitlab - Dockerized GitLab http://www.da... 2017-01-09
sinon - Test spies, stubs and mocks for JavaScript. ... 2017-01-09
in-view - Get notified when a DOM element enters or exits th... 2017-01-09
falcon - Falcon is a low-level, high-performance Python fra... 2017-01-09
ImageOptim - GUI image optimizer for Mac htt... 2017-01-09
node-style-guide - A guide for styling your node.js / JavaScript code... 2017-01-09
FDFullscreenPopGesture - A UINavigationController's category to enable full... 2017-01-10
Gource - oftware version control visualization ... 2017-01-10
CLNDR - 2017-02-19
OptiKey - OptiKey - Full computer control and speech with yo... 2017-02-19
MRProgress - Collection of iOS drop-in components to visualize ... 2017-02-19
BGARefreshLayout-Android - On a variety of drop-down refresh effect, loading ... 2017-02-19
angular-http-auth - 2017-02-19
pydata-book - Materials and IPython notebooks for "Python for Da... 2017-02-19
xenia - Xbox 360 Emulator Research Project ... 2017-02-18
flux-comparison - 2017-02-18
luvit - Lua + libUV + jIT = pure awesomesauce ... 2017-02-18
orleans - Orleans - Distributed Virtual Actor Model ... 2017-02-18

 Back to top