Curl 不会在 HTTP POST 请求中发送整个表单数据

ssh*_*ssh 3 c sockets http http-headers server

编辑:

问题:2问题:3通过以下@melpomene 注释解决,即通过使用读取的字节数来打印缓冲区。

但仍然遇到问题: 1


我写了一个TCP服务器-客户端程序。后来出于好奇,我想了解一下HTTP服务器。

我之前的问题:简单的 TCP 服务器无法输出到网络浏览器

现在我只是看看使用GETand POSTform-datax-www-form-urlencoded现在)将数据传输到服务器的内容和方式。

我正在按照如何从命令行 cURL POST发送POST请求。

当我发送x-www-form-urlencoded为:

curl -d "data=example1&data2=example2" localhost:8080
Run Code Online (Sandbox Code Playgroud)

服务器上的输出

POST / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.54.0
Accept: */*
Content-Length: 28
Content-Type: application/x-www-form-urlencoded

data=example1&data2=example2
Run Code Online (Sandbox Code Playgroud)

这正如预期的那样。


问题:1

现在问题来了。当我尝试发送时form-data,输出不是预期的。

当我发送form-data为:

curl -X POST -F "name=user" -F "password=test" localhost:8080
Run Code Online (Sandbox Code Playgroud)

服务器上的输出

POST / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.54.0
Accept: */*
Content-Length: 244
Expect: 100-continue
Content-Type: multipart/form-data; boundary=------------------------78b7f8917ad1992c
Run Code Online (Sandbox Code Playgroud)

我得到了边界,但没有得到下一部分,比如我发送的数据。

问题:2

x-www-form-urlencoded一件更奇怪的事情是当我在发送后尝试发送时form-data

当我发送x-www-form-urlencodedform-data

curl -d "data=example1&data2=example2" localhost:8080
Run Code Online (Sandbox Code Playgroud)

服务器上的输出

POST / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.54.0
Accept: */*
Content-Length: 28
Content-Type: application/x-www-form-urlencoded

data=example1&data2=example2------------78b7f8917ad1992c
Run Code Online (Sandbox Code Playgroud)

我为什么来boundary这里?

问题:3

并且同时发送GET为:

curl localhost:8080
Run Code Online (Sandbox Code Playgroud)

服务器上的输出

GET / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.54.0
Accept: */*

ontent-Length: 28
Content-Type: application/x-www-form-urlencoded

data=example1&data2=example2------------78b7f8917ad1992c
Run Code Online (Sandbox Code Playgroud)

我正在获取Content-Typex-www-form-urlencoded数据boundary

我究竟做错了什么?我的代码或我的理解有问题吗?

服务器.c

// Server side C program to demonstrate Socket programming
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>

#define PORT 8080
int main(int argc, char const *argv[])
{
    int server_fd, new_socket; long valread;
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    char buffer[1024] = {0};
    char *hello = "HTTP/1.1 200 OK\nContent-Type: text/plain\nContent-Length: 12\n\nHello world!";

    // Creating socket file descriptor
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
    {
        perror("In socket");
        exit(EXIT_FAILURE);
    }


    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons( PORT );


    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0)
    {
        perror("In bind");
        exit(EXIT_FAILURE);
    }
    if (listen(server_fd, 10) < 0)
    {
        perror("In listen");
        exit(EXIT_FAILURE);
    }
    while(1)
    {
        printf("\n+++++++ Waiting for new connection ++++++++\n\n");
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0)
        {
            perror("In accept");
            exit(EXIT_FAILURE);
        }
        valread = read( new_socket , buffer, 1024);
        printf("%s\n",buffer );
        write(new_socket , hello , strlen(hello));
        printf("------------------Hello message sent-------------------\n");
        close(new_socket);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Skr*_*ing 7

问题1

我检查了请求标头并找到了Expect: 100-continue标头。这是我第一次看到这个标题。

在 Google 中进行简单搜索表明这是导致问题的原因。

预期:100-Continue' 问题和风险(​​我只是粘贴所有内容以避免死链接)

Expect: 100-Continue 标题如何工作

当 Expect: 100-Continue 不存在时,HTTP 大致遵循以下流程(从客户端的角度来看):

