【问题标题】:Trying to send Python HTTPConnection content after accepting 100-continue header在接受 100-continue 标头后尝试发送 Python HTTPConnection 内容
【发布时间】:2022-01-18 03:24:38
【问题描述】:

我一直在尝试调试我继承的 Python 脚本。它试图通过 HTTPLib 将 CSV 发布到网站。据我所知,问题在于 HTTPLib 不处理接收 100-continue 响应,如python http client stuck on 100 continue。与那篇文章类似,这个“Just Works”通过 Curl,但由于各种原因,我们需要从 Python 脚本运行它。

我已尝试使用该帖子的答案中详述的解决方法,但我找不到一种方法来使用它来提交 CSV在接受 100-continue 之后回应。

一般流程需要是这样的:

  • ->建立连接
  • -> 发送包含“expect: 100-continue”标头的数据,但尚不包含 JSON 正文
  • ->使用同一个连接,发送请求的JSON正文

以下是当前状态下的代码,删除了我的 10 多个其他已尝试解决方法的注释残余:

#!/usr/bin/env python

import os
import ssl
import http.client
import binascii
import logging
import json

#classes taken from https://stackoverflow.com/questions/38084993/python-http-client-stuck-on-100-continue
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()

但这只是返回 400“错误请求”响应。问题(我认为)在于“摄取”变量的格式和类型。如果我不通过 json.dumps() 和 encode() 运行它,那么 HTTPConnection.send() 方法会拒绝它:

ERROR: Got error: memoryview: a bytes-like object is required, not 'str'

我查看了使用 Requests 库,但我无法让它使用我的本地证书包来接受站点的证书。我有一个带有加密密钥的完整链,我确实对其进行了解密,但仍然遇到来自请求的持续 SSL_VERIFY 错误。如果您有解决我当前的 Requests 问题的建议,我也很乐意走这条路。

如何使用 HTTPLib 或 Requests(或任何其他库)来实现我需要实现的目标?

【问题讨论】:

    标签: python json post python-3.6 httplib


    【解决方案1】:

    万一以后有人遇到这个问题,我最终还是有点笨拙地解决了这个问题。我试过 HTTPLib、Requests 和 URLLib3 都知道不处理 100-continue 标头,所以...我只是通过 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 库构建的,它会失败,我通过在包中包含一个静态构建的 Curl 二进制文件来解决这个问题,该文件的路径包含在 curlPath 变量中代码。我从this Github repo 获得了这个二进制文件。

    【讨论】:

      猜你喜欢
      • 2013-04-27
      • 2013-03-30
      • 1970-01-01
      • 2010-10-25
      • 2013-08-04
      • 1970-01-01
      • 2011-03-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多