用Python编码HTTP请求

用Python编码HTTP请求,python,http,ssh,python-requests,Python,Http,Ssh,Python Requests,简短版本:是否有任何简单的API用于编码HTTP请求(并解码响应),而无需作为过程的一部分实际发送和接收编码的字节 长版本:我正在编写一些嵌入式软件,它使用paramiko打开与服务器的SSH会话。然后,我需要通过使用transport.open\u通道('direct-tcpip',)打开的SSH通道发出HTTP请求 请求具有is传输适配器,允许您替换自己的传输。但是BaseAdapter提供的send接口只接受PreparedRequest对象,该对象(a)没有以任何有用的方式提供远程地址;

简短版本:是否有任何简单的API用于编码HTTP请求(并解码响应),而无需作为过程的一部分实际发送和接收编码的字节

长版本:我正在编写一些嵌入式软件,它使用paramiko打开与服务器的SSH会话。然后,我需要通过使用
transport.open\u通道('direct-tcpip',)
打开的SSH通道发出HTTP请求

请求
具有is传输适配器,允许您替换自己的传输。但是
BaseAdapter
提供的
send
接口只接受
PreparedRequest
对象,该对象(a)没有以任何有用的方式提供远程地址;您需要解析URL以找出主机和端口,(b)不提供请求的编码版本,只提供头和编码体(如果有)的字典。它也无法帮助您解码响应
HTTPAdapter
将整个过程延迟,包括对请求进行编码、建立网络连接、发送字节、接收响应字节以及对响应进行解码,直到
urllib3

urllib3
同样遵从
http.client
http.client
HTTPConnection
类的编码和网络操作都混乱在一起


有没有一种简单的方法可以说,“给我一堆字节发送到HTTP服务器”和“这是HTTP服务器的一堆字节;将它们转换成有用的Python对象”

您可以编写自己的SOCKS4代理,在本地主机上运行,然后将HTTP请求指向它。例如,描述如何将SOCKS代理与urllib3一起使用

简单的握手,然后是原始HTTP/TCP通信。握手传递目标IP地址和端口。因此,您的代理可以进行握手以使客户端确信它是SOCKS服务器,然后代理可以直接将“真实”流量发送到SSH会话(并以相反方向代理响应)


这种方法最酷的一点是,它可以处理成吨的客户机——SOCKS已经流行很长时间了。

这是我能想到的最简单的实现:

from http.client import HTTPConnection
import requests
from requests.structures import CaseInsensitiveDict
from urllib.parse import urlparse
from argparse import ArgumentParser

class TunneledHTTPConnection(HTTPConnection):
    def __init__(self, transport, *args, **kwargs):
        self.ssh_transport = transport
        HTTPConnection.__init__(self, *args, **kwargs)

    def connect(self):
        self.sock = self.ssh_transport.open_channel(
            'direct-tcpip', (self.host, self.port), ('localhost', 0)
        )

class TunneledHTTPAdapter(requests.adapters.BaseAdapter):
    def __init__(self, transport):
        self.transport = transport

    def close(self):
        pass

    def send(self, request, **kwargs):
        scheme, location, path, params, query, anchor = urlparse(request.url)
        if ':' in location:
            host, port = location.split(':')
            port = int(port)
        else:
            host = location
            port = 80

        connection = TunneledHTTPConnection(self.transport, host, port)
        connection.request(method=request.method,
                           url=request.url,
                           body=request.body,
                           headers=request.headers)
        r = connection.getresponse()
        resp = requests.Response()
        resp.status_code = r.status
        resp.headers = CaseInsensitiveDict(r.headers)
        resp.raw = r
        resp.reason = r.reason
        resp.url = request.url
        resp.request = request
        resp.connection = connection
        resp.encoding = requests.utils.get_encoding_from_headers(response.headers)
        requests.cookies.extract_cookies_to_jar(resp.cookies, request, r)
        return resp

if __name__ == '__main__':
    import paramiko

    parser = ArgumentParser()
    parser.add_argument('-p', help='Port the SSH server listens on', default=22)
    parser.add_argument('host', help='SSH server to tunnel through')
    parser.add_argument('username', help='Username on SSH server')
    args = parser.parse_args()

    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.connect(args.host, args.p, username=args.username)

BaseAdapter有各种各样的选项。发送它无法处理的,它完全忽略了连接池等问题,但它完成了任务。

这是一个有趣的想法,但比我想要的要复杂得多。我已经想出了一个有效的方法(我将很快添加作为答案),但它似乎仍然太复杂了。TunneledHTTPAdapter在哪里使用?我在代码的任何地方都看不到它被引用。@Tom TBH我不知道。我似乎已经丢失了实现这个的代码。我需要找个时间把一台旧的笔记本电脑挖出来,如果我遇到它,我会发布更新。