mirror of
https://github.com/discourse/discourse.git
synced 2024-12-02 04:33:40 +08:00
f1a43f2319
We're seeing unhandled errors in production when web push notifications are failing with an SSL error. This is happening for a few users, but generating a large amount of log noise due to the sheer number of notifications. This adds handling of SSL errors in two places: 1. In FinalDestination::HTTP, this is handled the same as a timeout error, and gives a chance to recover. 2. In PushNotificationPusher. This will cause the notification to retry a number of times, and if it keeps failing, disable push notifications for the user. (Existing behaviour.) I wanted to wrap the SSL error in e.g. WebPush::RequestError, but the gem doesn't have request error handling, so didn't want to have the freedom patch diverge from the gem as well. Instead just propagating the raw SSL error.
41 lines
1.2 KiB
Ruby
41 lines
1.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class FinalDestination::HTTP < Net::HTTP
|
|
def connect
|
|
original_open_timeout = @open_timeout
|
|
return super if @ipaddr
|
|
|
|
timeout_at = current_time + @open_timeout
|
|
|
|
# This iteration through addresses would normally happen in Socket#tcp
|
|
# We do it here because we're tightly controlling addresses rather than
|
|
# handing Socket#tcp a hostname
|
|
ips = FinalDestination::SSRFDetector.lookup_and_filter_ips(@address, timeout: @connect_timeout)
|
|
|
|
ips.each_with_index do |ip, index|
|
|
debug "[FinalDestination] Attempting connection to #{ip}..."
|
|
self.ipaddr = ip
|
|
|
|
remaining_time = timeout_at - current_time
|
|
if remaining_time <= 0
|
|
raise Net::OpenTimeout.new("Operation timed out - FinalDestination::HTTP")
|
|
end
|
|
|
|
@open_timeout = remaining_time
|
|
return super
|
|
rescue OpenSSL::SSL::SSLError, SystemCallError, Net::OpenTimeout => e
|
|
debug "[FinalDestination] Error connecting to #{ip}... #{e.message}"
|
|
was_last_attempt = index == ips.length - 1
|
|
raise if was_last_attempt
|
|
end
|
|
ensure
|
|
@open_timeout = original_open_timeout
|
|
end
|
|
|
|
private
|
|
|
|
def current_time
|
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
end
|
|
end
|