Skip to content

Commit bab0140

Browse files
authored
Fixes skupperproject#1424: Removed double calls to link free and also removed calls to qdr_connection_free from timer handler (skupperproject#1426)
1 parent 1ad457c commit bab0140

File tree

2 files changed

+88
-74
lines changed

2 files changed

+88
-74
lines changed

src/adaptors/http2/http2_adaptor.c

+69-54
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ static void encrypt_outgoing_tls(qdr_http2_connection_t *conn, qd_adaptor_buffer
7676
bool write_buffers);
7777
static bool schedule_activation(qdr_http2_connection_t *conn, qd_duration_t msec);
7878
static void cancel_activation(qdr_http2_connection_t *conn);
79+
static void close_connections(qdr_http2_connection_t* conn);
7980

8081
static void grant_read_buffers(qdr_http2_connection_t *conn, const char *msg)
8182
{
@@ -87,6 +88,27 @@ static void grant_read_buffers(qdr_http2_connection_t *conn, const char *msg)
8788
buffers);
8889
}
8990

91+
92+
static void free_stream_dispatcher_link(qdr_http2_connection_t *conn)
93+
{
94+
if (conn && conn->stream_dispatcher) {
95+
qdr_http2_stream_data_t *stream_data = qdr_link_get_context(conn->stream_dispatcher);
96+
qd_log(LOG_HTTP_ADAPTOR, QD_LOG_DEBUG,
97+
"[C%" PRIu64 "] Detaching stream dispatcher link on egress connection, freed associated stream data",
98+
conn->conn_id);
99+
if (stream_data) {
100+
qd_log(LOG_HTTP_ADAPTOR, QD_LOG_DEBUG, "[C%" PRIu64 "] Freeing stream_data (stream_dispatcher, free_stream_dispatcher_link) (%p)", conn->conn_id, (void *) stream_data);
101+
free_qdr_http2_stream_data_t(stream_data);
102+
}
103+
qdr_link_set_context(conn->stream_dispatcher, 0);
104+
qdr_link_detach(conn->stream_dispatcher, QD_CLOSED, 0);
105+
106+
107+
conn->stream_dispatcher = 0;
108+
conn->stream_dispatcher_stream_data = 0;
109+
}
110+
}
111+
90112
/**
91113
* If an ALPN protocol was detected by the TLS API, we make sure that the protocol matches the "h2" (short for http2) protocol.
92114
* The connection is simply closed if any other protocol (other than h2) was detected.
@@ -416,6 +438,7 @@ void free_qdr_http2_connection(qdr_http2_connection_t* http_conn, bool on_shutdo
416438
http_conn->remote_address = 0;
417439
}
418440
if (http_conn->activate_timer) {
441+
cancel_activation(http_conn);
419442
qd_timer_free(http_conn->activate_timer);
420443
http_conn->activate_timer = 0;
421444
}
@@ -2878,7 +2901,6 @@ static void close_connections(qdr_http2_connection_t* conn)
28782901
conn->conn_id);
28792902
conn->dummy_link = 0;
28802903
}
2881-
28822904
qdr_connection_set_context(conn->qdr_conn, 0);
28832905
if (conn->qdr_conn) {
28842906
qdr_connection_closed(conn->qdr_conn);
@@ -2890,17 +2912,21 @@ static void close_connections(qdr_http2_connection_t* conn)
28902912
qdr_action_enqueue(http2_adaptor->core, action);
28912913
}
28922914

2893-
static void clean_http2_conn(qdr_http2_connection_t* conn)
2915+
static void clean_http2_conn(qdr_http2_connection_t* conn, bool free_buffers)
28942916
{
28952917
free_all_connection_streams(conn, false);
2896-
28972918
//
28982919
// This closes the nghttp2 session. Next time when a new connection is opened, a new nghttp2 session
28992920
// will be created by calling nghttp2_session_client_new
29002921
//
2901-
nghttp2_session_del(conn->session);
2902-
conn->session = 0;
2903-
qd_adaptor_buffer_list_free_buffers(&conn->out_buffs);
2922+
if (conn->session) {
2923+
nghttp2_session_del(conn->session);
2924+
conn->session = 0;
2925+
}
2926+
2927+
if (free_buffers) {
2928+
qd_adaptor_buffer_list_free_buffers(&conn->out_buffs);
2929+
}
29042930

29052931
// Free tls related stuff if need be.
29062932
if (conn->tls) {
@@ -2912,46 +2938,28 @@ static void clean_http2_conn(qdr_http2_connection_t* conn)
29122938

29132939
static void handle_disconnected(qdr_http2_connection_t* conn)
29142940
{
2915-
sys_mutex_lock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
29162941
if (conn->pn_raw_conn) {
29172942
qd_log(LOG_HTTP_ADAPTOR, QD_LOG_DEBUG,
29182943
"[C%" PRIu64 "] handle_disconnected Setting conn->pn_raw_conn=0", conn->conn_id);
29192944
pn_raw_connection_set_context(conn->pn_raw_conn, 0);
29202945
conn->pn_raw_conn = 0;
29212946
}
2922-
29232947
if (conn->ingress) {
2924-
clean_http2_conn(conn);
2948+
clean_http2_conn(conn, false);
29252949
close_connections(conn);
29262950
}
29272951
else {
29282952
if (conn->stream_dispatcher) {
2929-
qdr_http2_stream_data_t *stream_data = qdr_link_get_context(conn->stream_dispatcher);
2930-
qd_log(LOG_HTTP_ADAPTOR, QD_LOG_DEBUG,
2931-
"[C%" PRIu64 "] Detaching stream dispatcher link on egress connection, freed associated stream data",
2932-
conn->conn_id);
2933-
qdr_link_detach(conn->stream_dispatcher, QD_CLOSED, 0);
2934-
qdr_link_set_context(conn->stream_dispatcher, 0);
2935-
conn->stream_dispatcher = 0;
2936-
if (stream_data) {
2937-
qd_log(LOG_HTTP_ADAPTOR, QD_LOG_DEBUG,
2938-
"[C%" PRIu64 "] Freeing stream_data (stream_dispatcher, handle_disconnected) (%p)",
2939-
conn->conn_id, (void *) stream_data);
2940-
free_qdr_http2_stream_data_t(stream_data);
2941-
}
2942-
conn->stream_dispatcher_stream_data = 0;
2943-
2953+
free_stream_dispatcher_link(conn);
29442954
}
2945-
29462955
if (conn->delete_egress_connections) {
2947-
clean_http2_conn(conn);
2956+
clean_http2_conn(conn, false);
29482957
close_connections(conn);
29492958
}
29502959
else {
2951-
clean_http2_conn(conn);
2960+
clean_http2_conn(conn, true);
29522961
}
29532962
}
2954-
sys_mutex_unlock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
29552963
}
29562964

29572965

@@ -2965,24 +2973,6 @@ static void egress_conn_timer_handler(void *context)
29652973
// Protect with the lock when accessing conn->pn_raw_conn
29662974
sys_mutex_lock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
29672975

2968-
if (conn->delete_egress_connections) {
2969-
//
2970-
// The connector that this connection is associated with has been deleted.
2971-
// Free the associated connections
2972-
// It is ok to call qdr_connection_closed from this timer callback.
2973-
//
2974-
sys_mutex_unlock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
2975-
if (conn->qdr_conn) {
2976-
qdr_connection_closed(conn->qdr_conn);
2977-
qd_connection_counter_dec(QD_PROTOCOL_HTTP2);
2978-
conn->qdr_conn = 0;
2979-
}
2980-
2981-
if (conn->connection_status == QD_CONNECTION_NEW)
2982-
free_qdr_http2_connection(conn, false);
2983-
return;
2984-
}
2985-
29862976
//
29872977
// If there is already a conn->pn_raw_conn, don't try to connect again.
29882978
//
@@ -3250,18 +3240,22 @@ static void handle_connection_event(pn_event_t *e, qd_server_t *qd_server, void
32503240
break;
32513241
}
32523242
case PN_RAW_CONNECTION_DISCONNECTED: {
3243+
//
3244+
// Lock immediately since a delete connector can be called from a management thread
3245+
// This same lock is used to lock the delete_connector function as well.
3246+
//
3247+
sys_mutex_lock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
32533248
conn->connection_status = QD_CONNECTION_DISCONNECTED;
32543249
qd_set_condition_on_vflow(conn->pn_raw_conn, conn->vflow);
32553250
if (!conn->ingress) {
32563251
conn->initial_settings_frame_sent = false;
3257-
sys_mutex_lock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
3252+
32583253
if (conn->delete_egress_connections) {
3259-
sys_mutex_unlock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
3254+
32603255
// The egress connection has been deleted, cancel any pending timer
32613256
cancel_activation(conn);
32623257
}
32633258
else {
3264-
sys_mutex_unlock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
32653259
if (schedule_activation(conn, 2000))
32663260
qd_log(LOG_HTTP_ADAPTOR, QD_LOG_DEBUG,
32673261
"[C%" PRIu64 "] Scheduling 2 second timer to reconnect to egress connection",
@@ -3275,6 +3269,7 @@ static void handle_connection_event(pn_event_t *e, qd_server_t *qd_server, void
32753269
"[C%" PRIu64 "] PN_RAW_CONNECTION_DISCONNECTED, ingress=%i, drained_buffers=%i", conn->conn_id,
32763270
conn->ingress, drained_buffers);
32773271
handle_disconnected(conn);
3272+
sys_mutex_unlock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
32783273
break;
32793274
}
32803275
case PN_RAW_CONNECTION_NEED_WRITE_BUFFERS: {
@@ -3299,8 +3294,9 @@ static void handle_connection_event(pn_event_t *e, qd_server_t *qd_server, void
32993294
conn->conn_id);
33003295
handle_incoming_http(conn);
33013296
}
3302-
3303-
while (qdr_connection_process(conn->qdr_conn)) {}
3297+
if (conn->qdr_conn) {
3298+
while (qdr_connection_process(conn->qdr_conn)) {}
3299+
}
33043300
break;
33053301
}
33063302
case PN_RAW_CONNECTION_READ: {
@@ -3381,14 +3377,33 @@ void qd_http2_delete_connector(qd_dispatch_t *qd, qd_http_connector_t *connector
33813377
//
33823378
// Deleting a connector must delete the corresponding qdr_connection_t and qdr_http2_connection_t objects also.
33833379
//
3380+
// Lock immediately since a PN_RAW_CONNECTION_DISCONNECTED event can be raised from another thread.
3381+
// This same lock is used to lock in the handler of PN_RAW_CONNECTION_DISCONNECTED as well.
3382+
//
3383+
sys_mutex_lock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
33843384
if (connector->ctx) {
33853385
qdr_connection_t *qdr_conn = (qdr_connection_t *) connector->ctx;
33863386
qdr_http2_connection_t *http_conn = qdr_connection_get_context(qdr_conn);
3387-
sys_mutex_lock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
3387+
33883388
http_conn->delete_egress_connections = true;
3389-
sys_mutex_unlock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
3390-
qdr_core_close_connection(qdr_conn);
3389+
//
3390+
// The stream_dispatcher is a qdr_link. When a qdr_connection_t gets freed, it frees
3391+
// all links in that connection. We want to explicitly detach and free the stream_dispatcher ourselves.
3392+
// https://github.com/skupperproject/skupper-router/issues/1424
3393+
//
3394+
free_stream_dispatcher_link(http_conn);
3395+
if (http_conn->connection_status == QD_CONNECTION_DISCONNECTED) {
3396+
//
3397+
// This connection has already received the PN_RAW_CONNECTION_DISCONNECTED event
3398+
//
3399+
clean_http2_conn(http_conn, false);
3400+
close_connections(http_conn);
3401+
}
3402+
else {
3403+
qdr_core_close_connection(qdr_conn);
3404+
}
33913405
}
3406+
sys_mutex_unlock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
33923407
qd_http_connector_decref(connector);
33933408
}
33943409
}

tests/system_tests_http2.py

+19-20
Original file line numberDiff line numberDiff line change
@@ -337,25 +337,25 @@ def check_connector_delete(self, client_addr, server_addr, server_port,
337337
self.assertEqual(len(connectors), 0)
338338

339339
# Deleting the connector must have taken out the connection to the server.
340-
connections = qd_manager.query(CONNECTION_TYPE)
341-
server_conn_found = False
342-
for conn in connections:
343-
if str(server_port) in conn['name']:
344-
server_conn_found = True
345-
break
346-
self.assertFalse(server_conn_found)
347-
348-
sleep(2)
349-
350-
# Now, run a curl client GET request with a timeout
351-
request_timed_out = False
352-
try:
353-
_, out, _ = self.run_curl(client_addr, args=self.get_all_curl_args(), timeout=5)
354-
print(out)
355-
except Exception as e:
356-
request_timed_out = True
357-
358-
self.assertTrue(request_timed_out)
340+
def query_connections():
341+
conns = qd_manager.query(CONNECTION_TYPE)
342+
server_connection_found = False
343+
for conn in conns:
344+
if str(server_port) in conn['name']:
345+
server_connection_found = True
346+
break
347+
self.assertFalse(server_connection_found)
348+
retry_assertion(query_connections)
349+
350+
def retry_request():
351+
request_timed_out = False
352+
# Now, run a curl client GET request with a timeout
353+
try:
354+
_, out, _ = self.run_curl(client_addr, args=self.get_all_curl_args(), timeout=5)
355+
except Exception as e:
356+
request_timed_out = True
357+
self.assertTrue(request_timed_out)
358+
retry_assertion(retry_request)
359359

360360
# Add back the httpConnector
361361
# skmanage CREATE type=httpConnector address=examples.com host=127.0.0.1 port=80 protocolVersion=HTTP2
@@ -1013,7 +1013,6 @@ def test_check_connector_delete(self):
10131013
request_timed_out = False
10141014
try:
10151015
_, out, _ = self.run_curl(address, args=["--head"], timeout=3)
1016-
print(out)
10171016
except Exception as e:
10181017
request_timed_out = True
10191018
self.assertTrue(request_timed_out)

0 commit comments

Comments
 (0)