HTTP标头“内容类型:多部分/混合”导致“ 400错误的请求”

时间:2018-07-28 22:33:38

标签: php http http-headers fsockopen

我正在尝试使用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')."--"

服务器返回“ 400 Bad Request”。一切对我来说似乎都很好,但不知何故。我在做什么错了?

非常感谢您的帮助!

编辑(@Rei建议发布请求转储):

这就是客户端的回声

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--

我个人看不到两者之间的任何区别,也看不到请求有任何问题。但是,服务器返回“ 400 Bad Request”。

*注意:“我的远程服务器的IP地址”是真实的IP地址,但是出于安全考虑,我不发布它。

**注意:没有提到PHP脚本在CLI环境中运行,并且不用作Web应用程序后端(请求不是由Web浏览器准备的。)

1 个答案:

答案 0 :(得分:3)

正如我怀疑的那样,您发送的HTTP请求未遵循HTTP规范。 参见RFC7230 Section 3。 问题是:缺少CRLF。

在最后一个HTTP标头之后也应该有一个额外的CRLF,在最后一个MIME标头之后也应该有一个CRLF。

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标头,但您应该知道它已被弃用。 请改用Content-Type: multipart/form-data。 参见related answer hereRFC7578

正确请求的示例

内容类型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标头才能命名该字段,并可以选择为其指定文件名。

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规范的每一步。