1.请求发起到服务器的 TCP 连接。

2.建立与服务器的连接后,完整的请求(包括请求标头和请求正文)将传输到服务器。

3.客户端等待服务器的响应(由响应头和响应体组成)。

4.如果支持 HTTP keep-alive,则可以选择从步骤 2 开始重复请求。

当客户端使用 Expect: 100-Continue 功能时,会发生以下事件:

1.请求发起到服务器的 TCP 连接。

2.建立与服务器的连接后,请求(包括标头、Expect:100-Continue 标头,不包括请求正文)将传输到服务器。

3.然后客户端等待服务器的响应。如果状态代码是最终状态代码,则使用上述步骤,客户端会在不带 Expect: 100-Continue 标头的情况下重试请求。如果状态码为100-Continue,则请求正文发送到服务器。

4.然后客户端将等待服务器的响应(由响应标头和响应正文组成)。

5.如果支持 HTTP keep-alive,则可以选择从步骤 2 开始重复请求。

为什么使用 Expect: 100-Continue?

包含 Expect: 100-Continue 标头的 API POST 请求可以节省客户端和服务器之间的带宽,因为服务器甚至可以在传输请求正文之前拒绝 API 请求。例如,对于请求正文非常大的 API POST 请求(例如文件上传),服务器可以在发送推送正文之前检查无效身份验证并拒绝请求,从而显着节省带宽。

没有预期:100-继续:

如果没有 Expect: 100-Continue 功能,则必须先传输整个 API 请求,包括(可能很大的)推送正文,然后服务器才能确定语法或身份验证是否有效。但是,由于我们的大多数 API 请求都具有较小的 POST 正文,因此将请求标头与请求正文分开的好处可以忽略不计。

请求头和请求体分开发送时的问题

由于 Urban Airship 处理大量请求,我们的客户端和负责响应 API 请求的服务器之间存在许多复杂程度。对于大多数服务器配置和策略来说,这并不是异常现象,但它确实会给使用 Expect: 100-Continue 标头的任何 API POST 请求带来更高的请求失败风险。这是因为请求标头和请求正文是彼此分开发送的,并且必须通过整个 API 服务器基础设施中的同一连接进行传输。

随着所实现的代理、负载平衡服务器和后端请求处理服务器数量的增加,带有 Expect: 100-Continue 标头的请求彼此分离并因此返回错误的可能性会增加。

期待什么:

我们一直尝试支持 Expect: 100-Continue。但是,我们确定使用 Expect: 100-Continue 的客户由于请求失败率较高而收到的服务质量不是最优的。

此外,我们的大多数 API 请求都具有较小的 POST 正文,因此将请求标头与请求正文分开的好处可以忽略不计。这些原因促使我们在整个服务范围内禁用对 Expect: 100-Continue 的支持。

我们的建议:

我们建议不要使用 Expect: 100-Continue。如果您收到 HTTP 错误 417(期望失败),请在没有期望的情况下重试请求:100-Continue。

因此,为了防止Expect: 100-continue标头出现在 中POST form-data,请包含-H 'Expect:'在您的“curl”中

curl -X POST -F "name=user" -F "password=test" localhost:8080 -H 'Expect:'
Run Code Online (Sandbox Code Playgroud)

现在,正如您在评论中所说,您可以一次性接收全部数据(就像 Postman 一样)。


问题2和3

正如@melpomene 在评论中所说,读完后read()不放。\0这就是您看到以前请求的数据的原因。

因此,只需使用valread迭代字符串来打印或仅在 while 循环中声明变量,正如我在评论中所说。

代码

while(1)
    {
        printf("\n+++++++ Waiting for new connection ++++++++\n\n");
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0)
        {
            perror("In accept");
            exit(EXIT_FAILURE);
        }

        char buffer[30000] = {0};  // This way you get new variable everytime. So, there is no need to iterate over the string using valread value.
        valread = read( new_socket , buffer, 30000);
        printf("%s\n",buffer );
        write(new_socket , hello , strlen(hello));
        printf("------------------Hello message sent-------------------%lu\n", valread);
        close(new_socket);
    }
Run Code Online (Sandbox Code Playgroud)