Change Web Push API deliveries to use request pooling (#16014)
This commit is contained in:
		@@ -3,22 +3,67 @@
 | 
			
		||||
class Web::PushNotificationWorker
 | 
			
		||||
  include Sidekiq::Worker
 | 
			
		||||
 | 
			
		||||
  sidekiq_options backtrace: true, retry: 5
 | 
			
		||||
  sidekiq_options queue: 'push', retry: 5
 | 
			
		||||
 | 
			
		||||
  TTL     = 48.hours.to_s
 | 
			
		||||
  URGENCY = 'normal'
 | 
			
		||||
 | 
			
		||||
  def perform(subscription_id, notification_id)
 | 
			
		||||
    subscription = ::Web::PushSubscription.find(subscription_id)
 | 
			
		||||
    notification = Notification.find(notification_id)
 | 
			
		||||
    @subscription = Web::PushSubscription.find(subscription_id)
 | 
			
		||||
    @notification = Notification.find(notification_id)
 | 
			
		||||
 | 
			
		||||
    subscription.push(notification) unless notification.activity.nil?
 | 
			
		||||
  rescue Webpush::ResponseError => e
 | 
			
		||||
    code = e.response.code.to_i
 | 
			
		||||
    # Polymorphically associated activity could have been deleted
 | 
			
		||||
    # in the meantime, so we have to double-check before proceeding
 | 
			
		||||
    return unless @notification.activity.present? && @subscription.pushable?(@notification)
 | 
			
		||||
 | 
			
		||||
    if (400..499).cover?(code) && ![408, 429].include?(code)
 | 
			
		||||
      subscription.destroy!
 | 
			
		||||
    else
 | 
			
		||||
      raise e
 | 
			
		||||
    payload = @subscription.encrypt(push_notification_json)
 | 
			
		||||
 | 
			
		||||
    request_pool.with(@subscription.audience) do |http_client|
 | 
			
		||||
      request = Request.new(:post, @subscription.endpoint, body: payload.fetch(:ciphertext), http_client: http_client)
 | 
			
		||||
 | 
			
		||||
      request.add_headers(
 | 
			
		||||
        'Content-Type'     => 'application/octet-stream',
 | 
			
		||||
        'Ttl'              => TTL,
 | 
			
		||||
        'Urgency'          => URGENCY,
 | 
			
		||||
        'Content-Encoding' => 'aesgcm',
 | 
			
		||||
        'Encryption'       => "salt=#{Webpush.encode64(payload.fetch(:salt)).delete('=')}",
 | 
			
		||||
        'Crypto-Key'       => "dh=#{Webpush.encode64(payload.fetch(:server_public_key)).delete('=')};#{@subscription.crypto_key_header}",
 | 
			
		||||
        'Authorization'    => @subscription.authorization_header
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      request.perform do |response|
 | 
			
		||||
        # If the server responds with an error in the 4xx range
 | 
			
		||||
        # that isn't about rate-limiting or timeouts, we can
 | 
			
		||||
        # assume that the subscription is invalid or expired
 | 
			
		||||
        # and must be removed
 | 
			
		||||
 | 
			
		||||
        if (400..499).cover?(response.code) && ![408, 429].include?(response.code)
 | 
			
		||||
          @subscription.destroy!
 | 
			
		||||
        elsif !(200...300).cover?(response.code)
 | 
			
		||||
          raise Mastodon::UnexpectedResponseError, response
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  rescue ActiveRecord::RecordNotFound
 | 
			
		||||
    true
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def push_notification_json
 | 
			
		||||
    json = I18n.with_locale(@subscription.locale || I18n.default_locale) do
 | 
			
		||||
      ActiveModelSerializers::SerializableResource.new(
 | 
			
		||||
        @notification,
 | 
			
		||||
        serializer: Web::NotificationSerializer,
 | 
			
		||||
        scope: @subscription,
 | 
			
		||||
        scope_name: :current_push_subscription
 | 
			
		||||
      ).as_json
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    Oj.dump(json)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def request_pool
 | 
			
		||||
    RequestPool.current
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user