Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Python “requests” with existing socket connection

The Python “requests” library is currently all the rage, because of the beautiful interface that it provides for making HTTP requests — but beneath it there seems to be many layers of indirection — sessions, HTTP adapters, and finally the mechanics of urllib3.

Where in this stack of abstractions is the right place to intervene if I already hold an open socket, and want to use “requests” to send an HTTP response down that socket and receive a reply back?

Without some kind of intervention (or customization?), the stack will try to create a new TCP/IP socket for me, but in my particular application my code is not called until a connection has already been established on my behalf, so I will need to convince Requests to talk on that existing socket if I want to be able to use Requests' features.

The Requests library:

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

https://github.com/kennethreitz/requests

like image 632
Brandon Rhodes Avatar asked Feb 02 '13 18:02

Brandon Rhodes


People also ask

Does Python requests reuse connection?

Any requests that you make within a session will automatically reuse the appropriate connection!

What does listen () do in Python?

Listen() Function Of Socket Class In Python Calling listen() makes a socket ready for accepting connections. The listen() method should be called before calling the accept() method on the server socket. The listen() function accepts a queue size through the parameter backlog.

Which method of the socket module allows a server socket to accept requests from a client socket from another host?

The listen method enables a server to accept connections. The server can now listen for connections on a socket.


1 Answers

The following code needs requests from git (especially requests.packages.urllib3.poolmanager.PoolManager._new_pool())

I tested it using ncat -v -l 127.0.0.1 8000

The problem is the fact, that the connection isn't opened by urllib3 but by httplib from the standard library.

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:

Or direct monkeypatching of the 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')
like image 159
t-8ch Avatar answered Oct 16 '22 11:10

t-8ch