HTTP header "Content-type: multipart/mixed" 导致“400 错误请求”
HTTP header "Content-type: multipart/mixed" causes "400 Bad request"
我正在尝试使用 php 的 fsockopen 将文件上传到远程服务器。如果我的请求看起来像:
$httpContent = [
"POST /index.php HTTP/1.1",
"Host: The IP address of my remote server",
"Connection: Close",
"User-Agent: My client"
];
服务器响应 200 OK
。但是,如果我添加
"Content-Type: multipart/mixed; boundary=".md5('myBoundary'),
"--".md5('myBoundary'),
"Content-Type: text/plain",
"value",
"--".md5('myBoundary')."--"
服务器returns“400 错误请求”。对我来说一切似乎都很好,但不知何故它不起作用。我做错了什么?
非常感谢任何帮助!
编辑(@Rei 建议 post 转储请求):
这是客户端终端的回显
POST /index.php HTTP/1.1
Host: The IP address of my remote server
Connection: Close
User-Agent: My client
Content-Type: multipart/mixed; boundary=be0850c82dd4983ddc49a51a797dce49
--be0850c82dd4983ddc49a51a797dce49
Content-Type: text/plain
value
--be0850c82dd4983ddc49a51a797dce49--
这是服务器得到的(用 tcpdump 捕获):
POST /index.php HTTP/1.1
Host: The IP address of my remote server
Connection: Close
User-Agent: My client
Content-Type: multipart/mixed; boundary=be0850c82dd4983ddc49a51a797dce49
--be0850c82dd4983ddc49a51a797dce49
Content-Type: text/plain
value
--be0850c82dd4983ddc49a51a797dce49--
我个人认为两者之间没有任何区别,也没有发现请求有任何问题。然而,服务器 returns“400 错误请求”。
*注意:"The IP address of my remote server" 是一个真实的 IP 地址,但出于安全考虑,我没有 post 使用它。
**注意:没有提到 PHP 脚本在 CLI 环境中是 运行 并且不用作 web-application back-end(请求不是由网络浏览器准备的)。
正如我所怀疑的,您发送的 HTTP 请求不符合 HTTP 规范。
参见 RFC7230 Section 3。
问题:缺少 CRLF。
在最后一个 HTTP header 之后应该有一个额外的 CRLF,在最后一个 MIME header 之后也应该有一个。
POST /index.php HTTP/1.1
Host: The IP address of my remote server
Connection: Close
User-Agent: My client
Content-Type: multipart/mixed; boundary=be0850c82dd4983ddc49a51a797dce49
--be0850c82dd4983ddc49a51a797dce49
Content-Type: text/plain
value
--be0850c82dd4983ddc49a51a797dce49--
这将解决“错误请求”问题。
虽然问题的原因不是 Content-Type: multipart/mixed
header,但您应该知道它已被弃用。
请改用 Content-Type: multipart/form-data
。
参见 related answer here and RFC7578。
正确请求示例
内容类型multipart/form-data
可用于发送值和文件。
区别仅在于属性 filename
.
尽可能少地修改你的HTTP请求,这里是发送两个字段(一个文件和一个值)的例子:
POST /index.php HTTP/1.1
Host: The IP address of my remote server
Connection: Close
User-Agent: My client
Content-Type: multipart/form-data; boundary=be0850c82dd4983ddc49a51a797dce49
Content-Length: 234
--be0850c82dd4983ddc49a51a797dce49
Content-Disposition: form-data; name="one"; filename="example.txt"
foo
--be0850c82dd4983ddc49a51a797dce49
Content-Disposition: form-data; name="two"
bar
--be0850c82dd4983ddc49a51a797dce49--
对于每个字段,您需要一个 Content-Disposition
header 才能命名该字段并可选择为其指定一个文件名。
Content-Type
是可选的,但如果值出现乱码,您可能想添加一个。
如果您只需要上传一个文件,请根据需要进行修改。
正在测试请求
这是我用来测试 HTTP 请求的一个简洁的小脚本:
<?php
header('Content-Type: application/json');
echo json_encode([
'method' => $_SERVER['REQUEST_METHOD'],
'uri' => $_SERVER['REQUEST_URI'],
'body' => file_get_contents('php://input'),
'GET' => $_GET,
'POST' => $_POST,
'FILES' => $_FILES,
'headers' => getallheaders(),
], JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);
在接收端使用该脚本,我将我的示例请求 未经修改 发送到我的本地主机服务器,我得到了这个结果:
HTTP/1.1 200 OK
Date: Tue, 31 Jul 2018 11:33:57 GMT
Server: Apache/2.4.9 (Win32)
Connection: close
Transfer-Encoding: chunked
Content-Type: application/json
254
{
"method": "POST",
"uri": "/index.php",
"body": "",
"GET": [],
"POST": {
"two": "bar"
},
"FILES": {
"one": {
"name": "example.txt",
"type": "",
"tmp_name": "C:\wamp\tmp\phpD6B5.tmp",
"error": 0,
"size": 3
}
},
"headers": {
"Host": "The IP address of my remote server",
"Connection": "Close",
"User-Agent": "My client",
"Content-Type": "multipart/form-data; boundary=be0850c82dd4983ddc49a51a797dce49",
"Content-Length": "234"
}
}
0
如您所见,字段“一”转到 $_FILES
,字段“二”转到 $_POST
。
首先,发送不加修改的示例请求。
继续尝试,直到得到正确的结果。
然后根据需要进行修改,确保请求仍然遵循 HTTP 规范的每一步。
我正在尝试使用 php 的 fsockopen 将文件上传到远程服务器。如果我的请求看起来像:
$httpContent = [
"POST /index.php HTTP/1.1",
"Host: The IP address of my remote server",
"Connection: Close",
"User-Agent: My client"
];
服务器响应 200 OK
。但是,如果我添加
"Content-Type: multipart/mixed; boundary=".md5('myBoundary'),
"--".md5('myBoundary'),
"Content-Type: text/plain",
"value",
"--".md5('myBoundary')."--"
服务器returns“400 错误请求”。对我来说一切似乎都很好,但不知何故它不起作用。我做错了什么?
非常感谢任何帮助!
编辑(@Rei 建议 post 转储请求):
这是客户端终端的回显
POST /index.php HTTP/1.1
Host: The IP address of my remote server
Connection: Close
User-Agent: My client
Content-Type: multipart/mixed; boundary=be0850c82dd4983ddc49a51a797dce49
--be0850c82dd4983ddc49a51a797dce49
Content-Type: text/plain
value
--be0850c82dd4983ddc49a51a797dce49--
这是服务器得到的(用 tcpdump 捕获):
POST /index.php HTTP/1.1
Host: The IP address of my remote server
Connection: Close
User-Agent: My client
Content-Type: multipart/mixed; boundary=be0850c82dd4983ddc49a51a797dce49
--be0850c82dd4983ddc49a51a797dce49
Content-Type: text/plain
value
--be0850c82dd4983ddc49a51a797dce49--
我个人认为两者之间没有任何区别,也没有发现请求有任何问题。然而,服务器 returns“400 错误请求”。
*注意:"The IP address of my remote server" 是一个真实的 IP 地址,但出于安全考虑,我没有 post 使用它。
**注意:没有提到 PHP 脚本在 CLI 环境中是 运行 并且不用作 web-application back-end(请求不是由网络浏览器准备的)。
正如我所怀疑的,您发送的 HTTP 请求不符合 HTTP 规范。 参见 RFC7230 Section 3。 问题:缺少 CRLF。
在最后一个 HTTP header 之后应该有一个额外的 CRLF,在最后一个 MIME header 之后也应该有一个。
POST /index.php HTTP/1.1
Host: The IP address of my remote server
Connection: Close
User-Agent: My client
Content-Type: multipart/mixed; boundary=be0850c82dd4983ddc49a51a797dce49
--be0850c82dd4983ddc49a51a797dce49
Content-Type: text/plain
value
--be0850c82dd4983ddc49a51a797dce49--
这将解决“错误请求”问题。
虽然问题的原因不是 Content-Type: multipart/mixed
header,但您应该知道它已被弃用。
请改用 Content-Type: multipart/form-data
。
参见 related answer here and RFC7578。
正确请求示例
内容类型multipart/form-data
可用于发送值和文件。
区别仅在于属性 filename
.
尽可能少地修改你的HTTP请求,这里是发送两个字段(一个文件和一个值)的例子:
POST /index.php HTTP/1.1
Host: The IP address of my remote server
Connection: Close
User-Agent: My client
Content-Type: multipart/form-data; boundary=be0850c82dd4983ddc49a51a797dce49
Content-Length: 234
--be0850c82dd4983ddc49a51a797dce49
Content-Disposition: form-data; name="one"; filename="example.txt"
foo
--be0850c82dd4983ddc49a51a797dce49
Content-Disposition: form-data; name="two"
bar
--be0850c82dd4983ddc49a51a797dce49--
对于每个字段,您需要一个 Content-Disposition
header 才能命名该字段并可选择为其指定一个文件名。
Content-Type
是可选的,但如果值出现乱码,您可能想添加一个。
如果您只需要上传一个文件,请根据需要进行修改。
正在测试请求
这是我用来测试 HTTP 请求的一个简洁的小脚本:
<?php
header('Content-Type: application/json');
echo json_encode([
'method' => $_SERVER['REQUEST_METHOD'],
'uri' => $_SERVER['REQUEST_URI'],
'body' => file_get_contents('php://input'),
'GET' => $_GET,
'POST' => $_POST,
'FILES' => $_FILES,
'headers' => getallheaders(),
], JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);
在接收端使用该脚本,我将我的示例请求 未经修改 发送到我的本地主机服务器,我得到了这个结果:
HTTP/1.1 200 OK
Date: Tue, 31 Jul 2018 11:33:57 GMT
Server: Apache/2.4.9 (Win32)
Connection: close
Transfer-Encoding: chunked
Content-Type: application/json
254
{
"method": "POST",
"uri": "/index.php",
"body": "",
"GET": [],
"POST": {
"two": "bar"
},
"FILES": {
"one": {
"name": "example.txt",
"type": "",
"tmp_name": "C:\wamp\tmp\phpD6B5.tmp",
"error": 0,
"size": 3
}
},
"headers": {
"Host": "The IP address of my remote server",
"Connection": "Close",
"User-Agent": "My client",
"Content-Type": "multipart/form-data; boundary=be0850c82dd4983ddc49a51a797dce49",
"Content-Length": "234"
}
}
0
如您所见,字段“一”转到 $_FILES
,字段“二”转到 $_POST
。
首先,发送不加修改的示例请求。 继续尝试,直到得到正确的结果。 然后根据需要进行修改,确保请求仍然遵循 HTTP 规范的每一步。