2014-05-20 21 views
7

Vorrei sapere se è possibile abilitare la compressione gzip per gli eventi inviati dal server (SSE; Content-Type: text/event-stream).È possibile utilizzare la compressione gzip con Server-Sent Events (SSE)?

Sembra sia possibile, secondo questo libro: http://chimera.labs.oreilly.com/books/1230000000545/ch16.html

Ma non riesco a trovare alcun esempio di SSE con compressione gzip. Ho provato a inviare messaggi compressi con il campo dell'intestazione della risposta Codifica del contenuto impostato su "gzip" senza successo.

Per sperimentare su SSE, sto testando una piccola applicazione Web realizzata in Python con la struttura della bottiglia + gevent; Sto solo facendo funzionare server bottiglia WSGI:

@bottle.get('/data_stream') 
def stream_data(): 
    bottle.response.content_type = "text/event-stream" 
    bottle.response.add_header("Connection", "keep-alive") 
    bottle.response.add_header("Cache-Control", "no-cache") 
    bottle.response.add_header("Content-Encoding", "gzip") 
    while True: 
     # new_data is a gevent AsyncResult object, 
     # .get() just returns a data string when new 
     # data is available 
     data = new_data.get() 
     yield zlib.compress("data: %s\n\n" % data) 
     #yield "data: %s\n\n" % data 

Il codice senza compressione (ultima riga, commentato) e senza gzip Content-Encoding campo di intestazione funziona come un fascino.

EDIT: grazie alla risposta ed a quest'altra domanda: Python: Creating a streaming gzip'd file-like?, sono riuscito a risolvere il problema:

@bottle.route("/stream") 
def stream_data(): 
    compressed_stream = zlib.compressobj() 
    bottle.response.content_type = "text/event-stream" 
    bottle.response.add_header("Connection", "keep-alive") 
    bottle.response.add_header("Cache-Control", "no-cache, must-revalidate") 
    bottle.response.add_header("Content-Encoding", "deflate") 
    bottle.response.add_header("Transfer-Encoding", "chunked") 
    while True: 
     data = new_data.get() 
     yield compressed_stream.compress("data: %s\n\n" % data) 
     yield compressed_stream.flush(zlib.Z_SYNC_FLUSH) 

risposta

4

TL; DR: Se le richieste non vengono memorizzati nella cache, è probabile che desidera utilizzare zlib e dichiara Content-Encoding come "sgonfiato". Quel cambiamento da solo dovrebbe far funzionare il tuo codice.


Se si dichiara Content-Encoding essere gzip, è necessario utilizzare effettivamente gzip. Sono basati sullo stesso algoritmo di compressione, ma gzip ha alcune cornici extra. Questo funziona, ad esempio:

import gzip 
import StringIO 
from bottle import response, route 
@route('/') 
def get_data(): 
    response.add_header("Content-Encoding", "gzip") 
    s = StringIO.StringIO() 
    with gzip.GzipFile(fileobj=s, mode='w') as f: 
     f.write('Hello World') 
    return s.getvalue() 

Questo ha senso solo se si utilizza un file reale come cache, però.

+0

Grazie per la spiegazione. In effetti, cambiare la codifica del contenuto per sgonfiare aiuta un po ': il primo messaggio viene elaborato sul lato client. Ma solo il primo :(Hai qualche idea del perché? Grazie in anticipo – mguijarr

+0

Stai cercando di chiamare compress in modo indipendente per ogni blocco di dati? Non credo che funzionerà. Tutti i dati dovrebbero essere in un unico flusso compresso.Ciò significa che gzip con la sua interfaccia di streaming potrebbe effettivamente essere la strada da percorrere. Tuttavia, avrei bisogno di vedere più del tuo codice per offrire puntatori specifici. – otus

+0

Grazie un milione; finalmente funziona! Ho modificato la mia domanda per dire cosa dovevo cambiare. – mguijarr

2

C'è anche del middleware che puoi usare per non doverti preoccupare delle risposte gzipping per ognuno dei tuoi metodi. Eccone uno che ho usato di recente.

https://code.google.com/p/ibkon-wsgi-gzip-middleware/

Ecco come l'ho usato (sto usando bottle.py con il server gevent)

from gzip_middleware import Gzipper 
import bottle 
app = Gzipper(bottle.app()) 
run(app = app, host='0.0.0.0', port=8080, server='gevent') 

Per questa particolare libreria, è possibile impostare w/c tipi di risposte che vuole comprimere modificando il DEFAULT_COMPRESSABLES variable ad esempio

DEFAULT_COMPRESSABLES = set(['text/plain', 'text/html', 'text/css', 
'application/json', 'application/x-javascript', 'text/xml', 
'application/xml', 'application/xml+rss', 'text/javascript',  
'image/gif']) 

Tutte le risposte passano attraverso il middleware e ottenere compressi con gzip senza modificare il codice esistente. Per impostazione predefinita, comprime le risposte il cui tipo di contenuto appartiene a DEFAULT_COMPRESSABLES e la cui lunghezza del contenuto è maggiore di 200 caratteri.

+0

Grazie per la risposta – mguijarr