about summary refs log tree commit diff
path: root/app/workers/pubsubhubbub
diff options
context:
space:
mode:
authorMatt Jankowski <mjankowski@thoughtbot.com>2017-05-12 14:35:36 -0400
committerEugen Rochko <eugen@zeonfederated.com>2017-05-12 20:35:36 +0200
commit0d70fe26590433c06948a2827ea582b53fdfd6a1 (patch)
tree70bb50ecaf17ad3f783ca962610462a802bd8f35 /app/workers/pubsubhubbub
parenta1fc2cfa0990845db70294277e84d4347fe50642 (diff)
Spec coverage and refactor for pubsub/delivery worker (#3021)
* Framework for delivery worker spec

* Refactor of pubsub delivery worker
Diffstat (limited to 'app/workers/pubsubhubbub')
-rw-r--r--app/workers/pubsubhubbub/delivery_worker.rb87
1 files changed, 65 insertions, 22 deletions
diff --git a/app/workers/pubsubhubbub/delivery_worker.rb b/app/workers/pubsubhubbub/delivery_worker.rb
index 0e57a74b6..708a5fb9f 100644
--- a/app/workers/pubsubhubbub/delivery_worker.rb
+++ b/app/workers/pubsubhubbub/delivery_worker.rb
@@ -10,40 +10,83 @@ class Pubsubhubbub::DeliveryWorker
     5 * (count + 1)
   end
 
+  attr_reader :subscription, :payload
+
   def perform(subscription_id, payload)
-    subscription = Subscription.find(subscription_id)
-    headers      = {}
-    host         = Addressable::URI.parse(subscription.callback_url).normalize.host
+    @subscription = Subscription.find(subscription_id)
+    @payload = payload
+    process_delivery unless blocked_domain?
+  end
 
-    return if DomainBlock.blocked?(host)
+  private
 
-    headers['User-Agent']      = 'Mastodon/PubSubHubbub'
-    headers['Content-Type']    = 'application/atom+xml'
-    headers['Link']            = LinkHeader.new([[api_push_url, [%w(rel hub)]], [account_url(subscription.account, format: :atom), [%w(rel self)]]]).to_s
-    headers['X-Hub-Signature'] = signature(subscription.secret, payload) if subscription.secret?
+  def process_delivery
+    payload_delivery
 
-    response = HTTP.timeout(:per_operation, write: 50, connect: 20, read: 50)
-                   .headers(headers)
-                   .post(subscription.callback_url, body: payload)
+    if response_successful?
+      subscription.touch(:last_successful_delivery_at)
+    elsif response_failed_permanently?
+      subscription.destroy!
+    else
+      raise "Delivery failed for #{subscription.callback_url}: HTTP #{payload_delivery.code}"
+    end
+  end
 
-    return subscription.destroy! if response_failed_permanently?(response) # HTTP 4xx means error is not temporary, except for 429 (throttling)
-    raise "Delivery failed for #{subscription.callback_url}: HTTP #{response.code}" unless response_successful?(response)
+  def payload_delivery
+    @_payload_delivery ||= callback_post_payload
+  end
 
-    subscription.touch(:last_successful_delivery_at)
+  def callback_post_payload
+    HTTP.timeout(:per_operation, write: 50, connect: 20, read: 50)
+        .headers(headers)
+        .post(subscription.callback_url, body: payload)
   end
 
-  private
+  def blocked_domain?
+    DomainBlock.blocked?(host)
+  end
+
+  def host
+    Addressable::URI.parse(subscription.callback_url).normalize.host
+  end
+
+  def headers
+    {
+      'User-Agent' => 'Mastodon/PubSubHubbub',
+      'Content-Type' => 'application/atom+xml',
+      'Link' => link_headers,
+    }.merge(signature_headers.to_h)
+  end
+
+  def link_headers
+    LinkHeader.new([hub_link_header, self_link_header]).to_s
+  end
+
+  def hub_link_header
+    [api_push_url, [%w(rel hub)]]
+  end
+
+  def self_link_header
+    [account_url(subscription.account, format: :atom), [%w(rel self)]]
+  end
+
+  def signature_headers
+    { 'X-Hub-Signature' => payload_signature } if subscription.secret?
+  end
+
+  def payload_signature
+    "sha1=#{hmac_payload_digest}"
+  end
 
-  def signature(secret, payload)
-    hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), secret, payload)
-    "sha1=#{hmac}"
+  def hmac_payload_digest
+    OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), subscription.secret, payload)
   end
 
-  def response_failed_permanently?(response)
-    response.code > 299 && response.code < 500 && response.code != 429
+  def response_failed_permanently?
+    payload_delivery.code > 299 && payload_delivery.code < 500 && payload_delivery.code != 429
   end
 
-  def response_successful?(response)
-    response.code > 199 && response.code < 300
+  def response_successful?
+    payload_delivery.code > 199 && payload_delivery.code < 300
   end
 end