python - 在 Python 中编码 HTTP 请求

标签 python http ssh python-requests

简短版本:是否有任何简单的 API 用于编码 HTTP 请求(和解码响应),而无需在过程中实际传输和接收编码字节?

长版本:我正在编写一些使用 paramiko 打开与服务器的 SSH session 的嵌入式软件。然后,我需要通过使用 transport.open_channel('direct-tcpip', <remote address>, <source address>) 打开的 SSH channel 发出 HTTP 请求。 .

requests有一个传输适配器,它可以让你替换你自己的传输。但是 send BaseAdapter提供的界面只接受 PreparedRequest (a) 不以任何有用的方式提供远程地址的对象;您需要解析 URL 以找出主机和端口,并且 (b) 不提供请求的编码版本,仅提供 header 字典和编码正文(如果有)。它也没有帮助您解码响应。 HTTPAdapter推迟整个过程,包括编码请求、建立网络连接、发送字节、接收响应字节和解码响应,到 urllib3 .

urllib3同样遵从 http.clienthttp.clientHTTPConnection类的编码和网络操作都混杂在一起。

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

最佳答案

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

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')
    parser.add_argument('url', help='URL to perform HTTP GET on')
    args = parser.parse_args()

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

    transport = client.get_transport()

    s = requests.Session()
    s.mount(url, TunneledHTTPAdapter(transport))
    response = s.get(url)
    print(response.text)

它不处理 BaseAdapter.send 的各种选项,它完全忽略连接池等问题,但它完成了工作。

关于python - 在 Python 中编码 HTTP 请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46446904/

相关文章:

python - 使用 matplotlib 平滑 imshow 图

python - 仅在同一天内重新取样

javascript - 如何通过 Content-Security-Policy 允许 `javascript:void(0)` 在 HTML 元素属性中使用?

linux - 通过本地主机上的 SSH 代理在浏览器中访问应用程序。

python - python 中的多线程 I/O 减慢

python - Ansible:如何将列表转换为整数

python - 如何在不破坏浏览器后退按钮的情况下正确重定向到另一个站点?

c# - MVC3 HttpStatusCodeResult 在 OnActionExecuted 中不起作用

linux - 无密码 SSH 最佳实践

linux - 尝试终止进程时出错 -- "kill: pid: arguments must be process or job IDs"