如何将winhttp api与“transfer-encoding:chunked”一起使用

时间:2017-10-23 15:51:43

标签: c++ winhttp chunked transfer-encoding

我正在尝试将一些数据发送到需要“Transfer-encoding:chunked”标头的Web服务。它可以正常的POST请求正常工作。 但是一旦我添加标题,我总是得到:

  

由于以下情况,无法发送内容:   收到客户的无效请求

这是发送请求的部分:

std::vector<std::wstring> m_headers;
m_headers.push_back(TEXT("Transfer-encoding: chunked"));
std::wstring m_verb(TEXT("POST"));
std::vector<unsigned __int8> m_payload;

HINTERNET m_connectionHandle = WinHttpConnect(m_http->getSessionHandle(), hostName.c_str(), m_urlParts.nPort, 0);
if (!m_connectionHandle) {
    std::cout << "InternetConnect failed: " << GetLastError() << std::endl;
    return;
}

__int32 requestFlags = WINHTTP_FLAG_SECURE | WINHTTP_FLAG_REFRESH;
HINTERNET m_requestHandle = WinHttpOpenRequest(m_connectionHandle, m_verb.c_str(), (path + extra).c_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, requestFlags);
if(!m_requestHandle) {
    std::cout << "HttpOpenRequest failed: " << GetLastError() << std::endl;
    return;
}

for(auto header : m_headers) {
    if(!WinHttpAddRequestHeaders(m_requestHandle, (header + TEXT("\r\n")).c_str(), -1, WINHTTP_ADDREQ_FLAG_ADD)) {
        std::cout << "WinHttpAddRequestHeaders failed: " << GetLastError() << std::endl;
        return;
    }
}

if(!WinHttpSendRequest(m_requestHandle, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, (DWORD_PTR)this)) {
    std::cout << "HttpSendRequest failed: " << GetLastError() << std::endl;
    return;
}

unsigned chunkSize = 1024;
unsigned chunkCount = m_payload.size() / chunkSize;
char chunksizeString[128];
for (unsigned i = 0; i <= chunkCount; i++) {
    unsigned actualChunkSize = std::min<unsigned>(chunkSize, m_payload.size() - i * chunkSize);
    sprintf_s(chunksizeString, "%d\r\n", actualChunkSize);
    if (!WinHttpWriteData(m_requestHandle, chunksizeString, strlen(chunksizeString), (LPDWORD)&m_totalBytesWritten)) {
        std::cout << "HttpWriteData failed: " << GetLastError() << std::endl;
        return;
    }
    if (!WinHttpWriteData(m_requestHandle, m_payload.data() + i * chunkSize, actualChunkSize, (LPDWORD)&m_totalBytesWritten)) {
        std::cout << "HttpWriteData failed: " << GetLastError() << std::endl;
        return;
    }
}

// terminate chunked transfer
if (!WinHttpWriteData(m_requestHandle, "0\r\n", strlen("0\r\n"), (LPDWORD)&m_totalBytesWritten)) {
    std::cout << "HttpWriteData failed: " << GetLastError() << std::endl;
    return;
}

if(!WinHttpReceiveResponse(m_requestHandle, NULL)) {
    std::wcout << "HttpReceiveResponse failed: " << GetLastError() << std::endl;
    return;
}

我不得不从不同的文件中复制它,所以我希望我得到所有重要的变量定义。现在我只是同步使用它,因为我认为它更容易调试。

因为它适用于普通的POST请求(我只使用带有效负载的WinHttpSendRequest)我猜它必须与我使用WinHttpSendRequest&amp;的方式有关。 WinHttpWriteData,我只是看不出它应该如何使用。

感谢任何帮助!

2 个答案:

答案 0 :(得分:1)

您需要手动将数据拆分为块:

int chunkSize = 512;       // can be anything
char chunkSizeString[128]; // large enough string buffer
for (int i=0; i<chunksCount; ++i) {
    int actualChunkSize = chunkSize; // may be less when passing the last chunk of data (if that's not a multiple of chunkSize)
    sprintf(chunkSizeString, "%d\r\n", actualChunkSize);
    WinHttpWriteData(m_requestHandle, chunkSizeString, strlen(chunkSizeString), (LPDWORD)&m_totalBytesWritten);
    WinHttpWriteData(m_requestHandle, m_payload.data() + i*chunkSize, actualChunkSize, (LPDWORD)&m_totalBytesWritten);
}
WinHttpWriteData(m_requestHandle, "0\r\n", strlen("0\r\n"), (LPDWORD)&m_totalBytesWritten); // the last zero chunk, end of transmission 

答案 1 :(得分:0)

感谢@ anton-malyshev提供的链接,我找到了解决方案,我只是将上面所有对WinHttpWriteData的调用替换为:

/* Chunk header */
char chunksizeString[64];
sprintf_s(chunksizeString, "%X\r\n", m_payload.size());
if (!WinHttpWriteData(m_requestHandle, chunksizeString, strlen(chunksizeString), (LPDWORD)&m_totalBytesWritten)) {
    std::wcout << "WinHttpWriteData chunk header failed: " << getHttpErrorMessage(GetLastError()) << std::endl;
    return;
}

/* Chunk body */
if (!WinHttpWriteData(m_requestHandle, m_payload.data(), m_payload.size(), (LPDWORD)&m_totalBytesWritten)) {
    std::wcout << "WinHttpWriteData chunk body failed: " << getHttpErrorMessage(GetLastError()) << std::endl;
    return;
}

/* Chunk footer */
if (!WinHttpWriteData(m_requestHandle, "\r\n", 2, (LPDWORD)&m_totalBytesWritten)) {
    std::wcout << "WinHttpWriteDatachunk footer failed: " << getHttpErrorMessage(GetLastError()) << std::endl;
    return;
}

/* Terminate chunk transfer */
if (!WinHttpWriteData(m_requestHandle, "0\r\n\r\n", 5, (LPDWORD)&m_totalBytesWritten)) {
    std::wcout << "WinHttpWriteData chunk termination failed: " << getHttpErrorMessage(GetLastError()) << std::endl;
    return;
}