2009-04-22 4 views
9

Ich habe einen einfachen HTTP-Server mit Twisted erstellt, der den Header Content-Type: multipart/x-mixed-replace sendet. Ich verwende das, um einen HTTP-Client zu testen, den ich einrichten möchte, um einen langfristigen Stream zu akzeptieren.Wie benutze ich Twisted's twisted.web Klassen, wie speichere ich meine ausgehenden Puffer?

Das Problem, das aufgetreten ist, ist, dass meine Client-Anforderung hängt, bis die http.Request ruft self.finish(), dann erhält es alle mehrteiligen Dokumente auf einmal.

Gibt es eine Möglichkeit, die Ausgabepuffer manuell auf den Client zu leeren? Ich gehe davon aus, dass ich deshalb die einzelnen mehrteiligen Dokumente nicht erhalte.

#!/usr/bin/env python 

import time 

from twisted.web import http 
from twisted.internet import protocol 

class StreamHandler(http.Request): 
    BOUNDARY = 'BOUNDARY' 

    def writeBoundary(self): 
     self.write("--%s\n" % (self.BOUNDARY)) 

    def writeStop(self): 
     self.write("--%s--\n" % (self.BOUNDARY)) 

    def process(self): 
     self.setHeader('Connection', 'Keep-Alive') 
     self.setHeader('Content-Type', "multipart/x-mixed-replace;boundary=%s" % (self.BOUNDARY)) 

     self.writeBoundary() 

     self.write("Content-Type: text/html\n") 
     s = "<html>foo</html>\n" 
     self.write("Content-Length: %s\n\n" % (len(s))) 
     self.write(s) 
     self.writeBoundary() 
     time.sleep(2) 

     self.write("Content-Type: text/html\n") 
     s = "<html>bar</html>\n" 
     self.write("Content-Length: %s\n\n" % (len(s))) 
     self.write(s) 
     self.writeBoundary() 
     time.sleep(2) 

     self.write("Content-Type: text/html\n") 
     s = "<html>baz</html>\n" 
     self.write("Content-Length: %s\n\n" % (len(s))) 
     self.write(s) 

     self.writeStop() 

     self.finish() 

class StreamProtocol(http.HTTPChannel): 
    requestFactory = StreamHandler 

class StreamFactory(http.HTTPFactory): 
    protocol = StreamProtocol 


if __name__ == '__main__': 
    from twisted.internet import reactor 
    reactor.listenTCP(8800, StreamFactory()) 
    reactor.run() 

Antwort

9

Mit time.sleep() verhindert, dass Twisted seine Arbeit macht. Damit es funktioniert, können Sie time.sleep() nicht verwenden, stattdessen müssen Sie die Steuerung auf twist zurücksetzen. Der einfachste Weg, um Ihren vorhandenen Code zu modifizieren, dass zu tun ist durch twisted.internet.defer.inlineCallbacks mit, was die nächste beste Sache seit geschnittenem Brot ist:

#!/usr/bin/env python 

import time 

from twisted.web import http 
from twisted.internet import protocol 
from twisted.internet import reactor 
from twisted.internet import defer 

def wait(seconds, result=None): 
    """Returns a deferred that will be fired later""" 
    d = defer.Deferred() 
    reactor.callLater(seconds, d.callback, result) 
    return d 

class StreamHandler(http.Request): 
    BOUNDARY = 'BOUNDARY' 

    def writeBoundary(self): 
     self.write("--%s\n" % (self.BOUNDARY)) 

    def writeStop(self): 
     self.write("--%s--\n" % (self.BOUNDARY)) 

    @defer.inlineCallbacks 
    def process(self): 
     self.setHeader('Connection', 'Keep-Alive') 
     self.setHeader('Content-Type', "multipart/x-mixed-replace;boundary=%s" % (self.BOUNDARY)) 

     self.writeBoundary() 

     self.write("Content-Type: text/html\n") 
     s = "<html>foo</html>\n" 
     self.write("Content-Length: %s\n\n" % (len(s))) 
     self.write(s) 
     self.writeBoundary() 


     yield wait(2) 

     self.write("Content-Type: text/html\n") 
     s = "<html>bar</html>\n" 
     self.write("Content-Length: %s\n\n" % (len(s))) 
     self.write(s) 
     self.writeBoundary() 

     yield wait(2) 

     self.write("Content-Type: text/html\n") 
     s = "<html>baz</html>\n" 
     self.write("Content-Length: %s\n\n" % (len(s))) 
     self.write(s) 

     self.writeStop() 

     self.finish() 


class StreamProtocol(http.HTTPChannel): 
    requestFactory = StreamHandler 

class StreamFactory(http.HTTPFactory): 
    protocol = StreamProtocol 


if __name__ == '__main__': 
    reactor.listenTCP(8800, StreamFactory()) 
    reactor.run() 

Die in Firefox funktioniert, ich denke, es Ihre Frage richtig beantwortet.

+0

Ermöglicht dies effektiv jeden anderen (wie vom Reaktor ausgewählt) Code auszuführen, wenn Sie nachgeben? Wenn dies der Fall ist, müssen Sie sehr vorsichtig sein, dass die Daten, die Sie verwenden, nicht vom anderen Code überschrieben oder geändert werden. Siehe den Kommentar unter der URL, die ich nirgendwo widersprüchlich gesehen habe: http://twistedmatrix.com/pipermail/twisted-python/2007-February/014869.html – Mick

1

Der Grund scheint in der FAQ for twisted erklärt werden. Der verdrehte Server schreibt nichts in die Unterstreichungsverbindung, bis der Reaktorfaden frei ist, in diesem Fall am Ende Ihrer Methode. Sie können jedoch reactor.doSelect(timeout) vor jedem Ihrer Schlaf verwenden, um den Reaktor schreiben zu lassen, was es zur Verbindung hat.

+5

Sie sollten niemals reactor.doSelect aufrufen. Dies ist in Reaktoren nicht übertragbar und könnte den Reaktor leicht durch erneutes Eintreten in den Reaktor zerbrechen, wo er nicht wieder betreten werden möchte. –

+2

Ungeachtet des Kommentars/der Korrektur um doSelect oben, für jeden, der herauszufinden versucht, was mit ihrem Transportcode passiert, ist der Zeiger auf die FAQ genau richtig - insbesondere "Twisted kann nur Daten senden, nachdem Sie die Kontrolle über die Ausführung des Reaktors aufgegeben haben Wenn Sie zum Beispiel eine Endlosschleife haben, die Daten in einen Transport schreibt, werden die Daten niemals wirklich gesendet, da die Steuerung Ihren Code niemals verlässt und zum Reaktor zurückkehrt. " – Mick