简短版本:是否有任何简单的 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.client
和 http.client
的 HTTPConnection
类的编码和网络操作都混杂在一起。
有没有一种简单的方法可以说“给我一堆字节发送到 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/