我已经阅读了对“如何计算多部分 HTTP 请求内容长度?”的问题的相互矛盾且有些模棱两可的答复。具体来说,我想知道:
- 计算“Content-length”标头的精确内容范围是多少?
- CRLF ("\r\n") 八位字节序列算作一个还是两个八位字节?
有人可以提供一个明确的例子来回答这些问题吗?
我已经阅读了对“如何计算多部分 HTTP 请求内容长度?”的问题的相互矛盾且有些模棱两可的答复。具体来说,我想知道:
有人可以提供一个明确的例子来回答这些问题吗?
您的计算Content-Length
方式不取决于有效负载的状态代码或媒体类型;它是线上的字节数。因此,编写您的多部分响应,计算字节数(并CRLF
计为两个),并将其用于Content-Length
.
下面的活生生的例子应该有望回答这些问题。
Google 的 OAuth 2.0 Playground 网页是针对 Google Drive 云执行多部分 HTTP 请求的绝佳方式。您无需了解有关 Google Drive 的任何内容即可执行此操作 - 我会为您完成所有工作。我们只对 HTTP 请求和响应感兴趣。但是,如果需要,使用 Playground 可以让您尝试多部分并回答其他问题。
我创建了一个名为“test-multipart.txt”的本地文本文件,保存在我的文件系统的某个位置。该文件大小为 34 字节,如下所示:
We're testing multipart uploading!
我们首先在浏览器中打开 Google 的 OAuth 2.0 Playground,使用 URL https://developers.google.com/oauthplayground/:
选择 Drive API v2 和“ https://www.googleapis.com/auth/drive ”,然后按“授权 API”:
点击“兑换代币授权码”:
在这里,我们提供所有相关的多部分请求信息:
{“title”:“test-multipart.txt”,“parents”:[{“id”:“0B09i2ZH5SsTHTjNtSS9QYUZqdTA”}],“properties”:[{“kind”:“drive#property”,“key”:“ cloudwrapper", "value": "true"}]}
Google 的 OAuth 2.0 Playground 奇迹般地插入了所有必需的标头,计算内容长度,生成边界序列,在需要的地方插入边界字符串,并向我们显示服务器的响应:
多部分 HTTP 请求成功并带有 200 状态码,因此请求和响应是我们可以依赖的良好请求和响应。Google 的 Playground 插入了我们执行分段 HTTP 上传所需的一切。您可以看到“Content-length”设置为 352。让我们看看标题后面的空行之后的每一行:
--===============0688100289==\r\n 内容类型:应用程序/json\r\n \r\n {“title”:“test-multipart.txt”,“parents”:[{“id”:“0B09i2ZH5SsTHTjNtSS9QYUZqdTA”}],“properties”:[{“kind”:“drive#property”,“key”:“ cloudwrapper", "value": "true"}]}\r\n --===============0688100289==\r\n 内容类型:文本/纯文本\r\n \r\n 我们正在测试分段上传!\r\n --===============0688100289==--
有九 (9) 行,我在前八 (8) 行的每一行末尾手动添加了“\r\n”(出于可读性原因)。以下是每行中的八位字节(字符)数:
八位字节的总和为 344,将每个 '\r\n' 视为单个八位字节序列,我们得到了令人垂涎的内容长度 344 + 8 = 352。
总结调查结果:
如果 HTTP 消息具有Content-Length
标头,则此标头指示 HTTP 标头之后的确切字节数。如果任何东西决定自由地算作\r\n
一个字节,那么一切都会崩溃:保持活动的 http 连接将停止工作,因为 HTTP 堆栈将无法看到下一条 HTTP 消息从哪里开始,并且会尝试解析随机数据,就好像它一样是一条 HTTP 消息。
\n\r 是两个字节。
Moshe Rubin 的回答是错误的。该实现在那里被窃听。
我发送了一个 curl 请求来上传文件,并使用 WireShark 专门收集我的网络发送的确切实际数据。每个人都应该同意的方法比某个地方的在线申请更有效。
--------------------------de798c65c334bc76\r\n
Content-Disposition: form-data; name="file"; filename="requireoptions.txt"\r\n
Content-Type: text/plain\r\n
\r\n
Pillow
pyusb
wxPython
ezdxf
opencv-python-headless
\r\n--------------------------de798c65c334bc76--\r\n
Curl,每个人都会同意可能正确地实现了这一点:
Content-Length: 250
> len("2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d646537393863363563333334626337360d0a436f6e74656e742d446973706f736974696f6e3a20666f726d2d646174613b206e616d653d2266696c65223b2066696c656e616d653d22726571756972656f7074696f6e732e747874220d0a436f6e74656e742d547970653a20746578742f706c61696e0d0a0d0a50696c6c6f770d0a70797573620d0a7778507974686f6e0d0a657a6478660d0a6f70656e63762d707974686f6e2d686561646c6573730d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d646537393863363563333334626337362d2d0d0a")
500
(2x250 = 500,从 WireShark 复制十六进制流。)
我在那里拿了实际的二进制文件。'2d' 是 --- 开始边界。
请注意,将错误的计数分配给将 0d0a 视为 1 而不是 2 个八位字节的服务器(这太疯狂了,它们是八位字节并且不能是复合的),主动拒绝了错误的请求。
此外,这回答了问题的第二部分。实际的内容长度就是这里的一切。从第一个边界到结尾处的最后一个边界,--\r\n
所有的八位字节都留在了线路中。