PhantomJS: ensuring that the response object remains alive in server.listen (...)

I am using server.listen(...) from PhantomJS. I understand that it is largely experimental and should not be used in production. I use it for a simple screenshot server that accepts generates screenshots for the URL; This is a toy project that I use to play with PhantomJS. I noticed a problem with long queries, in particular where the response object is not available. Here are the relevant snippets from my code:

 var service = server.listen(8080, function (request, response) { response.statusCode = 200; if (loglevel === level.VERBOSE) { log(request); } else { console.log("Incoming request with querystring:", request.url); } var params = parseQueryString(request.url); if (params[screenshotOptions.ACTION] === action.SCREENSHOT) { getScreenshot(params, function (screenshot) { response.headers["success"] = screenshot.success; //<-- here is where I get the error that response.headers is unavailable. Execution pretty much stops at that point for that particular request. response.headers["message"] = screenshot.message; if (screenshot.success) { response.write(screenshot.base64); } else { response.write("<html><body>There were errors!<br /><br />"); response.write(screenshot.message.replace(/\n/g, "<br />")); response.write("</body></html>"); } response.close(); }); } else { response.write("<html><body><h1>Welcome to the screenshot server!</h1></body></html>") response.close(); } }); 

getScreenshot is an asynchronous method that uses the WebPage.open(...) function to open a web page; this function is also asynchronous. So it seems that when the callback passed as an argument to getScreenshot is finally called, it seems that the response object has already been deleted. I basically get the following error from PhantomJS:

 Error: cannot access member `headers' of deleted QObject 

I believe this is due to the fact that the request expires, and therefore the connection is closed. The documentation mentions calling response.write("") at least once to make sure the connection remains open. I tried calling response.write("") at the beginning of server.listen(...) , and I even tried a pretty hacky solution where I used setInterval(...) to execute response.write("") every 500 milliseconds ( I even lowered it to 50). I also took care to clear the interval as soon as I finished. However, I still seem to get this problem.

Is this something I will have to face until they make the webserver module more reliable? Or is there a way around this?

+6
source share
2 answers

I was able to figure it out. It seems that when certain pages are loaded from WebPage.open (for example, http://fark.com and http://cnn.com ), several onLoadFinished events are onLoadFinished . This causes the callback in WebPage.open called multiple times. So what happens when control returns to the calling function, I have already closed the response, and therefore the response object is no longer valid. I fixed this by creating a flag before calling the WebPage.open function. Inside the callback, I check the status of the flag to see if I met the previous onLoadFinished event onLoadFinished . Once I'm with what I need to do in the WebPage.open , I update the flag to show that I have finished processing. Thus, false (at least in the context of my code) onLoadFinished events onLoadFinished no longer served.

+8
source

(Note that the following applies to PhantomJS 1.9.7, while the OP most likely applies to 1.6.1 or later.)

In the case of multiple onLoadFinished events onLoadFinished you can use page.open() instead of listening onLoadFinished yourself. Using page.open() will transfer the handler to a private handler to ensure that your callback is called only once.

From source :

 definePageSignalHandler(page, handlers, "_onPageOpenFinished", "loadFinished"); page.open = function (url, arg1, arg2, arg3, arg4) { var thisPage = this; if (arguments.length === 1) { this.openUrl(url, 'get', this.settings); return; } else if (arguments.length === 2 && typeof arg1 === 'function') { this._onPageOpenFinished = function() { thisPage._onPageOpenFinished = null; arg1.apply(thisPage, arguments); } this.openUrl(url, 'get', this.settings); return; } // ... Truncated for brevity 

This functionality is exactly the same as the other answer presented as part of the official API.

0
source

Source: https://habr.com/ru/post/921056/


All Articles