NgHttp2单个请求触发两次request data handler问题排查
问题根因
nghttp2 asio 服务端的 on_data 回调设计为两次触发机制:
- 收到请求体数据时触发,此时
len参数为实际接收到的字节数(大于0),如果请求体分多帧传输还可能触发多次大于0的回调 - 整个请求体全部接收完成后,会额外触发一次
len == 0的回调,用于通知上层业务请求数据已接收完毕
你当前的代码没有判断len参数的有效值,只要触发回调就执行计数+1、返回响应的逻辑:
- 不带请求体的请求:只会触发1次
len == 0的回调,所以输出1条日志符合预期 - 带10字节请求体的请求:先触发1次
len=10的回调(输出1),再触发1次len=0的回调(输出2),所以最终输出2条日志
修复方案
根据业务需求选择任意一种即可:
方案1:仅处理实际携带数据的回调
只在len > 0时执行业务逻辑,忽略收尾的空回调:
server.handle("/", [&count](const request &req, const response &res) { req.on_data([&res, &count](const uint8_t *data, std::size_t len) { // 忽略请求接收完成的空回调 if (len == 0) { return; } std::cerr << ++count << '\n'; res.write_head(200); res.end("done"); }); });
方案2:等待全部请求体接收完成后再处理
如果需要等所有数据收完再统一处理,只在len == 0时执行业务逻辑,之前的回调只负责拼接数据:
server.handle("/", [&count](const request &req, const response &res) { // 存储完整请求体 auto req_body = std::make_shared<std::string>(); req.on_data([&res, &count, req_body](const uint8_t *data, std::size_t len) { if (len > 0) { req_body->append(reinterpret_cast<const char*>(data), len); return; } // 所有数据接收完成后再执行核心逻辑 std::cerr << ++count << '\n'; res.write_head(200); res.end("done"); }); });
方案3:添加处理标记避免重复执行
如果业务逻辑允许提前返回响应,可以加个状态标记防止回调逻辑重复执行:
server.handle("/", [&count](const request &req, const response &res) { auto processed = std::make_shared<bool>(false); req.on_data([&res, &count, processed](const uint8_t *data, std::size_t len) { if (*processed) { return; } *processed = true; std::cerr << ++count << '\n'; res.write_head(200); res.end("done"); }); });
内容的提问来源于stack exchange,提问作者user16516564
相关产品推荐
相关产品推荐

