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
This commit is contained in:
syeopite 2025-05-13 06:16:25 -07:00
parent 0270917756
commit abaaeb7f4a
No known key found for this signature in database
GPG Key ID: A73C186DA3955A1A

View File

@ -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