def update_pubsub_state(feed, context): """ perform any (un/re)subscription needed based on the state of the feed given and currently listed hubs. """ hubs = feed.find_hub_urls() ps = feed.hub_info # if pubsub is disabled for this feed if not ps.enabled: if ps.subscribed: try: hubbub_unsub(feed, context) except: log.warn("Error unsubscribing from hub %s for feed %s: %s" % (ps.hub_url, feed.url, traceback.format_exc())) return # if the currently subscribed hub is no longer # listed in the feed, unsubscribe from it. if ps.subscribed and ps.hub_url not in hubs: try: hubbub_unsub(feed, context) except: log.warn("Error unsubscribing from current hub: %s" % traceback.format_exc()) feed = RemoteFeed.get(feed.id, context) # refresh # if it is time to resubscribe to the current hub, try to # resubscribe elif ps.subscribed and datetime.utcnow() > ps.next_sub_time: log.info('resubscribe %s to hub %s' % (feed.url, feed.hub_info.hub_url)) if not _sub_any(feed, [feed.hub_info.hub_url], context): log.warn("Failed to resubscribe to %s for feed %s." % (ps.hub_url, feed.url)) try: hubbub_unsub(feed, context) except: log.warn("Error unsubscribing from hub %s for feed %s: %s" % (ps.hub_url, feed.url, traceback.format_exc())) feed = RemoteFeed.get(feed.id, context) # refresh # if it is not subscribed, subscribe to first thing # that works. if not ps.subscribed: _sub_any(feed, hubs, context)
def test_sub_push(ctx): from httplib2 import Http from eventlet import sleep, spawn from melk.util.nonce import nonce_str from melkman.db import RemoteFeed from melkman.fetch.worker import run_feed_indexer from melkman.fetch.pubsubhubbub import WSGISubClient, callback_url_for, psh_digest import logging logging.basicConfig(level=logging.WARN) w = WSGISubClient(ctx) client = spawn(w.run) indexer = spawn(run_feed_indexer, ctx) http = Http() url = 'http://example.org/feed/0' content = random_atom_feed(url, 10) secret = nonce_str() digest = 'sha1=%s' % psh_digest(content, secret) cb = callback_url_for(url, ctx) assert RemoteFeed.get_by_url(url, ctx) == None # try posting something that is not subscribed r, c = http.request(cb, 'POST', body=content, headers={'X-Hub-Signature': digest}) assert r.status == 200, 'Expected 200, got %d' % r.status sleep(1) # nothing should happen... assert RemoteFeed.get(url, ctx) == None # set up the feed, but don't subscribe rf = RemoteFeed.create_from_url(url, ctx) rf.save() r, c = http.request(cb, 'POST', body=content, headers={'X-Hub-Signature': digest}) assert r.status == 200, 'Expected 200, got %d' % r.status sleep(1) # nothing should happen... rf = RemoteFeed.get_by_url(url, ctx) assert len(rf.entries) == 0 # now set it up rf.hub_info.enabled = True rf.hub_info.subscribed = True rf.hub_info.secret = secret rf.save() # try with wrong digest... r, c = http.request(cb, 'POST', body=content, headers={'X-Hub-Signature': 'wrong'}) assert r.status == 200, 'Expected 200, got %d' % r.status sleep(0.5) # nothing should happen... rf = RemoteFeed.get_by_url(url, ctx) assert len(rf.entries) == 0 # try with no digest r, c = http.request(cb, 'POST', body=content) assert r.status == 200, 'Expected 200, got %d' % r.status sleep(0.5) # nothing should happen... rf = RemoteFeed.get_by_url(url, ctx) assert len(rf.entries) == 0 # finally, try with correct digest r, c = http.request(cb, 'POST', body=content, headers={'X-Hub-Signature': digest}) assert r.status == 200, 'Expected 200, got %d' % r.status sleep(0.5) # nothing should happen... rf = RemoteFeed.get_by_url(url, ctx) assert len(rf.entries) == 10 for iid in melk_ids_in(content, url): assert iid in rf.entries client.kill() client.wait() indexer.kill() indexer.wait()