Python 2.7: streaming server HTTP che supporta più connessioni su una porta

Sto cercando un pacchetto standard Python 2.7 che fornisce un server HTTP che esegue connessioni di streaming simultanee sullo stesso numero di porta.

Ehi, i tuoi moderatori sono là fuori, per favore smetti di contrassegnare la mia domanda come un duplicato di domande che vogliono servire in modi non streaming, come questo: server web multithread in python . No, non voglio un hack come ThreadingMixIn che raccolga semplicemente la risposta e la restituisca come un’unità.

In altre parole, sto cercando il modo standard per fare ciò che fa il seguente programma di esempio, ma senza scrivere personalmente l’intero server HTTP.

 import time, socket, threading sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM) host = socket.gethostname() port = 8000 sock.bind((host, port)) sock.listen(1) # my OWN HTTP server... Oh man, this is bad style. HTTP = "HTTP/1.1 200 OK\nContent-Type: text/html; charset=UTF-8\n\n" class Listener(threading.Thread): def __init__(self): threading.Thread.__init__(self) self.daemon = True # stop Python from biting ctrl-C self.start() def run(self): conn, addr = sock.accept() conn.send(HTTP) # serve up an infinite stream i = 0 while True: conn.send("%i " % i) time.sleep(0.1) i += 1 [Listener() for i in range(100)] time.sleep(9e9) 

Quindi prima ho provato:

 # run with this command: # gunicorn -k gevent myapp:app import time def app(environ, start_response): data = b"Hello, World!\n" start_response("200 OK", [ ("Content-Type", "text/plain"), ("Content-Length", str(len(data))) ]) for i in range(5): time.sleep(1) yield "Hello %i\n" % i # https://stackoverflow.com/questions/22739394/streaming-with-gunicorn 

ma sfortunatamente non -k gevent , anche con -k gevent .

Aggiornamento: sembra che Gunicorn stia cercando di fare keepalive, il che richiederebbe Chunked Transfer Coding con l’ last-chunk bit. Un rapido grep delle fonti rivela che non lo sta implementando. Quindi potrei aver bisogno di un server HTTP molto più elaborato, o di uno più semplice (come il mio primo esempio sopra, basato sul socket ) che non si preoccupa di keepalive (che comunque è piuttosto sciocco per grandi flussi).

Allora ho provato:

 import time import threading import BaseHTTPServer class Handler(BaseHTTPServer.BaseHTTPRequestHandler): def do_GET(self): if self.path != '/': self.send_error(404, "Object not found") return self.send_response(200) self.send_header('Content-type', 'text/html; charset=utf-8') self.end_headers() # serve up an infinite stream i = 0 while True: self.wfile.write("%i " % i) time.sleep(0.1) i += 1 class Listener(threading.Thread): def __init__(self, i): threading.Thread.__init__(self) self.i = i self.daemon = True self.start() def run(self): server_address = ('', 8000+self.i) # How to attach all of them to 8000? httpd = BaseHTTPServer.HTTPServer(server_address, Handler) httpd.serve_forever() [Listener(i) for i in range(100)] time.sleep(9e9) 

che è abbastanza buono, ma è un po ‘fastidioso che devo assegnare 100 numeri di porta. Ciò richiederà un fastidioso reindirizzamento lato client per portare il browser alla prossima porta disponibile (Bene, OK, posso nasconderlo con JavaScript, ma non è così elegante. Preferirei scrivere il mio server HTTP piuttosto che farlo).

Deve esserci un modo pulito per ottenere tutti i listener BaseHTTPServer su una porta, poiché è un modo standard di configurare un server web. O forse il pacchetto gunicorn o somesuch può essere fatto in streaming in modo affidabile?

Le impostazioni di BaseHTTPServer predefinite re- BaseHTTPServer un nuovo socket su ogni listener, che non funzionerà in Linux se tutti gli ascoltatori si trovano sulla stessa porta. Modificare tali impostazioni tra la chiamata BaseHTTPServer.HTTPServer() e la chiamata serve_forever() .

L’esempio seguente avvia 100 thread di gestione sulla stessa porta, con ciascun gestore avviato tramite BaseHTTPServer .

 import time, threading, socket, SocketServer, BaseHTTPServer class Handler(BaseHTTPServer.BaseHTTPRequestHandler): def do_GET(self): if self.path != '/': self.send_error(404, "Object not found") return self.send_response(200) self.send_header('Content-type', 'text/html; charset=utf-8') self.end_headers() # serve up an infinite stream i = 0 while True: self.wfile.write("%i " % i) time.sleep(0.1) i += 1 # Create ONE socket. addr = ('', 8000) sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(addr) sock.listen(5) # Launch 100 listener threads. class Thread(threading.Thread): def __init__(self, i): threading.Thread.__init__(self) self.i = i self.daemon = True self.start() def run(self): httpd = BaseHTTPServer.HTTPServer(addr, Handler, False) # Prevent the HTTP server from re-binding every handler. # https://stackoverflow.com/questions/46210672/ httpd.socket = sock httpd.server_bind = self.server_close = lambda self: None httpd.serve_forever() [Thread(i) for i in range(100)] time.sleep(9e9)