2012-07-22 5 views
6

I server.listen(...) von PhantomJS bin mit. Ich stelle fest, dass es weitgehend experimentell ist und dass es nicht in der Produktion verwendet werden sollte. Ich verwende es für einen einfachen Screenshot-Server, der Screenshots für eine URL generiert. Es ist ein Spielzeugprojekt, mit dem ich PhantomJS spiele. Ich habe ein Problem mit besonders langen Anfragen festgestellt, bei dem das Objekt response nicht verfügbar ist. Hier sind die entsprechenden Ausschnitte aus meinem Code:PhantomJS: Sicherstellen, dass das Antwortobjekt in server.listen am Leben bleibt (...)

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 ist eine asynchrone Methode, die die WebPage.open(...) Funktion verwendet, eine Webseite zu öffnen; Diese Funktion ist auch asynchron. Also, was passiert zu sein scheint, ist, dass, wenn der Rückruf, der in als Argument an getScreenshot geben wird schließlich aufgerufen wird, scheint es, dass die response Objekt wurde bereits gelöscht. Ich beende im Grunde mit dem folgenden Fehler von PhantomJS bis:

Error: cannot access member `headers' of deleted QObject 

Ich glaube, das liegt daran, dass die Anfragezeiten und so die Verbindung geschlossen wird. Die Dokumentation erwähnt mindestens einmal den Aufruf response.write(""), um sicherzustellen, dass die Verbindung offen bleibt. Ich versuchte response.write("") am Anfang von server.listen(...) zu benennen und ich probierte sogar eine ziemlich hacky Lösung, wo ich setInterval(...) verwendete, um alle 500 Millisekunden eine response.write("") durchzuführen (ich senkte es sogar hinunter zu so wenig wie 50). Ich habe auch dafür gesorgt, dass ich das Intervall löschte, sobald ich fertig war. Trotzdem scheint mir dieses Problem zu bestehen.

Ist das etwas, das ich nur behandeln gehen zu müssen, bis sie das Web-Server-Modul robuster zu machen? Oder gibt es einen Weg um es herum?

Antwort

8

Ich war in der Lage, dies herauszufinden. Es scheint, dass beim Laden von bestimmten Seiten mit WebPage.open (zB http://fark.com und http://cnn.com) mehr onLoadFinished Ereignissen ausgelöst werden. Dies führt dazu, dass der Rückruf in WebPage.open mehrfach aufgerufen wird. Also, was passiert ist, dass, wenn die Steuerung an die anrufende Funktion zurückkommt, habe ich schon die Antwort geschlossen und so die response Objekt ist nicht-mehr gültig. Ich habe dies behoben, indem ich ein Flag erstellt habe, bevor die WebPage.open-Funktion aufgerufen wird. Innerhalb des Callbacks überprüfe ich den Status des Flags, um zu sehen, ob ich bereits ein vorheriges onLoadFinished Ereignis gefunden habe. Sobald ich mit dem, was ich tun muss, innerhalb der WebPage.open Rückruf, aktualisiere ich die Flagge um zu zeigen, dass ich die Verarbeitung abgeschlossen habe. Auf diese Weise werden (zumindest im Rahmen meines Codes) onLoadFinished Ereignisse nicht mehr bedient.

+1

Ich habe auch den Server Keepalive auf falsch geändert, das schien auch zu helfen- service = server.listen (Port, {keepAlive: false}, Funktion (Anfrage, Antwort) {.... – chrismarx

+1

Sie sind super. Danke für wie du dein Problem gelöst hast.Gerade in das gleiche Problem gerannt. –

+0

Re Chris Kommentar oben - zumindest ab PhantomJS 1.9.7, ['keepAlive' ist standardmäßig auf' false' gesetzt (https://github.com/ariya/phantomjs/blob/1.9.7/src/webserver.cpp # L116-L118). – Whymarrh

0

(Beachten Sie, dass die folgenden zu PhantomJS 1.9.7 bezieht sich während der OP wahrscheinlich 1.6.1 oder älter bezog.)

Für den Fall, dass mehrere onLoadFinished Ereignisse ausgelöst werden, können Sie page.open() verwenden anstatt für onLoadFinished selbst zu hören. Wenn Sie page.open() verwenden, wird Ihr Handler in einen privaten Handler eingebunden, um sicherzustellen, dass Ihr Callback nur einmal aufgerufen wird.

Von 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 

Diese Funktionalität ist genau das gleiche wie die andere Antwort, als Teil der offiziellen API ausgesetzt.