Skip to content

Share connection between curl requests #170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

rvdgracht
Copy link

Using a curl_share between curl requests allows sharing an open connection and re-use cached DNS, PSL and TLS session id.

This change allows performing multiple requests without having to do re-perform the (full) TLS handshake.

For reference, on a stm32mp151c with OPTEE + pkcs11 TA a full TLS handshake takes ~8 seconds. Mostly due to small pager pool (internal sram) available for OPTEE.

With this change a mTLS curl request take around 60ms after the initial connection has been established.

@rvdgracht
Copy link
Author

I have some concerns about sharing the connection (CURL_LOCK_DATA_CONNECT) between the mainloop (poller) and the download thread. The documentation states: "It is not supported to share connections between multiple concurrent threads." I did place locking on the shared data including CURL_LOCK_DATA_CONNECT though.
I have succesfully tested this. So the mainloop can poll for status while the download thread is streaming data over the
same connection. Maybe someone with more extensive libcurl knowledge can verify this.

@Bastian-Krause
Copy link
Member

I think inter-thread connection sharing as it's implemented here should be okay. Maybe we should add -fsanitize=thread here, so we stumble upon any issues:

- CFLAGS="-fsanitize=address -fsanitize=leak -g" LDFLAGS="-fsanitize=address -fsanitize=leak"

@Bastian-Krause Bastian-Krause added the enhancement New feature or request label Feb 15, 2024
@rvdgracht
Copy link
Author

Maybe we should add -fsanitize=thread here

Added.

@Bastian-Krause
Copy link
Member

Maybe we should add -fsanitize=thread here

Added.

TSAN works by inserting instrumentation into compiled code to detect race conditions. We use glib from Debian in CI, meaning those libraries are not built with TSAN instrumentation, so memory accesses cannot be tracked. This leads to false positives. So suggesting to enable the sanitizer was not a good idea after all.

I'll drop the commit.

Bastian-Krause
Bastian-Krause previously approved these changes Dec 16, 2024
@Bastian-Krause Bastian-Krause dismissed their stale review December 18, 2024 10:58

Additional changes required.

@Bastian-Krause
Copy link
Member

I've changed the PR slightly:

  • use GMutexes instead of pthread_mutex_t, add mutex init/clear
  • moved curl_share_setopt() calls to set_default_curl_opts() guarded with g_once_init_enter()/g_once_init_leave()
  • add g_return_if_fail() for return value of curl_share_init()
  • added asserts in curl lock/unlock functions

Using a curl_share between curl requests allows sharing an open connection
and re-use cached DNS, PSL and TLS session id.

This change allows performing multiple requests without having to do
re-perform the (full) TLS handshake.

For reference, on a stm32mp151c with OPTEE + pkcs11 TA a full TLS
handshake takes ~8 seconds. Mostly due to small pager pool (internal sram)
available for OPTEE.

With this change a mTLS curl request take around 60ms after the initial
connection has been established.

Signed-off-by: Robin van der Gracht <[email protected]>
Signed-off-by: Bastian Krause <[email protected]>
Copy link
Member

@ejoerns ejoerns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me overall. Just a few notes left about possible enhancements I see.

g_mutex_init(&curl_share_locks[i]);

curl_share = curl_share_init();
g_return_if_fail(curl_share);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use g_return_*() methods for input verification only. Thus better either ignore it (since all potential errors for curl_share_setopt() are ignored so far anyway) or use g_assert_nonnull().

@@ -256,6 +274,24 @@ static void set_default_curl_opts(CURL *curl)
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, hawkbit_config->connect_timeout);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, hawkbit_config->ssl_verify ? 1L : 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, hawkbit_config->ssl_verify ? 1L : 0L);

if (g_once_init_enter(&curl_share_initialized)) {
for (gint i = 0; i <= CURL_LOCK_DATA_LAST; i++)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this deserves a short comment explaining that curl shopt requires a different lock for each data type (that is initialized here)?

@@ -85,6 +89,20 @@ GQuark rhu_hawkbit_client_http_error_quark(void)
return g_quark_from_static_string("rhu_hawkbit_client_http_error_quark");
}

void curl_share_lock(CURL *handle, curl_lock_data data, curl_lock_access access, void *clientp)
{
g_assert_cmpint(data, <=, CURL_LOCK_DATA_LAST);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, g_return_* might fit better as it's an input parameter validation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants