[英]NgHttp2 invokes request data handler twice, for one single request
I am running a HTTP2 server by using nghttp2
.我正在使用
nghttp2
运行 HTTP2 服务器。 I am trying to figure out why the on_data
handler in the server.cpp
is called twice.我试图弄清楚为什么
server.cpp
中的on_data
处理程序被调用了两次。 I know it's called twice because when I send a request that contains data, I get the following log output for a single request in the server.我知道它被调用了两次,因为当我发送一个包含数据的请求时,我得到了服务器中单个请求的以下日志输出。
1
2
But if I don't send data in the request.但是如果我不在请求中发送数据。 The log output for single request is what I would expect
单个请求的日志输出是我所期望的
1
I send data to the server by using the client and with this line of code我使用客户端和这行代码将数据发送到服务器
auto req = sess.submit(ec, "GET", "http://127.0.0.1:3000/", "aaaaaaaaaa");
To not send data I just remove the third parameter.为了不发送数据,我只需删除第三个参数。
server.cpp
Compiled with g++ server.cpp -o server.out -lnghttp2_asio -lboost_system -lcrypto -lpthread -lssl -lboost_thread
使用
g++ server.cpp -o server.out -lnghttp2_asio -lboost_system -lcrypto -lpthread -lssl -lboost_thread
#include <iostream>
#include <nghttp2/asio_http2_server.h>
using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::server;
int main() {
std::string hostname = "127.0.0.1";
std::string port = "3000";
boost::system::error_code ec;
http2 server;
int count = 0;
server.handle("/", [&count](const request &req, const response &res) {
req.on_data([&res, &count](const uint8_t *data, std::size_t len) {
std::cerr << ++count << '\n';
res.write_head(200);
res.end("done");
});
});
if (server.listen_and_serve(ec, hostname, port)) {
std::cerr << "error: " << ec.message() << std::endl;
}
}
Compiled with g++ client.cpp -o client.out -lnghttp2_asio -lboost_system -lcrypto -lpthread -lssl -lboost_thread
使用
g++ client.cpp -o client.out -lnghttp2_asio -lboost_system -lcrypto -lpthread -lssl -lboost_thread
#include <iostream>
#include <nghttp2/asio_http2_client.h>
using boost::asio::ip::tcp;
using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::client;
int main(int argc, char *argv[]) {
boost::system::error_code ec;
boost::asio::io_service io_service;
// connect to localhost:3000
session sess(io_service, "127.0.0.1", "3000");
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
boost::system::error_code ec;
auto req = sess.submit(ec, "GET", "http://127.0.0.1:3000/", "aaaaaaaaaa");
req->on_response([](const response &res) {
// print status code and response header fields.
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
for (auto &kv : res.header()) {
std::cerr << kv.first << ": " << kv.second.value << "\n";
}
std::cerr << std::endl;
res.on_data([](const uint8_t *data, std::size_t len) {
std::cerr.write(reinterpret_cast<const char *>(data), len);
std::cerr << std::endl;
});
});
req->on_close([&sess](uint32_t error_code) {
// shutdown session after first request was done.
sess.shutdown();
});
});
sess.on_error([](const boost::system::error_code &ec) {
std::cerr << "error: " << ec.message() << std::endl;
});
io_service.run();
}
If you print out also the length of the data frames, there would be more information to reason about.如果您还打印出数据帧的长度,则会有更多信息需要推理。
My guess is that the client sends a first DATA
frame with length 10 for aaaaaaaaaa
and end_stream=false
, and a second DATA
frame with length 0 and end_stream=true
, to signal the end of the stream on the request side.我的猜测是客户端为
aaaaaaaaaa
和end_stream=false
发送长度为 10 的第一个DATA
帧,以及长度为 0 和end_stream=true
的第二个DATA
帧,以表示请求端的流结束。
For the case where you don't send content, the client is probably sending just a DATA
frame of length 0 with end_stream=true
.对于不发送内容的情况,客户端可能只发送一个长度为 0 且带有
end_stream=true
的DATA
帧。
I imagine this can be optimized by using a different API (different method on sess
or different parameters) so that you can make it more efficient.我想这可以通过使用不同的 API(不同的
sess
方法或不同的参数)来优化,这样你就可以提高效率。
The first case, a request with content, would ideally have a single DATA
frame of length 10 and end_stream=true
, while the second case (no request content), would have just a HEADERS
frame with end_stream=true
and no DATA
frame at all.第一种情况,一个有内容的请求,理想情况下有一个长度为 10 和
end_stream=true
的DATA
帧,而第二种情况(没有请求内容)只有一个带有end_stream=true
的HEADERS
帧,根本没有DATA
帧.
When request body is received, on_data
primitive is called N times, until all the data received on nghttp2 reactor is consumed.当接收到请求体时,
on_data
原语被调用 N 次,直到 nghttp2 reactor 上接收到的所有数据都被消耗完。
Take a look to this gist :看看这个要点:
As you can see, I'm using algorithm std::copy and stringstream to append the data chunks received when length is positive.如您所见,我使用算法 std::copy 和 stringstream来附加长度为正时收到的数据块。 If you send, for example, 806 bytes,
on_data
could be called once with len=806, or perhaps twice with len=8 and then len=798.例如,如果您发送 806 字节,
on_data
可以在 len=806 时调用一次,或者在 len=8 和 len=798 时调用两次。
Finally, on_data
is called with len=0 and then you could process the complete transaction.最后,使用 len=0 调用
on_data
,然后您可以处理完整的事务。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.