jjzjj

c++ - HTTP_RECEIVE_REQUEST_FLAG_FLUSH_BODY 不复制 http body

coder 2024-06-06 原文

根据MSDN documentation ,传递到 HttpReceiveHttpRequest 函数中的 HTTP_RECEIVE_REQUEST_FLAG_FLUSH_BODY 标志导致实体主体被复制到传入的 HTTP_REQUEST 结构中。

我写了一个简单的 http 服务器(见下文),它调用这个函数并设置了这个标志。然后,我从测试客户端发送了一个 4MB 的 POST 请求,并将调试器附加到 http 服务器,以查看函数返回后有多少字节被复制到 HTTP_REQUEST 结构中;我观察到这没有复制 http 正文,只复制了 header 。

有没有办法让 HttpReceiveHttpRequest 将整个 4MB 主体复制到 HTTP 请求的 pEntityChunks 部分?

注意:我验证了 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;
}

最佳答案

在分析了我自己的使用 HttpReceiveHttpRequest 函数的 HTTPServer 中的大量日志后,我发现如果我没有使用 HTTP_RECEIVE_REQUEST_ENTITY_BODY_FLAG_FILL_BUFFER 标志,它总是先接收一个 1 字节的包,然后再接收一个完整的缓冲区. (注意:我在 MS-Windows 8 上发现了这种行为)。如果我确实使用了上述标志,即使是第一次,它也会立即收到一个缓冲区。

我不知道这是否意味着更多或更少的往返,但在使用此标志后,在较大的文件上以毫秒为单位测量的吞吐量几乎下降到 50%。

请同时在其他版本的 MS-Windows 上测试!

关于c++ - HTTP_RECEIVE_REQUEST_FLAG_FLUSH_BODY 不复制 http body,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28006881/

有关c++ - HTTP_RECEIVE_REQUEST_FLAG_FLUSH_BODY 不复制 http body的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby - 如何模拟 Net::HTTP::Post? - 2

    是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

  3. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  4. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  5. UE4 源码阅读:从引擎启动到Receive Begin Play - 2

    一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame

  6. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  7. ruby-on-rails - Rails - 从命名路由中提取 HTTP 动词 - 2

    Rails中有没有一种方法可以提取与路由关联的HTTP动词?例如,给定这样的路线:将“users”匹配到:“users#show”,通过:[:get,:post]我能实现这样的目标吗?users_path.respond_to?(:get)(显然#respond_to不是正确的方法)我最接近的是通过执行以下操作,但它似乎并不令人满意。Rails.application.routes.routes.named_routes["users"].constraints[:request_method]#=>/^GET$/对于上下文,我有一个设置cookie然后执行redirect_to:ba

  8. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  9. ruby-on-rails - Heroku 吃掉了我的自定义 HTTP header - 2

    我正在使用Heroku(heroku.com)来部署我的Rails应用程序,并且正在构建一个iPhone客户端来与之交互。我的目的是将手机的唯一设备标识符作为HTTPheader传递给应用程序以进行身份​​验证。当我在本地测试时,我的header通过得很好,但在Heroku上它似乎去掉了我的自定义header。我用ruby​​脚本验证:url=URI.parse('http://#{myapp}.heroku.com/')#url=URI.parse('http://localhost:3000/')req=Net::HTTP::Post.new(url.path)#boguspara

  10. ruby-on-rails - 使用 HTTP.get_response 检索 Facebook 访问 token 时出现 Rails EOF 错误 - 2

    我试图在我的网站上实现使用Facebook登录功能,但在尝试从Facebook取回访问token时遇到障碍。这是我的代码:ifparams[:error_reason]=="user_denied"thenflash[:error]="TologinwithFacebook,youmustclick'Allow'toletthesiteaccessyourinformation"redirect_to:loginelsifparams[:code]thentoken_uri=URI.parse("https://graph.facebook.com/oauth/access_token

随机推荐