HTTP_RECEIVE_REQUEST_FLAG_FLUSH_BODY не копирует тело http

Согласно Документация MSDN, HTTP_RECEIVE_REQUEST_FLAG_FLUSH_BODY флаг перешел в HttpReceiveHttpRequest функция вызывает копирование тел объекта в HTTP_REQUEST структура, которая передается в.

Я написал простой http-сервер (см. Ниже), который вызывает эту функцию с этим установленным флагом. Затем я отправил 4 МБ POST запрос от тестового клиента и подключил отладчик к серверу http, чтобы увидеть, сколько байтов было скопировано в HTTP_REQUEST структура после возврата функции; Я заметил, что это не копирует тело http, только скопирован заголовок.

Есть ли способ сделать HttpReceiveHttpRequest скопируйте все тело 4MB в pEntityChunks часть HTTP-запроса?

Примечание: я подтвердил HttpReceiveRequestEntityBody Функция копирует все тело, когда HTTP_RECEIVE_REQUEST_ENTITY_BODY_FLAG_FILL_BUFFER флаг установлен.

HTTP-сервер

// HttpServer.cpp : Defines the entry point for the console application.
//

#ifndef UNICODE
#define UNICODE
#endif

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>
#include <http.h>
#include <stdio.h>
#include <string>
#include <memory>

#pragma comment(lib, "httpapi.lib")

int RequestBufferLength = 4194304 + 4096;
int ResponseBufferLength = 4194304;
int SmallBufferLength = 100;

std::unique_ptr<char[]> ResponseBuffer = std::make_unique<char[]>(ResponseBufferLength);
std::unique_ptr<char[]> SmallBuffer = std::make_unique<char[]>(SmallBufferLength);
std::unique_ptr<char[]> RequestBuffer = std::make_unique<char[]>(RequestBufferLength);

DWORD SendHttpResponse(HANDLE hReqQueue, PHTTP_REQUEST pRequest)
{
HTTP_RESPONSE response;
HTTP_DATA_CHUNK dataChunk;
DWORD result;
DWORD bytesSent;

// Initialize the HTTP response structure.
RtlZeroMemory(&response, sizeof(response));
response.StatusCode = 200;
response.pReason = "OK";
response.ReasonLength = 2;

// Add known headers.
std::string contentType = "text/html";
response.Headers.KnownHeaders[HttpHeaderContentType].pRawValue = contentType.c_str();
response.Headers.KnownHeaders[HttpHeaderContentType].RawValueLength = (USHORT)contentType.length();

// Add the body
if (pRequest->Verb == HttpVerbGET)
{
// Send big response
dataChunk.DataChunkType = HttpDataChunkFromMemory;
dataChunk.FromMemory.pBuffer = ResponseBuffer.get();
dataChunk.FromMemory.BufferLength = ResponseBufferLength;
}
else
{
// Send small response
dataChunk.DataChunkType = HttpDataChunkFromMemory;
dataChunk.FromMemory.pBuffer = SmallBuffer.get();
dataChunk.FromMemory.BufferLength = SmallBufferLength;
}

response.EntityChunkCount = 1;
response.pEntityChunks = &dataChunk;

// Because the entity body is sent in one call, it is not
// required to specify the Content-Length.
result = HttpSendHttpResponse(
hReqQueue,           // ReqQueueHandle
pRequest->RequestId, // Request ID
0,                   // Flags
&response,           // HTTP response
NULL,                // HTTP Cache Policy
&bytesSent,          // bytes sent  (OPTIONAL)
NULL,                // pReserved2  (must be NULL)
0,                   // Reserved3   (must be 0)
NULL,                // LPOVERLAPPED(OPTIONAL)
NULL                 // pReserved4  (must be NULL)
);

if (result != NO_ERROR)
{
wprintf(L"HttpSendHttpResponse failed with %lu \n", result);
}

return result;
}

DWORD ReceiveRequests(HANDLE hReqQueue)
{
ULONG result;
HTTP_REQUEST_ID requestId;
DWORD bytesRead;
PHTTP_REQUEST pRequest;

pRequest = (PHTTP_REQUEST)RequestBuffer.get();

// Wait for a new request. This is indicated by a NULL
// request ID.
HTTP_SET_NULL_ID(&requestId);

for (;;)
{
result = HttpReceiveHttpRequest(
hReqQueue,
requestId,
HTTP_RECEIVE_REQUEST_FLAG_FLUSH_BODY,
pRequest,
RequestBufferLength,
&bytesRead,
NULL);

if (NO_ERROR == result)
{
switch (pRequest->Verb)
{
case HttpVerbGET:
result = SendHttpResponse(hReqQueue, pRequest);
break;
case HttpVerbPUT:
case HttpVerbPOST:
result = HttpReceiveRequestEntityBody(
hReqQueue,
pRequest->RequestId,
HTTP_RECEIVE_REQUEST_ENTITY_BODY_FLAG_FILL_BUFFER,
RequestBuffer.get() + bytesRead,
RequestBufferLength - bytesRead,
&bytesRead,
NULL);

if (NO_ERROR == result)
{
result = SendHttpResponse(hReqQueue, pRequest);
}
break;
default:
wprintf(L"Got a unknown request for %ws \n", pRequest->CookedUrl.pFullUrl);
result = E_FAIL;
break;
}

if (result != NO_ERROR)
{
break;
}

// Reset the Request ID to handle the next request.
HTTP_SET_NULL_ID(&requestId);
}
else
{
break;
}
}

return result;
}

