From abaaeb7f4a4e67a259cf4b0c1b1bf1950cb9940d Mon Sep 17 00:00:00 2001 From: syeopite Date: Tue, 13 May 2025 06:16:25 -0700 Subject: [PATCH] Use single global SSL context across HTTP Clients The standard Crystal `HTTP::Client` creates a new SSL context for every client/reconnection. This is not ideal because creating a new SSL context is an incredibly slow operation. The standard practice instead is to cache or use a single SSL CTX throughout the lifetime of the application. The OpenSSL context has been designed with this in mind, and even explicitly recommends it. It even works concurrently across many threads. Reusing a single SSL context significantly speeds up the process of starting and restarting clients. Which is incredibly important in the case of the Invidious connection pool where new clients are often spun up. The only caveat is that a cached SSL context may become out of sync with the system ca store. But for an Invidious instance following the best practices of frequent restarts this is an non-issue. See https://github.com/crystal-lang/crystal/issues/15419 --- src/invidious/connection/client.cr | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/invidious/connection/client.cr b/src/invidious/connection/client.cr index 1a49b09b..3411ad32 100644 --- a/src/invidious/connection/client.cr +++ b/src/invidious/connection/client.cr @@ -1,3 +1,10 @@ +# SSL Contexts are designed to be reused and supports infinite connections. +# +# This *significantly* improves the performance of spinning up new clients/reconnections +# +# For more information see https://github.com/crystal-lang/crystal/issues/15419 +private GLOBAL_SSL_CONTEXT = OpenSSL::SSL::Context::Client.new + module Invidious class IVTCPSocket < TCPSocket def initialize(host : String, port, dns_timeout = nil, connect_timeout = nil, blocking = false, family = Socket::Family::UNSPEC) @@ -89,13 +96,19 @@ def make_client( use_http_proxy : Bool = true, allow_auto_reconnect : Bool = true, ) + tls = if url.scheme == "https" + GLOBAL_SSL_CONTEXT + else + nil + end + if CONFIG.http_proxy && use_http_proxy - client = Invidious::HTTPClient.new(url) + client = Invidious::HTTPClient.new(url, tls: tls) client.proxy = make_configured_http_proxy_client() if CONFIG.http_proxy && use_http_proxy elsif force_resolve - client = Invidious::HTTPClient.new(url, force_resolve: CONFIG.force_resolve) + client = Invidious::HTTPClient.new(url, tls: tls, force_resolve: CONFIG.force_resolve) else - client = Invidious::HTTPClient.new(url, allow_auto_reconnect: allow_auto_reconnect) + client = Invidious::HTTPClient.new(url, tls: tls, allow_auto_reconnect: allow_auto_reconnect) end client.before_request { |r| add_yt_headers(r) } if url.host.try &.ends_with?("youtube.com") || force_youtube_headers