Skip to content

Commit

Permalink
RequestServer: Implement preconnect for CreateConnection cache level
Browse files Browse the repository at this point in the history
This uses the share mechanism in curl to cache connection and DNS
information.

The multi handle already does some caching, but in order to use
that, we would also need to construct an `ActiveRequest` here. That
isn't necessary, and we gain some explicitness+flexibility this way :^)
  • Loading branch information
rmg-x committed Nov 2, 2024
1 parent bd41902 commit cef3009
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 3 deletions.
43 changes: 40 additions & 3 deletions Userland/Services/RequestServer/ConnectionFromClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
namespace RequestServer {

ByteString g_default_certificate_path;

static HashMap<int, RefPtr<ConnectionFromClient>> s_connections;
static IDAllocator s_client_ids;
static long s_connect_timeout_seconds = 90L;

struct ConnectionFromClient::ActiveRequest {
CURLM* multi { nullptr };
Expand Down Expand Up @@ -199,6 +201,10 @@ ConnectionFromClient::ConnectionFromClient(IPC::Transport transport)
s_connections.set(client_id(), *this);

m_curl_multi = curl_multi_init();
m_curl_share = curl_share_init();

curl_share_setopt(m_curl_share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
curl_share_setopt(m_curl_share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);

auto set_option = [this](auto option, auto value) {
auto result = curl_multi_setopt(m_curl_multi, option, value);
Expand Down Expand Up @@ -305,7 +311,8 @@ void ConnectionFromClient::start_request(i32 request_id, ByteString const& metho
set_option(CURLOPT_ACCEPT_ENCODING, "gzip, deflate, br");
set_option(CURLOPT_URL, url.to_string().value().to_byte_string().characters());
set_option(CURLOPT_PORT, url.port_or_default());
set_option(CURLOPT_CONNECTTIMEOUT, 90L);
set_option(CURLOPT_CONNECTTIMEOUT, s_connect_timeout_seconds);
set_option(CURLOPT_SHARE, m_curl_share);

if (method == "GET"sv) {
set_option(CURLOPT_HTTPGET, 1L);
Expand Down Expand Up @@ -440,8 +447,38 @@ void ConnectionFromClient::ensure_connection(URL::URL const& url, ::RequestServe
return;
}

(void)cache_level;
dbgln("FIXME: EnsureConnection: Pre-connect to {}", url);
auto const url_string_value = url.to_string().value();

if (cache_level == CacheLevel::CreateConnection) {
auto* easy = curl_easy_init();
if (!easy) {
dbgln("EnsureConnection: Failed to initialize curl easy handle");
return;
}

auto set_option = [easy](auto option, auto value) {
auto result = curl_easy_setopt(easy, option, value);
if (result != CURLE_OK) {
dbgln("EnsureConnection: Failed to set curl option: {}", curl_easy_strerror(result));
return false;
}
return true;
};

set_option(CURLOPT_URL, url_string_value.to_byte_string().characters());
set_option(CURLOPT_PORT, url.port_or_default());
set_option(CURLOPT_CONNECTTIMEOUT, s_connect_timeout_seconds);
set_option(CURLOPT_CONNECT_ONLY, 1L);
set_option(CURLOPT_SHARE, m_curl_share);

curl_easy_perform(easy);
curl_easy_cleanup(easy);
return;
}

if (cache_level == CacheLevel::ResolveOnly) {
dbgln("FIXME: EnsureConnection: Implement ResolveOnly cache level");
}
}

void ConnectionFromClient::websocket_connect(i64 websocket_id, URL::URL const& url, ByteString const& origin, Vector<ByteString> const& protocols, Vector<ByteString> const& extensions, HTTP::HeaderMap const& additional_request_headers)
Expand Down
6 changes: 6 additions & 0 deletions Userland/Services/RequestServer/ConnectionFromClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ class ConnectionFromClient final

void check_active_requests();
void* m_curl_multi { nullptr };

// NOTE: m_curl_multi already caches some information about DNS and connections,
// but using a share is more explicit and allows us flexibility when making "connect only" requests.
// See https://everything.curl.dev/helpers/sharing.html and https://everything.curl.dev/internals/caches.html
void* m_curl_share { nullptr };

RefPtr<Core::Timer> m_timer;
HashMap<int, NonnullRefPtr<Core::Notifier>> m_read_notifiers;
HashMap<int, NonnullRefPtr<Core::Notifier>> m_write_notifiers;
Expand Down

0 comments on commit cef3009

Please sign in to comment.