Skip to content
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

Reuse a global default OpenSSL::SSL::Context::Client in HTTP::Client #15420

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion src/http/client.cr
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ class HTTP::Client
{% else %}
@tls = case tls
when true
OpenSSL::SSL::Context::Client.new
HTTP::Client.default_ssl_context
when OpenSSL::SSL::Context::Client
tls
when false, nil
Expand All @@ -158,6 +158,40 @@ class HTTP::Client
@reconnect = false
end

{% unless flag?(:without_openssl) %}
@@default_ssl_context : OpenSSL::SSL::Context::Client? = nil
@@default_ssl_context_initialization_mutex = Mutex.new

# Returns a shared, global, default OpenSSL::SSL::Context::Client.
#
# This can be slow because it loads all system root certificates,
# so we do it lazily, and once.
#
# The returned instance must not be modified. For customization,
# first instantiate your own with OpenSSL::SSL::Context::Client.new,
# and pass it to HTTP::Client.new(..., tls: custom_context)
#
# "A single SSL_CTX object can be used to create many connections [...]
# Note that you should not normally make changes to an SSL_CTX after the first
# SSL object has been created from it."
# - https://docs.openssl.org/3.4/man7/ossl-guide-libssl-introduction/
protected def self.default_ssl_context
{% if LibSSL.has_method?(:ssl_get0_param) %}
if @@default_ssl_context.nil?
@@default_ssl_context_initialization_mutex.synchronize do
if @@default_ssl_context.nil? # double-check in case we got mutex after being blocked
@@default_ssl_context = OpenSSL::SSL::Context::Client.new
end
end
end
@@default_ssl_context.not_nil!
{% else %}
# OpenSSL <= 1.0.1 needs set_cert_verify_callback, which mutates SSL_CTX, so don't cache a global Context object.
OpenSSL::SSL::Context::Client.new
{% end %}
end
{% end %}

private def check_host_only(string : String)
# When parsing a URI with just a host
# we end up with a URI with just a path
Expand Down