nghttp2 / nghttp2

nghttp2 - HTTP/2 C Library and tools
https://nghttp2.org
Other
4.62k stars 871 forks source link

Protocol error if calling nghttp2_submit_headers multiple times for one stream #2241

Closed antrv closed 4 weeks ago

antrv commented 4 weeks ago

Hi,

I want to send headers to the server in two or more steps, so as not to allocate a buffer for headers. For example, to send no more than 50 fields at a time. Does the nghttp2 library allow this?

When I create a stream by calling nghttp2_submit_headers in the client session, I pass the first batch of header fields and get a new stream ID like this.

// Send request
std::vector<nghttp2_nv> headers;
headers.push_back(toNameValuePair(":method", "POST"));
headers.push_back(toNameValuePair(":scheme", "https"));
headers.push_back(toNameValuePair(":authority", host));
headers.push_back(toNameValuePair(":path", "/test"));
headers.push_back(toNameValuePair("Content-Length", payloadSize));
headers.push_back(toNameValuePair("Content-Type", "text/plain; charset=utf-8"));
headers.push_back(toNameValuePair("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"));
headers.push_back(toNameValuePair("Accept-Encoding", "gzip, deflate, br, zstd"));
headers.push_back(toNameValuePair("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"));

const int32_t streamId = nghttp2_submit_headers(session.get(),
    NGHTTP2_FLAG_NONE, -1, nullptr, headers.data(),
    headers.size(), nullptr);

EXPECT_EQ(streamId, 1);

After that, I send the data to the server using nghttp2_session_mem_send2.

// Send pending frames using nghttp2_session_mem_send2
co_await asyncSendFrames(connection, session.get(), ec);

After that, I call nghttp2_submit_headers again with the already received stream ID, and pass the second batch of header fields like this.

headers.clear();
headers.push_back(toNameValuePair("Test1", "test1"));
headers.push_back(toNameValuePair("Test2", "test2"));
headers.push_back(toNameValuePair("Test3", "test3"));

EXPECT_FALSE(nghttp2_submit_headers(session.get(),
    NGHTTP2_FLAG_NONE, streamId, nullptr, headers.data(),
    headers.size(), nullptr));

// Send pending frames
co_await asyncSendFrames(connection, session.get(), ec);

After that, I send the data by calling nghttp2_submit_data2.

Http2DataProvider dataProvider;
dataProvider.data = payload;
EXPECT_FALSE(nghttp2_submit_data2(session.get(), NGHTTP2_FLAG_END_STREAM,
    streamId, &dataProvider));

// Send pending frames
co_await asyncSendFrames(connection, session.get(), ec);

ISSUE: The server responds with RST_STREAM frame with protocol error (1). In Wireshark I can see that nghttp2 library generates two HEADERS (type = 1) frames both with END_HEADERS (4) flag. The first header frame must be without END_HEADERS flag and the second frame must be CONTINUATION frame.

If I send all headers in one nghttp2_submit_headers call, then the server responds with a http response.

tatsuhiro-t commented 4 weeks ago

I want to send headers to the server in two or more steps, so as not to allocate a buffer for headers. For example, to send no more than 50 fields at a time. Does the nghttp2 library allow this?

No.

antrv commented 4 weeks ago

Thank you!