2013-02-02 1 views
12

La libreria Python "requests" è attualmente di gran moda, a causa della bella interfaccia che fornisce per le richieste HTTP - ma sotto di essa ci sono molti livelli di riferimento indiretto - sessioni , Adattatori HTTP e infine i meccanismi di urllib3.Utilizzo di Python "richieste" con connessione socket esistente

Dove in questa pila di astrazioni è il posto giusto per intervenire se ho già un socket aperto e voglio usare "richieste" per inviare una risposta HTTP su quel socket e ricevere una risposta?

Senza alcun tipo di intervento (o personalizzazione?), Lo stack proverà a creare un nuovo socket TCP/IP per me, ma nella mia particolare applicazione il mio codice non viene chiamato fino a quando non è stata stabilita una connessione per mio conto , quindi dovrò convincere le richieste a parlare su quel socket esistente se voglio essere in grado di utilizzare le funzionalità richieste.

La biblioteca Richieste:

http://pypi.python.org/pypi/requests

https://github.com/kennethreitz/requests

risposta

8

Il seguente codice ha bisogno di richieste da Git (soprattutto requests.packages.urllib3.poolmanager.PoolManager._new_pool())

ho provato usando ncat -v -l 127.0.0.1 8000

Il problema è il fatto, che la connessione non viene aperta dal urllib3 ma httplib dalla libreria standard .

import socket 
import requests 
from requests.adapters import HTTPAdapter 
from requests.packages.urllib3 import PoolManager, HTTPConnectionPool 

try: 
    from http.client import HTTPConnection 
except ImportError: 
    from httplib import HTTPConnection 


class MyAdapter(HTTPAdapter): 
    def init_poolmanager(self, connections, maxsize): 
     self.poolmanager = MyPoolManager(num_pools=connections, 
             maxsize=maxsize) 


class MyPoolManager(PoolManager): 
    def _new_pool(self, scheme, host, port): 
     # Important! 
     if scheme == 'http' and host == my_host and port == my_port: 
      return MyHTTPConnectionPool(host, port, **self.connection_pool_kw) 
     return super(PoolManager, self)._new_pool(self, scheme, host, port) 


class MyHTTPConnectionPool(HTTPConnectionPool): 
    def _new_conn(self): 
     self.num_connections += 1 
     return MyHTTPConnection(host=self.host, 
          port=self.port, 
          strict=self.strict) 


class MyHTTPConnection(HTTPConnection): 
    def connect(self): 
     """Connect to the host and port specified in __init__.""" 
     # Original 
     # self.sock = socket.create_connection((self.host, self.port), 
     #         self.timeout, self.source_address) 
     # Important! 
     self.sock = my_socket 
     if self._tunnel_host: 
      self._tunnel() 


if __name__ == '__main__': 
    import time 

    my_host = '127.0.0.1' 
    my_port = 8000 

    my_socket = socket.create_connection((my_host, my_port)) 
    time.sleep(4) 
    s = requests.Session() 
    s.mount('http://', MyAdapter()) 
    s.get('http://127.0.0.1:8000/foo') 

Edit:

O diretta monkeypatching del ConnectionPool:

class MyHTTPConnection(HTTPConnection): 
    def connect(self): 
     self.sock = my_socket 
     if self._tunnel_host: 
      self._tunnel() 

requests.packages.urllib3.connectionpool.HTTPConnection = MyHTTPConnection 

if __name__ == '__main__': 
    my_host = '127.0.0.1' 
    my_port = 8000 

    my_socket = socket.create_connection((my_host, my_port)) 
    requests.get('http://127.0.0.1:8000/foo') 
+0

Questo monkeypatching non funziona per me. Non sono sicuro che non funzionerà mai o se le richieste sono cambiate negli ultimi quattro anni, ma la mia classe non è stata utilizzata (almeno, i suoi metodi '__init__' e' connect' non vengono mai chiamati). Sto usando python 3.5 e richieste 2.18.1. – Tom