2015-01-30 14 views
8

I asked a question su come eseguire il throttle di un caricamento python, che mi ha inviato a this answer, dove sono stato informato di una piccola libreria di supporto chiamata socket-throttle. Questo è tutto bene e dandy per il normale HTTP e probabilmente anche per la maggior parte degli usi semplici del socket. Comunque, sto cercando di strozzare una connessione SSL, e cercando di coniugare socket-throttle con la libreria magazzino SSL (usato implicitamente da requests) causa un'eccezione in profondità nelle viscere della biblioteca:Limitazione della larghezza di banda di una connessione SSL

File "***.py", line 590, in request 
    r = self.session.get(url, headers=extra_headers) 
    File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 394, in get 
    return self.request('GET', url, **kwargs) 
    File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 382, in request 
    resp = self.send(prep, **send_kwargs) 
    File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 485, in send 
    r = adapter.send(request, **kwargs) 
    File "/usr/local/lib/python2.7/dist-packages/requests/adapters.py", line 324, in send 
    timeout=timeout 
    File "/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/connectionpool.py", line 478, in urlopen 
    body=body, headers=headers) 
    File "/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/connectionpool.py", line 285, in _make_request 
    conn.request(method, url, **httplib_request_kw) 
    File "/usr/lib/python2.7/httplib.py", line 973, in request 
    self._send_request(method, url, body, headers) 
    File "/usr/lib/python2.7/httplib.py", line 1007, in _send_request 
    self.endheaders(body) 
    File "/usr/lib/python2.7/httplib.py", line 969, in endheaders 
    self._send_output(message_body) 
    File "/usr/lib/python2.7/httplib.py", line 829, in _send_output 
    self.send(msg) 
    File "/usr/lib/python2.7/httplib.py", line 791, in send 
    self.connect() 
    File "/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/connection.py", line 95, in connect 
    ssl_version=resolved_ssl_version) 
    File "/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/util.py", line 643, in ssl_wrap_socket 
    ssl_version=ssl_version) 
    File "/usr/lib/python2.7/ssl.py", line 487, in wrap_socket 
    ciphers=ciphers) 
    File "/usr/lib/python2.7/ssl.py", line 211, in __init__ 
    socket.__init__(self, _sock=sock._sock) 
    File "***/socket_throttle.py", line 54, in __getattr__ 
    return getattr(self._wrappedsock, attr) 
AttributeError: '_socket.socket' object has no attribute '_sock' 

Beh, questo è un Downer. Come potete vedere, il pacchetto ssl sta tentando di utilizzare uno dei campi privati ​​del socket, _sock anziché lo stesso socket. (Non è il punto di campi privati ​​che non dovresti accedervi dall'esterno Grr?). Se cerco di iniettare me stesso in quel campo sul mio ThrottledSocket oggetto, mi imbatto in questo problema:

File "/home/alex/dev/jottalib/src/jottalib/JFS.py", line 590, in request 
    r = self.session.get(url, headers=extra_headers) 
    File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 394, in get 
    return self.request('GET', url, **kwargs) 
    File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 382, in request 
    resp = self.send(prep, **send_kwargs) 
    File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 485, in send 
    r = adapter.send(request, **kwargs) 
    File "/usr/local/lib/python2.7/dist-packages/requests/adapters.py", line 324, in send 
    timeout=timeout 
    File "/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/connectionpool.py", line 478, in urlopen 
    body=body, headers=headers) 
    File "/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/connectionpool.py", line 285, in _make_request 
    conn.request(method, url, **httplib_request_kw) 
    File "/usr/lib/python2.7/httplib.py", line 973, in request 
    self._send_request(method, url, body, headers) 
    File "/usr/lib/python2.7/httplib.py", line 1007, in _send_request 
    self.endheaders(body) 
    File "/usr/lib/python2.7/httplib.py", line 969, in endheaders 
    self._send_output(message_body) 
    File "/usr/lib/python2.7/httplib.py", line 829, in _send_output 
    self.send(msg) 
    File "/usr/lib/python2.7/httplib.py", line 791, in send 
    self.connect() 
    File "/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/connection.py", line 95, in connect 
    ssl_version=resolved_ssl_version) 
    File "/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/util.py", line 643, in ssl_wrap_socket 
    ssl_version=ssl_version) 
    File "/usr/lib/python2.7/ssl.py", line 487, in wrap_socket 
    ciphers=ciphers) 
    File "/usr/lib/python2.7/ssl.py", line 241, in __init__ 
    ciphers) 
TypeError: must be _socket.socket, not ThrottledSocket 

