POST HTTP 消息未调用 libcurl 写入回调

libcurl write callback is not called for post http message

提问人:Denis Detochka 提问时间:5/8/2018 最后编辑:Denis Detochka 更新时间:5/11/2018 访问量:492

问:

介绍

我正在向服务器发送 POST 请求,该请求响应分块消息。 因此,我正在尝试对每个收到的分块 http 消息调用 writecallback。

法典

#include <iostream>
#include <string>
#include <curl/curl.h>

using namespace std;

size_t write_callback(char *d, size_t n, size_t l, void *userp)
{
    cerr << ""--- Called once" << endl;
    return n*l;
}

string xml_msg()
{
    return "<<some request data>>";
}

curl_slist* get_header(size_t content_length)
{
    auto list = curl_slist_append(nullptr, "<<protocol version>>");
    list = curl_slist_append(list, "Content-Type: text/xml");
    list = curl_slist_append(list, "Content-Length: " + content_length);
    return list;
}

void main()
{
    auto xml = xml_msg();

    curl_global_init(CURL_GLOBAL_ALL);

    auto curl = curl_easy_init();
    curl_easy_setopt(curl, CURLOPT_URL, "<<server url>>");
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, nullptr);
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0)");
    curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
    curl_easy_setopt(curl, CURLOPT_USERPWD, "<<user credentials>>");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, get_header(xml.size()));
    curl_easy_setopt(curl, CURLOPT_POST, 1);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, xml.data());
    curl_easy_setopt(curl, CURLOPT_HTTP_CONTENT_DECODING, 0L);

    curl_easy_perform(curl);

    curl_easy_cleanup(curl);
    curl_global_cleanup();
}

详细日志

* STATE: INIT => CONNECT handle 0x15c4de0; line 1422 (connection #-5000)
* Added connection 0. The cache now contains 1 members
* STATE: CONNECT => WAITRESOLVE handle 0x15c4de0; line 1458 (connection #0)
*   Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* STATE: WAITRESOLVE => WAITCONNECT handle 0x15c4de0; line 1539 (connection #0)
* Connected to <<host>> (xxx.xxx.xxx.xxx) port 80 (#0)
* STATE: WAITCONNECT => SENDPROTOCONNECT handle 0x15c4de0; line 1591 (connection #0)
* Marked for [keep alive]: HTTP default
* STATE: SENDPROTOCONNECT => PROTOCONNECT handle 0x15c4de0; line 1605 (connection #0)
* STATE: PROTOCONNECT => DO handle 0x15c4de0; line 1626 (connection #0)
* Server auth using Basic with user '<<credentials>>'
> POST <<URL>>
Host: <<host>>
Authorization: Basic <<base64 credentials>>
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0)
Accept: */*
Content-Type: text/xml
Content-Length: 204

* upload completely sent off: 204 out of 204 bytes
* STATE: DO => DO_DONE handle 0x15c4de0; line 1688 (connection #0)
* STATE: DO_DONE => WAITPERFORM handle 0x15c4de0; line 1813 (connection #0)
* STATE: WAITPERFORM => PERFORM handle 0x15c4de0; line 1823 (connection #0)
* HTTP 1.1 or later with persistent connection, pipelining supported
< HTTP/1.1 200 OK
< Date: Tue, 08 May 2018 12:29:49 GMT
* Server is not blacklisted
< Server: <<server>>
< Expires: Thu, 01 Jan 1970 00:00:00 GMT
< Content-Language: en-US
< Cache-Control: no-cache, no-store
< Pragma: no-cache
< Content-Type: application/xml;charset=UTF-8
< Set-Cookie: <<cookie>>
< Transfer-Encoding: chunked
<
--- Called once
* STATE: PERFORM => DONE handle 0x15c4de0; line 1992 (connection #0)
* multi_done
* Connection #0 to <<server>> left intact

问题

当服务器由于 FIN tcp 数据包超时而关闭连接时,而不是收到分块 http 响应的时刻,调用了 Writecallback。

此事件之间的间隔约为 30 秒。

问题

我做错了什么?

更新 1

服务器返回一个带有 PUSH 标志的 tcp 段和带有包含 XML 的分块传输编码的 http 消息。消息以 CLRF 结尾。同时 Win API 套接字不允许读取它,select() 返回 0,这意味着该套接字上没有任何可读/写的内容。

由于心跳超时(即服务器的内部实现)而延迟 30 秒后,服务器会发送具有分块传输编码的最终 http 消息,其中包含 0 和 CLRF。在该消息之后,select() 显示新的套接字状态,libcurl 调用写入回调并返回分块消息内容。

这就是我在调试 libcurl 后看到的。我需要找出在收到 libcurl 返回的分块 http 消息后获取它的方法,而不是在获得最终的 http 消息之后。

http winapi libcurl 彗星 分块编码

评论

0赞 JonyVol 5/9/2018
由于调用write_callback的唯一时间是达到超时期限时,因此在我看来,Web 服务器根本无法处理请求,而不是问题出在客户端代码上。您是否确认服务器返回了任何响应?
0赞 Denis Detochka 5/10/2018
是的,服务器在请求后立即返回带有 PUSH 标志的 tcp 段,其中包含分块的 http 消息。这就是我使用 wireshark 看到的。当收到 FIN tcp 段时,正在调用 Bur 回调。顺便说一句,wireshark 也在 FIN tcp 段之前显示 HTTP 消息。
0赞 Denis Detochka 5/10/2018
我看到WinApi select返回0,即使收到带有分块http消息的tcp段。

答:

1赞 Denis Detochka 5/11/2018 #1

好的,我能够发现问题出在 Win Api 套接字上。在 linux 构建上,libcurl 在收到 chuncked 消息后立即调用 writecallback。我不确定如何使用 Win 版本解决该问题,但至少我找到了问题的根本原因。

评论

0赞 Daniel Stenberg 5/11/2018
这听起来不像是“答案”,这听起来像是调试实际情况的一些线索......
0赞 Denis Detochka 5/11/2018
我是这里的新人。如果可以,请将此答案作为评论。同时,我想尝试使用 USE_LWIPSOCK 构建 libcurl,并检查是否会使用 Win 套接字修复 isuue。或者这会在内部使用 Win 套接字?
0赞 Daniel Stenberg 5/11/2018
嗨,新人,我们不会在评论中提出额外的问题来回答其他问题......
0赞 Denis Detochka 5/11/2018
我试图提出额外的问题作为新问题,但它被标记为重复:)stackoverflow.com/questions/50279343/......