2009-04-22 10 views
9

Ho creato un semplice server http con Twisted, che invia l'intestazione Content-Type: multipart/x-mixed-replace. Lo sto usando per testare un client http che voglio configurare per accettare un flusso a lungo termine.Utilizzando le classi twisted.web di Twisted, come faccio a svuotare i miei buffer in uscita?

Il problema che è sorto è che la mia richiesta del client si blocca fino a quando il self.finish http.Request chiamate(), poi riceve tutti i documenti più parti in una sola volta.

C'è un modo per svuotare manualmente i buffer di uscita verso il client? Suppongo che questo sia il motivo per cui non sto ricevendo i singoli documenti multipart.

#!/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() 

risposta

9

L'utilizzo di time.sleep() impedisce che il doppino funzioni. Per farlo funzionare non è possibile utilizzare time.sleep(), è necessario restituire il controllo a twisted invece. Il modo più semplice per modificare il codice esistente per farlo è quello di utilizzare twisted.internet.defer.inlineCallbacks, che è la cosa migliore dopo il pane affettato:

#!/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() 

che funziona in Firefox, credo che risponde correttamente alla tua domanda.

+0

Ciò consente effettivamente l'esecuzione di qualsiasi altro codice (come selezionato dal reattore) al momento della resa? In tal caso, dovrai stare molto attento che i dati che stai utilizzando non vengano sovrascritti o alterati dall'altro codice. Vedi il commento all'URL sotto il quale non ho visto confutato da nessuna parte: http://twistedmatrix.com/pipermail/twisted-python/2007-February/014869.html – Mick

1

Il motivo sembra essere spiegato nel FAQ for twisted. Il server contorto in realtà non scrive nulla sulla connessione di sottolineatura finché il thread del reattore non è libero di funzionare, in questo caso alla fine del metodo. Tuttavia è possibile utilizzare reactor.doSelect(timeout) prima di ogni sospensione per far scrivere al reattore ciò che ha sulla connessione.

+5

Non si dovrebbe mai chiamare reactor.doSelect. Questo non è portatile tra i reattori e potrebbe facilmente rompere il reattore inserendolo di nuovo dove non è previsto il reinserimento. –

+2

Nonostante il commento/correzione attorno a doSeleziona sopra, per chiunque cerchi di capire cosa sta accadendo con il loro codice di trasporto il puntatore alle FAQ è azzeccato - in particolare "Twisted può solo inviare dati dopo aver rinunciato al controllo dell'esecuzione al reattore Ad esempio, se si dispone di un ciclo infinito di scrittura di dati su un trasporto, i dati non verranno mai effettivamente inviati poiché il controllo non lascerà mai il proprio codice e tornerà al reattore. " – Mick

Problemi correlati