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

时间:2018-03-08 09:24:32

标签: c sockets http server http-headers

修改

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

但仍然遇到了问题:1

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

我之前的问题:Simple TCP server can't output to web browser

现在,我正在使用GETPOSTform-datax-www-form-urlencoded暂时查看数据传输到服务器的内容和方式。

我正在关注How to cURL POST from the Command Line发送POST次请求。

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

curl -d "data=example1&data2=example2" localhost:8080

服务器上的输出

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

这是预期的。

问题:1

现在出现了问题。当我尝试发送form-data时,不会产生输出。

当我发送form-data时:

curl -X POST -F "name=user" -F "password=test" localhost:8080

服务器上的输出

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

我正在接受边界,但我没有得到下一部分,就像我发送的数据一样。

问题:2

另一个奇怪的事情是我在发送x-www-form-urlencoded后尝试发送form-data

当我在x-www-form-urlencoded之后发送form-data时:

curl -d "data=example1&data2=example2" localhost:8080

服务器上的输出

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

为什么我在这里boundary

问题:3

同时发送GET作为:

curl localhost:8080

服务器上的输出

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

我收到Content-Typex-www-form-urlencoded数据以及boundary

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

Server.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;
}

1 个答案:

答案 0 :(得分:2)

问题1

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

Google中的简单搜索显示这导致了问题。

Expect: 100-Continue' Issues and Risks(我只是为了避免死链接而粘贴所有内容)

  

预期如何:100-Continue标题工作

     

当Expect:100-Continue不存在时,HTTP大致遵循以下流程(来自   客户的观点):

     

1. 请求启动与服务器的TCP连接。

     

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

     

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

     

4. 如果是HTTP   支持keep-alives,可选地重复请求   第2步。

     

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

     

1. 请求启动与服务器的TCP连接。

     

2。当   建立与服务器的连接,请求 - 包括   标题,Expect:100-Continue标题,没有请求正文 - 是   然后传送到服务器。

     

3。客户端随后等待回复   从服务器。如果状态代码是最终状态代码,请使用   客户端上面的先前步骤在没有Expect的情况下重试请求:   100-Continue标题。如果状态代码为100-Continue,则为请求   正文被发送到服务器。

     

4. 客户端将等待响应   来自服务器(由响应头和响应主体组成)。

     

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

     

为什么要使用Expect:100-Continue?

     

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

     

没有期望:100-继续:

     

没有期望:100-继续   功能,整个API请求,包括(可能很大)   在服务器可以连接之前必须传输推送体   确定语法或身份验证是否有效。但是,自从   我们的大多数API请求都有小的POST主体,这样做的好处是   将请求标头与请求主体分开可以忽略不计。

     

请求标题和正文单独发送时的问题

     

由于   Urban Airship处理的大量请求,涉及多个级别   我们的客户和负责的服务器之间存在复杂性   用于响应API请求。这不是一种异常现象   大多数服务器配置和策略,但确实引入了   使用。的任何API POST请求升级请求失败的风险   期待:100-Continue标题。这是由于请求的事实   标题和请求正文彼此分开发送,并且   必须在整个API中通过相同的连接   服务器基础设施。

     

使用代理数,负载平衡服务器和后端   请求处理已实现的服务器,请求处理   期望:100-Continue标题的成功概率增加   彼此分开,因此返回错误。

     

期待什么:

     

我们一直试图支持Expect:100-Continue。   但是,我们已确定使用Expect的客户:   由于,100-Continue正在接收次优服务质量   提升请求失败。

     

此外,我们的大多数API请求都有小的POST主体,   因此,将请求标题与...分开的好处   要求机构可以忽略不计。这些原因激励着我们   禁用对Expect的支持:100-Continue服务范围。

     

我们的建议:

     

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

因此,要阻止Expect: 100-continue POST中的form-data标题,请在您的`curl

中添加-H 'Expect:'
curl -X POST -F "name=user" -F "password=test" localhost:8080 -H 'Expect:'

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

问题2&amp; 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);
    }