Skip to content

Commit 8d767ef

Browse files
authored
Fixes skupperproject#1440: move TLS domain and session setup to I/O thread (skupperproject#1443)
Previously TLS domain and session setup for a new connection occurred either in the (shared) listener thread or the (shared) egress-dispatcher connection thread. TLS domain/session setup is expensive timewise (> 50msec). This caused the listener/connector thread to slow down significantly when under load since each connection setup was serialized. This patch moves the TLS domain/session setup to the new connections I/O thread, allowing the listener/connectors to service connection requests much faster and providing some parallelism via threading.
1 parent df7b6c6 commit 8d767ef

File tree

2 files changed

+248
-96
lines changed

2 files changed

+248
-96
lines changed

src/adaptors/tcp_lite/tcp_lite.c

+107-96
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ static uint64_t validate_outbound_message(const qdr_delivery_t *out_dlv);
131131
static void on_accept(qd_adaptor_listener_t *listener, pn_listener_t *pn_listener, void *context);
132132
static void on_tls_connection_secured(qd_tls_t *tls, void *user_context);
133133
static char *get_tls_negotiated_alpn(qd_message_t *msg); // caller must free() returned string!
134-
134+
static int setup_tls_session(tcplite_connection_t *conn, const qd_tls_domain_t *parent_domain, const char *alpn_protocol);
135135

136136
//=================================================================================
137137
// Thread assertions
@@ -484,34 +484,58 @@ static void free_connection_IO(void *context)
484484
free_tcp_resource(&conn->common);
485485
}
486486

