admin管理员组

文章数量:1399805

I'm using server.listen(...) from PhantomJS. I realize that it is largely experimental and that it shouldn't be used in production. I'm using it for a simple screenshot-server that accepts generates screenshots for a URL; it's a toy project that I'm using to play around with PhantomJS. I've noticed an issue with long-running requests in particular, where the response object is unavailable. 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("Ining 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>Wele to the screenshot server!</h1></body></html>")   
        response.close();
    }
});

getScreenshot is an asynchronous method that uses the WebPage.open(...) function to open a webpage; this function is also asynchronous. So what seems to be happening is that when the callback that is passed in as an argument to getScreenshot is finally called, it appears that the response object has already been deleted. I basically end up with the following error from PhantomJS:

Error: cannot access member `headers' of deleted QObject

I believe this is because the request times out and so the connection is closed. The documentation mentions calling response.write("") at least once to ensure that the connection stays 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 perform a response.write("") every 500 milliseconds (I even lowered it down to as little as 50). I also made sure to clear the interval once I was done. However, I still seem to get this issue.

Is this something that I'm just going to have to deal with until they make the webserver module more robust? Or is there a way around it?

I'm using server.listen(...) from PhantomJS. I realize that it is largely experimental and that it shouldn't be used in production. I'm using it for a simple screenshot-server that accepts generates screenshots for a URL; it's a toy project that I'm using to play around with PhantomJS. I've noticed an issue with long-running requests in particular, where the response object is unavailable. 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("Ining 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>Wele to the screenshot server!</h1></body></html>")   
        response.close();
    }
});

getScreenshot is an asynchronous method that uses the WebPage.open(...) function to open a webpage; this function is also asynchronous. So what seems to be happening is that when the callback that is passed in as an argument to getScreenshot is finally called, it appears that the response object has already been deleted. I basically end up with the following error from PhantomJS:

Error: cannot access member `headers' of deleted QObject

I believe this is because the request times out and so the connection is closed. The documentation mentions calling response.write("") at least once to ensure that the connection stays 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 perform a response.write("") every 500 milliseconds (I even lowered it down to as little as 50). I also made sure to clear the interval once I was done. However, I still seem to get this issue.

Is this something that I'm just going to have to deal with until they make the webserver module more robust? Or is there a way around it?

Share Improve this question asked Jul 22, 2012 at 5:39 Vivin PaliathVivin Paliath 95.6k42 gold badges230 silver badges301 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 8

I was able to figure this out. It appears that while loading certain pages with WebPage.open (for example http://fark. and http://cnn.) multiple onLoadFinished events are fired. This results in the callback in WebPage.open being called multiple times. So what happens is that when control es back to the calling function, I've already closed the response and so the response object is no-longer valid. I fixed this by using creating a flag before the WebPage.open function is called. Inside the callback, I check the status of the flag to see if I've already encountered a previous onLoadFinished event. Once I am with whatever I have to do inside the WebPage.open callback, I update the flag to show that I've finished processing. This way spurious (at least in the context of my code) onLoadFinished events are no-longer serviced.

(Note that the following refers to PhantomJS 1.9.7 while the OP was likely referring to 1.6.1 or older.)

In the event that multiple onLoadFinished events are being fired, you can use page.open() instead of listening for onLoadFinished yourself. Using page.open() will wrap your handler in a private handler to ensure that your callback is only called once.

From the 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, exposed as part of the official API.

本文标签: javascriptPhantomJS Ensuring that the response object stays alive in serverlisten()Stack Overflow