尝试在接受 100-continue header 后发送 Python HTTPConnection 内容
Trying to send Python HTTPConnection content after accepting 100-continue header
我一直在尝试调试我继承的 Python 脚本。它正在尝试通过 HTTPLib POST 将 CSV 文件发送到网站。据我所知,问题是 HTTPLib 无法按照 处理接收 100-continue 响应。类似于 post,这个通过 Curl 的“Just Works”,但是出于各种原因,我们需要从 Python 脚本中将其 运行。
我已尝试使用 work-around,详见 post 的回答,但我找不到使用它来提交 CSV 的方法 在 接受 100-continue 响应之后。
一般流程需要是这样的:
- -> 建立连接
- -> 发送数据包括“expect: 100-continue”header,但不包括 JSON body yet
- <- 收到“100-继续”
- -> 使用相同的连接,发送请求的 JSON body
- <- 接收 200 OK 消息,在 JSON 响应中包含其他信息
这是当前状态的代码,删除了我的 10 多个其他尝试的变通方法的剩余评论:
#!/usr/bin/env python
import os
import ssl
import http.client
import binascii
import logging
import json
#classes taken from
class ContinueHTTPResponse(http.client.HTTPResponse):
def _read_status(self, *args, **kwargs):
version, status, reason = super()._read_status(*args, **kwargs)
if status == 100:
status = 199
return version, status, reason
def begin(self, *args, **kwargs):
super().begin(*args, **kwargs)
if self.status == 199:
self.status = 100
def _check_close(self, *args, **kwargs):
return super()._check_close(*args, **kwargs) and self.status != 100
class ContinueHTTPSConnection(http.client.HTTPSConnection):
response_class = ContinueHTTPResponse
def getresponse(self, *args, **kwargs):
logging.debug('running getresponse')
response = super().getresponse(*args, **kwargs)
if response.status == 100:
setattr(self, '_HTTPConnection__state', http.client._CS_REQ_SENT)
setattr(self, '_HTTPConnection__response', None)
return response
def uploadTradeIngest(ingestFile, certFile, certPass, host, port, url):
boundary = binascii.hexlify(os.urandom(16)).decode("ascii")
headers = {
"accept": "application/json",
"Content-Type": "multipart/form-data; boundary=%s" % boundary,
"Expect": "100-continue",
}
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.load_cert_chain(certfile=certFile, password=certPass)
connection = ContinueHTTPSConnection(
host, port=port, context=context)
with open(ingestFile, "r") as fh:
ingest = fh.read()
## Create form-data boundary
ingest = "--%s\r\nContent-Disposition: form-data; " % boundary + \
"name=\"file\"; filename=\"%s\"" % os.path.basename(ingestFile) + \
"\r\n\r\n%s\r\n--%s--\r\n" % (ingest, boundary)
print("pre-request")
connection.request(
method="POST", url=url, headers=headers)
print("post-request")
#resp = connection.getresponse()
resp = connection.getresponse()
if resp.status == http.client.CONTINUE:
resp.read()
print("pre-send ingest")
ingest = json.dumps(ingest)
ingest = ingest.encode()
print(ingest)
connection.send(ingest)
print("post-send ingest")
resp = connection.getresponse()
print("response1")
print(resp)
print("response2")
print(resp.read())
print("response3")
return resp.read()
但这只是 returns 400“错误请求”响应。问题(我认为)在于“摄取”变量的格式和类型。如果我不通过 json.dumps() 和 encode() 运行 它,那么 HTTPConnection.send() 方法会拒绝它:
ERROR: Got error: memoryview: a bytes-like object is required, not 'str'
我查看了使用 Requests 库的方法,但我无法让它使用我的本地证书包来接受站点的证书。我有一个带有加密密钥的完整链,我确实对其进行了解密,但仍然 运行 来自请求的常量 SSL_VERIFY 错误。如果您有解决我当前 Requests 问题的建议,我也很乐意沿着这条路走下去。
如何使用 HTTPLib 或 Requests(或任何其他库)来实现我需要实现的目标?
以防万一以后有人遇到这个问题,我最终还是有点笨手笨脚地解决了这个问题。我试过 HTTPLib、Requests 和 URLLib3 都知道不能处理 100-continue header,所以...我只是通过 subprocess.run() 函数在 Curl 周围写了一个 Python 包装器,像这样:
def sendReq(upFile):
sendFile=f"file=@{upFile}"
completed = subprocess.run([
curlPath,
'--cert',
args.cert,
'--key',
args.key,
targetHost,
'-H',
'accept: application/json',
'-H',
'Content-Type: multipart/form-data',
'-H',
'Expect: 100-continue',
'-F',
sendFile,
'-s'
], stdout=subprocess.PIPE, universal_newlines=True)
return completed.stdout
我遇到的唯一问题是,如果 Curl 是针对 NSS 库构建的,它会失败,我通过在包中包含一个 statically-built Curl 二进制文件解决了这个问题,其路径包含在代码中的 curlPath 变量。我从 this Github repo.
获得了这个二进制文件
我一直在尝试调试我继承的 Python 脚本。它正在尝试通过 HTTPLib POST 将 CSV 文件发送到网站。据我所知,问题是 HTTPLib 无法按照 处理接收 100-continue 响应。类似于 post,这个通过 Curl 的“Just Works”,但是出于各种原因,我们需要从 Python 脚本中将其 运行。
我已尝试使用 work-around,详见 post 的回答,但我找不到使用它来提交 CSV 的方法 在 接受 100-continue 响应之后。
一般流程需要是这样的:
- -> 建立连接
- -> 发送数据包括“expect: 100-continue”header,但不包括 JSON body yet
- <- 收到“100-继续”
- -> 使用相同的连接,发送请求的 JSON body
- <- 接收 200 OK 消息,在 JSON 响应中包含其他信息
这是当前状态的代码,删除了我的 10 多个其他尝试的变通方法的剩余评论:
#!/usr/bin/env python
import os
import ssl
import http.client
import binascii
import logging
import json
#classes taken from
class ContinueHTTPResponse(http.client.HTTPResponse):
def _read_status(self, *args, **kwargs):
version, status, reason = super()._read_status(*args, **kwargs)
if status == 100:
status = 199
return version, status, reason
def begin(self, *args, **kwargs):
super().begin(*args, **kwargs)
if self.status == 199:
self.status = 100
def _check_close(self, *args, **kwargs):
return super()._check_close(*args, **kwargs) and self.status != 100
class ContinueHTTPSConnection(http.client.HTTPSConnection):
response_class = ContinueHTTPResponse
def getresponse(self, *args, **kwargs):
logging.debug('running getresponse')
response = super().getresponse(*args, **kwargs)
if response.status == 100:
setattr(self, '_HTTPConnection__state', http.client._CS_REQ_SENT)
setattr(self, '_HTTPConnection__response', None)
return response
def uploadTradeIngest(ingestFile, certFile, certPass, host, port, url):
boundary = binascii.hexlify(os.urandom(16)).decode("ascii")
headers = {
"accept": "application/json",
"Content-Type": "multipart/form-data; boundary=%s" % boundary,
"Expect": "100-continue",
}
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.load_cert_chain(certfile=certFile, password=certPass)
connection = ContinueHTTPSConnection(
host, port=port, context=context)
with open(ingestFile, "r") as fh:
ingest = fh.read()
## Create form-data boundary
ingest = "--%s\r\nContent-Disposition: form-data; " % boundary + \
"name=\"file\"; filename=\"%s\"" % os.path.basename(ingestFile) + \
"\r\n\r\n%s\r\n--%s--\r\n" % (ingest, boundary)
print("pre-request")
connection.request(
method="POST", url=url, headers=headers)
print("post-request")
#resp = connection.getresponse()
resp = connection.getresponse()
if resp.status == http.client.CONTINUE:
resp.read()
print("pre-send ingest")
ingest = json.dumps(ingest)
ingest = ingest.encode()
print(ingest)
connection.send(ingest)
print("post-send ingest")
resp = connection.getresponse()
print("response1")
print(resp)
print("response2")
print(resp.read())
print("response3")
return resp.read()
但这只是 returns 400“错误请求”响应。问题(我认为)在于“摄取”变量的格式和类型。如果我不通过 json.dumps() 和 encode() 运行 它,那么 HTTPConnection.send() 方法会拒绝它:
ERROR: Got error: memoryview: a bytes-like object is required, not 'str'
我查看了使用 Requests 库的方法,但我无法让它使用我的本地证书包来接受站点的证书。我有一个带有加密密钥的完整链,我确实对其进行了解密,但仍然 运行 来自请求的常量 SSL_VERIFY 错误。如果您有解决我当前 Requests 问题的建议,我也很乐意沿着这条路走下去。
如何使用 HTTPLib 或 Requests(或任何其他库)来实现我需要实现的目标?
以防万一以后有人遇到这个问题,我最终还是有点笨手笨脚地解决了这个问题。我试过 HTTPLib、Requests 和 URLLib3 都知道不能处理 100-continue header,所以...我只是通过 subprocess.run() 函数在 Curl 周围写了一个 Python 包装器,像这样:
def sendReq(upFile):
sendFile=f"file=@{upFile}"
completed = subprocess.run([
curlPath,
'--cert',
args.cert,
'--key',
args.key,
targetHost,
'-H',
'accept: application/json',
'-H',
'Content-Type: multipart/form-data',
'-H',
'Expect: 100-continue',
'-F',
sendFile,
'-s'
], stdout=subprocess.PIPE, universal_newlines=True)
return completed.stdout
我遇到的唯一问题是,如果 Curl 是针对 NSS 库构建的,它会失败,我通过在包中包含一个 statically-built Curl 二进制文件解决了这个问题,其路径包含在代码中的 curlPath 变量。我从 this Github repo.
获得了这个二进制文件