487-
488-
static void close_raw_connection_XSIDE_IO(tcplite_connection_t *conn)
487+
// Initate close of the raw connection.
488+
//
489+
// The close will be complete when the PN_RAW_CONNECTION_DISCONNECTED event is handled. At that point any associated
490+
// connection condition information will be read from the raw conn and written to the flow log.
491+
//
492+
// @param conn Holds the raw connection to close
493+
// @param condition Optional condition identifying the reason the connection was closed
494+
// @param description Optional description assocated with condition
495+
//
496+
static void close_raw_connection(tcplite_connection_t *conn, const char *condition, const char *description)
489497
{
490498
ASSERT_RAW_IO;
491-
if (conn->state != XSIDE_CLOSING) {
492-
set_state_XSIDE_IO(conn, XSIDE_CLOSING);
493-
if (!!conn->raw_conn) {
494-
CLEAR_ATOMIC_FLAG(&conn->raw_opened);
495-
pn_raw_connection_close(conn->raw_conn);
496-
drain_read_buffers_XSIDE_IO(conn->raw_conn);
497-
drain_write_buffers_XSIDE_IO(conn->raw_conn);
498-
499-
// note: this disables the raw connection event handler. No further PN_RAW_CONNECTION_* events will occur,
500-
// including DISCONNECTED!
501-
sys_mutex_lock(&conn->activation_lock);
502-
pn_raw_connection_set_context(conn->raw_conn, 0);
503-
conn->raw_conn = 0;
504-
sys_mutex_unlock(&conn->activation_lock);
499+
500+
assert(conn->raw_conn);
501+
if (condition) {
502+
pn_condition_t *cond = pn_raw_connection_condition(conn->raw_conn);
503+
if (!!cond) {
504+
(void) pn_condition_set_name(cond, condition);
505+
if (description) {
506+
(void) pn_condition_set_description(cond, description);
507+
}
505508
}
506509
}
510+
511+
CLEAR_ATOMIC_FLAG(&conn->raw_opened);
512+
pn_raw_connection_close(conn->raw_conn);
513+
514+
// Connection cleanup occurs on the PN_RAW_CONNECTION_DISCONNECTED event
507515
}
508516

509517
// Note: if no_delay is true, conn will be freed by this function
510518
//
511519
static void close_connection_XSIDE_IO(tcplite_connection_t *conn, bool no_delay)
512520
{
513521
ASSERT_RAW_IO;
514-
close_raw_connection_XSIDE_IO(conn);
522+
523+
if (conn->state != XSIDE_CLOSING)
524+
set_state_XSIDE_IO(conn, XSIDE_CLOSING);
525+
526+
if (!!conn->raw_conn) {
527+
CLEAR_ATOMIC_FLAG(&conn->raw_opened);
528+
pn_raw_connection_close(conn->raw_conn);
529+
drain_read_buffers_XSIDE_IO(conn->raw_conn);
530+
drain_write_buffers_XSIDE_IO(conn->raw_conn);
531+
532+
// note: this disables the raw connection event handler. No further PN_RAW_CONNECTION_* events will occur,
533+
// including DISCONNECTED!
534+
sys_mutex_lock(&conn->activation_lock);
535+
pn_raw_connection_set_context(conn->raw_conn, 0);
536+
conn->raw_conn = 0;
537+
sys_mutex_unlock(&conn->activation_lock);
538+
}
515539

516540
free(conn->reply_to);
517541

@@ -1119,55 +1143,16 @@ static uint64_t handle_first_outbound_delivery_CSIDE(tcplite_connector_t *cr, qd
11191143
return dispo;
11201144
}
11211145

1146+
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG, DLV_FMT " CSIDE new outbound delivery", DLV_ARGS(delivery));
1147+
11221148
qd_message_t *msg = qdr_delivery_message(delivery);
11231149
tcplite_connection_t *conn = new_tcplite_connection_t();
11241150
ZERO(conn);
11251151

1126-
conn->conn_id = qd_server_allocate_connection_id(tcplite_context->server);
1127-
1128-
// Catch TLS configuration issues early so we can avoid initializing the entire
1129-
// connection.
1130-
//
1131-
if (cr->tls_domain) {
1132-
bool failed = true;
1133-
// note qd_tls_domain_clone() will log error if it fails
1134-
conn->tls_domain = qd_tls_domain_clone(cr->tls_domain);
1135-
if (conn->tls_domain) {
1136-
int rc = 0;
1137-
assert(!conn->alpn_protocol);
1138-
conn->alpn_protocol = get_tls_negotiated_alpn(msg);
1139-
if (conn->alpn_protocol) {
1140-
const char *alpn_protocols[] = {conn->alpn_protocol};
1141-
rc = qd_tls_set_alpn_protocols(conn->tls_domain, alpn_protocols, 1);
1142-
if (rc != 0) {
1143-
qd_log(LOG_TCP_ADAPTOR, QD_LOG_ERROR,
1144-
"[%" PRIu64 "] tcpConnector %s: failed to configure ALPN protocol '%s' (%d)",
1145-
conn->conn_id, cr->adaptor_config->name, conn->alpn_protocol, rc);
1146-
}
1147-
}
1148-
if (rc == 0) {
1149-
// note: qd_tls() will log error if it fails
1150-
conn->tls = qd_tls(conn->tls_domain, conn, conn->conn_id, on_tls_connection_secured);
1151-
if (conn->tls) {
1152-
failed = false;
1153-
}
1154-
}
1155-
}
1156-
if (failed) {
1157-
qd_tls_domain_decref(conn->tls_domain);
1158-
qd_tls_free2(conn->tls);
1159-
free(conn->alpn_protocol);
1160-
free_tcplite_connection_t(conn);
1161-
qd_message_set_send_complete(msg);
1162-
return PN_RELEASED; // redeliver somewhere else!
1163-
}
1164-
}
1165-
1166-
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG, DLV_FMT " CSIDE new outbound delivery", DLV_ARGS(delivery));
1167-
11681152
qdr_delivery_incref(delivery, "CORE_deliver_outbound CSIDE");
11691153
qdr_delivery_set_context(delivery, conn);
11701154

1155+
conn->conn_id = qd_server_allocate_connection_id(tcplite_context->server);
11711156
conn->common.context_type = TL_CONNECTION;
11721157
conn->common.parent = (tcplite_common_t*) cr;
11731158

@@ -1587,10 +1572,17 @@ static void connection_run_LSIDE_IO(tcplite_connection_t *conn)
15871572
switch (conn->state) {
15881573
case LSIDE_INITIAL:
15891574
if (IS_ATOMIC_FLAG_SET(&conn->raw_opened)) { // raw connection is active
1590-
if (conn->tls) {
1591-
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG, "[C%"PRIu64"] LSIDE_IO performing TLS handshake", conn->conn_id);
1592-
set_state_XSIDE_IO(conn, LSIDE_TLS_HANDSHAKE);
1593-
repeat = true;
1575+
tcplite_listener_t *li = (tcplite_listener_t *) conn->common.parent;
1576+
if (li->tls_domain) {
1577+
if (setup_tls_session(conn, li->tls_domain, 0) != 0) {
1578+
// TLS setup failed: check logs for details
1579+
close_raw_connection(conn, "TLS-connection-failed", "Error loading credentials");
1580+
set_state_XSIDE_IO(conn, XSIDE_CLOSING); // prevent further connection I/O
1581+
} else {
1582+
qd_log(LOG_TCP_ADAPTOR, QD_LOG_DEBUG, "[C%"PRIu64"] LSIDE_IO performing TLS handshake", conn->conn_id);
1583+
set_state_XSIDE_IO(conn, LSIDE_TLS_HANDSHAKE);
1584+
repeat = true;
1585+
}
15941586
} else {
15951587
link_setup_LSIDE_IO(conn);
15961588
set_state_XSIDE_IO(conn, LSIDE_LINK_SETUP);
@@ -1614,6 +1606,7 @@ static void connection_run_LSIDE_IO(tcplite_connection_t *conn)
16141606
// TLS failed! Error logged, raw connection close initiated and error condition set. Clean up in
16151607
// DISCONNECTED raw connection event.
16161608
//
1609+
set_state_XSIDE_IO(conn, XSIDE_CLOSING); // prevent further connection I/O
16171610
} else if (qd_tls_is_secure(conn->tls)) {
16181611
//
16191612
// Handshake completed, begin the setup of the inbound and outbound links for this connection.
@@ -1687,6 +1680,19 @@ static void connection_run_CSIDE_IO(tcplite_connection_t *conn)
16871680
switch (conn->state) {
16881681
case CSIDE_INITIAL:
16891682
if (IS_ATOMIC_FLAG_SET(&conn->raw_opened)) { // raw connection is active
1683+
tcplite_connector_t *cr = (tcplite_connector_t *) conn->common.parent;
1684+
if (cr->tls_domain) {
1685+
assert(conn->outbound_stream);
1686+
char *alpn = get_tls_negotiated_alpn(conn->outbound_stream);
1687+
int rc = setup_tls_session(conn, cr->tls_domain, alpn);
1688+
free(alpn);
1689+
if (rc != 0) {
1690+
// TLS setup failed: check logs for details
1691+
close_raw_connection(conn, "TLS-connection-failed", "Error loading credentials");
1692+
set_state_XSIDE_IO(conn, XSIDE_CLOSING); // prevent further connection I/O
1693+
break;
1694+
}
1695+
}
16901696
link_setup_CSIDE_IO(conn, conn->outbound_delivery);
16911697
set_state_XSIDE_IO(conn, CSIDE_LINK_SETUP);
16921698
}
@@ -1793,8 +1799,12 @@ static void on_tls_connection_secured(qd_tls_t *tls, void *user_context)
17931799
if (conn->core_conn && conn->core_conn->connection_info) {
17941800
qd_tls_update_connection_info(conn->tls, conn->core_conn->connection_info);
17951801
}
1796-
if (!conn->alpn_protocol)
1802+
1803+
// check if we need to propagate client ALPN to server
1804+
if (conn->listener_side) {
1805+
assert(!conn->alpn_protocol);
17971806
qd_tls_get_alpn_protocol(conn->tls, &conn->alpn_protocol);
1807+
}
17981808
}
17991809

18001810
// Check for the ALPN value negotiated on the CSIDE TLS connection (optional).
@@ -1830,6 +1840,37 @@ static char *get_tls_negotiated_alpn(qd_message_t *msg)
18301840
return alpn_protocol;
18311841
}
18321842

1843+
/**
1844+
* Create a new TLS session for the given connection.
1845+
*
1846+
* @param parent_domain Reference to parent connector/listener TLS domain context
1847+
* @param alpn_protocol If CSIDE the optional alpn protocol value to pass to the remote server
1848+
*
1849+
* @return 0 on success, non-zero on error
1850+
*/
1851+
static int setup_tls_session(tcplite_connection_t *conn, const qd_tls_domain_t *parent_domain, const char *alpn_protocol)
1852+
{
1853+
conn->tls_domain = qd_tls_domain_clone(parent_domain);
1854+
if (!conn->tls_domain)
1855+
return -1; // error logged in qd_tls_domain_clone()
1856+
1857+
if (alpn_protocol) {
1858+
const char *alpn_protocols[] = {alpn_protocol};
1859+
int rc = qd_tls_set_alpn_protocols(conn->tls_domain, alpn_protocols, 1);
1860+
if (rc != 0) {
1861+
qd_log(LOG_TCP_ADAPTOR, QD_LOG_ERROR,
1862+
"[%" PRIu64 "] failed to configure ALPN protocol '%s' (%d)", conn->conn_id, alpn_protocol, rc);
1863+
return -1;
1864+
}
1865+
}
1866+
1867+
conn->tls = qd_tls(conn->tls_domain, conn, conn->conn_id, on_tls_connection_secured);
1868+
if (!conn->tls)
1869+
return -1; // error logged in qd_tls()
1870+
1871+
return 0;
1872+
}
1873+
18331874

18341875
//=================================================================================
18351876
// Handlers for events from the Raw Connections
@@ -1915,29 +1956,6 @@ static void on_accept(qd_adaptor_listener_t *listener, pn_listener_t *pn_listene
19151956

19161957
ZERO(conn);
19171958
conn->conn_id = qd_server_allocate_connection_id(tcplite_context->server);
1918-
1919-
// Catch TLS configuration issues early so we can avoid initializing the entire
1920-
// connection.
1921-
//
1922-
if (li->tls_domain) {
1923-
bool failed = true;
1924-
conn->tls_domain = qd_tls_domain_clone(li->tls_domain);
1925-
if (conn->tls_domain) {
1926-
conn->tls = qd_tls(conn->tls_domain, conn, conn->conn_id, on_tls_connection_secured);
1927-
if (conn->tls) {
1928-
failed = false;
1929-
} else {
1930-
qd_tls_domain_decref(conn->tls_domain);
1931-
}
1932-
}
1933-
if (failed) {
1934-
// qd_tls(_domain_clone) will log the appropriate error message
1935-
free_tcplite_connection_t(conn);
1936-
qd_adaptor_listener_deny_conn(listener, pn_listener);
1937-
return;
1938-
}
1939-
}
1940-
19411959
conn->common.context_type = TL_CONNECTION;
19421960
conn->common.parent = (tcplite_common_t*) li;
19431961

@@ -2129,14 +2147,7 @@ static void CORE_delivery_update(void *context, qdr_delivery_t *dlv, uint64_t di
21292147
if (final_outcome && disp != PN_ACCEPTED) {
21302148
// The delivery failed - this is unrecoverable.
21312149
if (!!conn->raw_conn) {
2132-
// set the raw connection condition info so it will appear in the vanflow logs
2133-
// when the connection disconnects
2134-
pn_condition_t *cond = pn_raw_connection_condition(conn->raw_conn);
2135-
if (!!cond) {
2136-
(void) pn_condition_set_name(cond, "delivery-failed");
2137-
(void) pn_condition_set_description(cond, "destination unreachable");
2138-
}
2139-
pn_raw_connection_close(conn->raw_conn);
2150+
close_raw_connection(conn, "delivery-failed", "destination unreachable");
21402151
// clean stuff up when DISCONNECT event arrives
21412152
}
21422153
} else {

0 commit comments

Comments
 (0)