E adesso? C'è qualcos'altro in questo dove potrei limitare la comunicazione python? O c'è un modo più pulito per farlo rispetto al dover sovrascrivere l'implementazione del socket? Il che risulta comunque discutibile, dal momento che il pacchetto ssl cerca semplicemente di aggirarlo del tutto.

+0

Il motivo 'ssl' fa il tifo intorno a' campi socket' privati ​​è, la libreria C sottostante per TLS, 'openssl', vuole molto per parlare direttamente al OS- descrittore di socket di livello. Quello che potrebbe invece funzionare è cambiare 'socket-throttle' in modo che monkeypatches' ssl.wrap_socket' * invece di * 'socket.socket' - è necessario il wrapper del throttle * al di fuori * del wrapper TLS. Non ho intenzione di postare questa risposta come risposta, perché non so se funzionerà, e anche se lo fosse probabilmente sarebbe un sacco di complicazioni. In bocca al lupo? – zwol

+0

La libreria ssl non si muove nei meanness privati ​​di un oggetto 'socket.socket'. 'ssl.wrap_socket' restituisce un nuovo oggetto che memorizza l'istanza originale di' socket.socket' su di esso come '_sock'. Ha tutto il diritto di utilizzare il proprio attributo privato. La prossima volta, leggi la fonte prima di fare affermazioni del genere su altre librerie. Inoltre, come punto d'ordine, l'eccezione proviene da 'ssl' /' socket-throttle', ma esplode attraverso le richieste. Le richieste non hanno la responsabilità per questo. –

risposta

1

Sembra che tu stia cercando di limitare le richieste HTTP. In questo caso, puoi provare con lo RequestsThrottler. Python requests è molto più bello di httplib.

+0

Attualmente sto usando la libreria delle richieste (che a sua volta usa httplib). Questo 'RequestsThrottler' sembra promettente, ma dovrò testarlo per essere sicuro prima di accettare la tua risposta. :) Non stavo riscontrando problemi con la limitazione delle connessioni HTTP, ma le connessioni HTTPS sono dove ho avuto problemi. – Alex

+0

O non capisco come funziona RequestsThrottler, o riguarda solo i download, non i caricamenti. Devo limitare i caricamenti, come dice la prima riga nella mia domanda. Qualche possibilità potresti fornirmi un campione funzionante, se hai già provato a farlo funzionare prima? – Alex

4

A seconda delle esigenze, è possibile e forse è necessario risolvere questo particolare problema a livello di sistema operativo anziché a livello di applicazione.

L'approccio a livello di sistema operativo presenta due vantaggi. Innanzitutto, non fa differenza su come vengono utilizzati i socket in questione (HTTP o HTTPS o IRC o alcuni pacchetti ping di morte - non importa). In secondo luogo, più si disaccoppiano i diversi componenti del sistema, più è facile apportare modifiche in seguito e risolvere i problemi.

Esistono strumenti (almeno per sistemi conformi a POSIX) per la limitazione della larghezza di banda delle interfacce di rete e/o dei processi. Si potrebbe desiderare di avere uno sguardo a questi, ad esempio:

  • trickle (per modellare il traffico dei processi)
  • wondershaper (per modellare il traffico di intere interfacce di rete, ho effettivamente utilizzato questo all'interno di un Ubuntu moderna , e funziona perfettamente bene)

Queste discussioni potrebbero essere rilevanti per voi:

+0

Speravo di fornire all'utente un'impostazione configurabile di caricamento (e possibilmente di download) nel programma stesso. La soluzione del trickle sembra che risolva il mio problema, ma rimuove anche il controllo della situazione dal mio programma a un'entità esterna ... tuttavia, sto davvero iniziando a perdere la speranza su questa situazione. O semplicemente Python * non può fare * quello che sto chiedendo, o è solo così complicato o esoterico che nessuno su Stack Overflow sa come. Onestamente ho pensato che questo sarebbe stato uno scenario relativamente comune, ma sto iniziando a pensare che mi sbagliavo in quella ipotesi. – Alex

+0

Prendere il controllo del traffico di rete sicuramente * è * possibile, ma è molto meno banale di quanto la maggior parte della gente pensi. Lo stack TCP/IP è incredibilmente complesso e ben progettato, incorporando molti algoritmi speciali. Il tuo sistema operativo fa un ottimo lavoro nel nascondere questa complessità a te. Puoi ottenere ciò che vuoi, ma non in un progetto breve. Forse questo ti rende un po 'a casa: se il trickle funziona per te, puoi spedirlo insieme alla tua applicazione e mettere tutto in un "wrapper", mentre il wrapper configura il trickle e la tua app. –

+0

A proposito, potresti voler sviare quelle risposte che ti hanno fornito alcune informazioni, anche se non era la soluzione perfetta che speravi di ottenere. ;) –