Skip to content

Commit e8f64f8

Browse files
committed
fix core: handle http2 GOAWAY
commit_hash:1bae7472ddacc5036cc9b19235297d9f5e35ffce
1 parent ddd2ab6 commit e8f64f8

6 files changed

Lines changed: 37 additions & 26 deletions

File tree

core/functional_tests/http2server/tests/test_h2spec.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# h2spec compliance tests https://github.com/summerwind/h2spec/tree/master/http2
1+
# h2spec compliance tests https://github.com/summerwind/h2spec
22
#
3-
# Names mirror h2spec spec IDs: http2/<section>/<case>.
4-
# Section maps to a source file, e.g. http2/6_7_ping.go -> RFC 7540 §6.7 (PING).
5-
# Case is the 1-based index of AddTestCase in that file; run via `h2spec http2/6.7/2`.
3+
# Names mirror h2spec spec IDs: <spec>/<section>/<case>.
4+
# Section maps to a source file, e.g. http2/6_7_ping.go or generic/3_8_goaway.go.
5+
# Case is the 1-based index of AddTestCase in that file.
66
import asyncio
77

88
import pytest
@@ -21,3 +21,21 @@ async def test_h2spec_6_7_2_ping_ack_ignored(create_connection, service_client):
2121

2222
with pytest.raises(asyncio.TimeoutError):
2323
await sock.recv(utils.RECEIVE_SIZE, timeout=0.5)
24+
25+
26+
async def test_h2spec_generic_3_8_1_client_goaway(create_connection, monitor_client, service_client):
27+
# generic/3_8_goaway.go, case 1 ("Sends a GOAWAY frame"):
28+
# after client GOAWAY the server must close the connection or answer a follow-up PING.
29+
await service_client.update_server_state()
30+
31+
async with monitor_client.metrics_diff(prefix='server.requests.http2') as differ:
32+
async with create_connection() as (sock, conn):
33+
conn.close_connection(error_code=0)
34+
ping_data = b'h2spec\x00\x00'
35+
ping = utils.create_frame(utils.PING_FRAME, utils.EMPTY_FLAGS, 0, ping_data)
36+
await sock.sendall(conn.data_to_send() + ping)
37+
38+
receive = await sock.recv(utils.RECEIVE_SIZE, timeout=2.0)
39+
assert receive == b''
40+
41+
assert differ.value_at('goaway') == 1

core/functional_tests/http2server/tests/test_low_level.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import asyncio
2-
import contextlib
32
import struct
43

54
import h2.connection
@@ -425,25 +424,6 @@ async def test_single_reset_keeps_connection_usable(
425424
assert differ.value_at('reset-streams') == 1
426425

427426

428-
async def test_client_goaway_metric(create_connection, monitor_client, service_client):
429-
await service_client.update_server_state()
430-
431-
async with monitor_client.metrics_diff(prefix='server.requests.http2') as differ:
432-
async with create_connection() as (sock, conn):
433-
conn.close_connection(error_code=0)
434-
await sock.sendall(conn.data_to_send())
435-
# Keep the socket open until the server reads GOAWAY (and optionally
436-
# responds). Closing immediately races with metrics collection.
437-
with contextlib.suppress(asyncio.TimeoutError):
438-
while True:
439-
receive = await sock.recv(utils.RECEIVE_SIZE, timeout=1.0)
440-
if not receive:
441-
break
442-
conn.receive_data(receive)
443-
444-
assert differ.value_at('goaway') == 1
445-
446-
447427
async def test_stream_already_closed(create_connection, service_client):
448428
async with create_connection() as (sock, conn):
449429

core/src/server/http/http2_session.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ void Http2Session::FinalizeRequest(Stream& stream) {
353353
}
354354
}
355355

356-
bool Http2Session::ConnectionIsOk() {
356+
bool Http2Session::ConnectionIsOk() const {
357357
return nghttp2_session_want_read(session_.get()) != 0 || nghttp2_session_want_write(session_.get()) != 0;
358358
}
359359

core/src/server/http/http2_session.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ class Http2Session final : public request::RequestParser {
6464
void WriteWhileWant();
6565
void HandleStreamingEvents();
6666

67+
bool ConnectionIsOk() const;
68+
6769
private:
6870
friend class Http2ResponseWriter;
6971

@@ -113,7 +115,6 @@ class Http2Session final : public request::RequestParser {
113115
void SubmitRstStream(Stream::Id stream_id);
114116

115117
void FinalizeRequest(Stream& stream);
116-
bool ConnectionIsOk();
117118

118119
const net::Http2SessionConfig& config_;
119120

core/src/server/net/http2_connection.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ void Http2Connection::ListenForRequests() {
9696
while (!engine::current_task::ShouldCancel()) {
9797
StartAllRequestTasks(wait_any);
9898

99+
if (ShouldCloseConnection()) {
100+
return;
101+
}
102+
99103
const auto ready_id = wait_any.Wait();
100104
if (!ready_id) {
101105
UASSERT(ready_id == utils::unexpected(engine::WaitAnyError::kCancelled));
@@ -178,6 +182,12 @@ void Http2Connection::SendResponse(http::HttpRequest& request) noexcept {
178182
request.WriteAccessLogs(request_handler_.LoggerAccess(), request_handler_.LoggerAccessTskv(), GetPeerName());
179183
}
180184

185+
bool Http2Connection::ShouldCloseConnection() const noexcept {
186+
UASSERT(parser_);
187+
// Check handler_tasks_ to do graceful shutdown.
188+
return !parser_->ConnectionIsOk() && handler_tasks_.empty();
189+
}
190+
181191
std::unique_ptr<http::Http2Session> Http2Connection::MakeParser() {
182192
const auto on_req_cb = [this](HttpRequestPtr&& request_ptr) {
183193
pending_requests_.push_back(std::move(request_ptr));

core/src/server/net/http2_connection.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ class Http2Connection final : public ConnectionBase {
6868
std::unique_ptr<http::Http2Session> MakeParser();
6969
void EnsureHttp2();
7070

71+
bool ShouldCloseConnection() const noexcept;
72+
7173
const ConnectionConfig& config_;
7274
const request::HttpRequestConfig& handler_defaults_config_;
7375
const http::RequestHandlerBase& request_handler_;

0 commit comments

Comments
 (0)