在现有套接字连接中使用Python“requests”

20 投票
2 回答
14291 浏览
提问于 2025-04-17 14:32

Python的“requests”库现在非常流行,因为它提供了一个很漂亮的界面,让我们可以方便地发送HTTP请求。不过在这个库的背后,其实有很多复杂的东西,比如会话、HTTP适配器,还有urllib3的底层机制。

如果我已经有一个打开的socket,想用“requests”库通过这个socket发送HTTP响应并接收回复,那么我应该在哪里进行干预呢?

如果不进行某种干预(或者说自定义),这个库会试图为我创建一个新的TCP/IP socket。但在我的应用中,我的代码是在连接已经建立后才被调用的,所以我需要说服Requests库使用那个已经存在的socket,这样我才能利用Requests的各种功能。

关于Requests库:

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

https://github.com/kennethreitz/requests

2 个回答

0

直接使用 urllib3 这个库,它里面有一个连接池,具体在 urllib3.connectionpool 模块

你可以通过修改 poolmanager 模块 来替换或调整这个连接池。

12

以下代码需要从git获取请求(特别是 requests.packages.urllib3.poolmanager.PoolManager._new_pool()

我使用 ncat -v -l 127.0.0.1 8000 进行了测试

问题在于,连接不是由urllib3打开的,而是由标准库中的httplib打开的。

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')

编辑:

或者直接修改连接池的设置:

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')

撰写回答