int main(int argc, char** argv)
{
std::wstring url;
if (argc == 1)
{
url = L"http://127.0.0.1:80/NativeBigDataTest";
}
else
{
std::string serverIp(argv[1]);
url = L"http://" + std::wstring(serverIp.begin(), serverIp.end()) + L":80/NativeBigDataTest";
}

HTTP_SERVER_SESSION_ID session;
HTTP_URL_GROUP_ID urlGroup;
HANDLE hReqQueue = NULL;
HTTP_BINDING_INFO bindingInfo;
ULONG retCode;

// Initialize HTTP Server APIs
retCode = HttpInitialize(
HTTPAPI_VERSION_2,
HTTP_INITIALIZE_SERVER,    // Flags
NULL                       // Reserved
);
if (retCode != NO_ERROR)
{
wprintf(L"HttpInitialize failed with %lu \n", retCode);
return retCode;
}

// Create server session
retCode = HttpCreateServerSession(HTTPAPI_VERSION_2, &session, 0);
if (retCode != NO_ERROR)
{
wprintf(L"HttpCreateServerSession failed with %lu \n", retCode);
goto CleanUp5;
}

// Create Url Group
retCode = HttpCreateUrlGroup(session, &urlGroup, 0);
if (retCode != NO_ERROR)
{
wprintf(L"HttpCreateUrlGroup failed with %lu \n", retCode);
goto CleanUp4;
}

// Add url to group
retCode = HttpAddUrlToUrlGroup(urlGroup, url.c_str(), HTTP_URL_CONTEXT{}, 0);
if (retCode != NO_ERROR)
{
wprintf(L"HttpAddUrlToUrlGroup failed with %lu \n", retCode);
goto CleanUp3;
}

// Create a Request Queue Handle
retCode = HttpCreateRequestQueue(HTTPAPI_VERSION_2, NULL, NULL, 0, &hReqQueue);
if (retCode != NO_ERROR)
{
wprintf(L"HttpCreateHttpHandle failed with %lu \n", retCode);
goto CleanUp2;
}

// Bind request queue to url group
bindingInfo.RequestQueueHandle = hReqQueue;
bindingInfo.Flags.Present = 1;
retCode = HttpSetUrlGroupProperty(urlGroup, HttpServerBindingProperty, &bindingInfo, sizeof(bindingInfo));
if (retCode != NO_ERROR)
{
wprintf(L"HttpSetUrlGroupProperty failed with %lu \n", retCode);
goto CleanUp1;
}

wprintf(L"Listening on url %s\n", url.c_str());

// Receive requests
ReceiveRequests(hReqQueue);

CleanUp1:
// Close the Request Queue handle.
retCode = HttpCloseRequestQueue(hReqQueue);
if (retCode != NO_ERROR)
{
wprintf(L"HttpCloseRequestQueue failed with %lu \n", retCode);
}

CleanUp2:
// Call HttpRemoveUrlFromUrlGroup for all added URLs.
retCode = HttpRemoveUrlFromUrlGroup(
urlGroup,
url.c_str(),
0);
if (retCode != NO_ERROR)
{
wprintf(L"HttpRemoveUrlFromUrlGroup failed with %lu \n", retCode);
}

CleanUp3:
// Close Url group
retCode = HttpCloseUrlGroup(urlGroup);
if (retCode != NO_ERROR)
{
wprintf(L"HttpCloseUrlGroup failed with %lu \n", retCode);
}

CleanUp4:
// Close Session
retCode = HttpCloseServerSession(session);
if (retCode != NO_ERROR)
{
wprintf(L"HttpCloseServerSession failed with %lu \n", retCode);
}

CleanUp5:
// Call HttpTerminate.
retCode = HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
if (retCode != NO_ERROR)
{
wprintf(L"HttpTerminate failed with %lu \n", retCode);
}

return retCode;
}

3

Решение

После анализа большого количества записей в моем собственном HTTPServer, использующем функцию HttpReceiveHttpRequest, я обнаружил, что, если бы я НЕ использовал флаг HTTP_RECEIVE_REQUEST_ENTITY_BODY_FLAG_FILL_BUFFER, он всегда сначала получал пакет из 1 байта, а после этого получал полный буфер. (вот: я обнаружил это поведение на MS-Windows 8). Если бы я использовал вышеупомянутый флаг, он сразу же получил бы буфер сразу, даже в первый раз.

Я не знаю, будет ли это означать большее или меньшее количество циклических переходов, но для больших файлов пропускная способность, измеренная в миллисекундах, была почти до 50% после использования этого флага.

Пожалуйста, проверьте и на других версиях MS-Windows!

0

Другие решения


По вопросам рекламы [email protected]