The full code for this sample is available at https://github.com/jcleblanc/programming-social-applications/tree/master/chapter_10/pubsubhubbub-subscriber-python.
Now let’s explore the same subscriber implementation, but now using Python. For this example, the subscriber classes are stored in a file name subscriber.py:
import re import urllib import urllib2 ''' ' Class: Subscription Error ' Description: Custom error class for subscription exceptions ''' class SubscribeError(Exception): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) ''' ' Class: Subscriber ' Description: Provides ability to subscribe / unsubscribe from hub feeds ''' class Subscriber: regex_url = re.compile('^https?://') #simple URL string validator #constructor that stores the hub and callback URLs for the subscriber def __init__(self, hub, callback): if self.regex_url.match(hub): self.hub = hub else: raise SubscribeError('Invalid hub URL supplied') if self.regex_url.match(callback): self.callback = callback else: raise SubscribeError('Invalid callback URL supplied') #initiates a request to subscribe to a feed def subscribe(self, feed): return self.change_subscription('subscribe', feed) #initiates a request to unsubscribe from a feed def unsubscribe(self, feed): return self.change_subscription('unsubscribe', feed) #makes request to hub to subscribe / unsubscribe def change_subscription(self, mode, feed): #check if provided feed is a valid URL if self.regex_url.match(feed): #set the post string for subscribe / unsubscribe post_string = 'hub.mode=%s&hub.callback=%s&hub.verify=async&hub.topic=%s' % (mode, self.callback, urllib.quote(feed)) try: #make return to hub file = urllib2.urlopen(self.hub, post_string) return True except (IOError, urllib2.HTTPError), e: #process http conditions in 2xx range as valid if hasattr(e, 'code') and str(e.code)[0] == '2': return True #process alternative error conditions error = '' if hasattr(e, 'read'): error = e.read() raise SubscribeError('%s, Response: "%s"' % (e, error)) else: raise SubscribeError('Invalid feed URL supplied')
Just like with the Python publisher, the subscriber file has a
custom exception class, SubscribeError
, whose purpose is to output
exceptions that are encountered during the execution of the
subscription.
When a new instance of the Subscriber
class is created, the constructor
will be called. The constructor’s purpose is to accept URLs for the hub
and callback, check that they are valid, and store them if valid or
display exceptions if they’re not.
The subscribe(...)
and unsubscribe(...)
methods are helper methods
that accept a feed URL and then call the change_subscription(...)
method with the
appropriate mode for the action to be taken, either subscribe
or unsubscribe
.
change_subscription(...)
is
used to make the request to the hub to subscribe to a feed. We start by
checking if the feed URL provided is valid. If so, we start constructing
the POST string that will be sent to the hub. It will consist of:
hub.mode
The subscription mode to run, either subscribe
or unsubscribe
hub.callback
The callback URL on the subscriber site, to which new feed updates from the publisher should be POSTed
hub.verify
The verify mode, either sync
or async
hub.topic
The feed URL
We then issue a POST request to the hub URL, passing in the POST
string. If the request completes successfully, we return True
. If an error is thrown, we handle it by
first checking the HTTP response code. If the code is in the
2xx range, we treat it as a valid response and
return True
. A valid response should
be an HTTP 202 code, but anything in the 2xx range
is a success. If the code isn’t in that range, we throw an exception
with the appropriate error response.
Now let’s see how we can use these classes to build a subscriber:
from subscriber import * #define hub, callback and feed hub = 'http://pubsubhubbub.appspot.com/' callback = 'http://www.example.com/publish' feed = 'http://www.example.com' #create new subscriber subscriber = Subscriber(hub, callback) #subscribe / unsubscribe methods: response == True on success response = subscriber.subscribe(feed) #response = subscriber.unsubscribe(feed) #print message on success if (response == True): print 'Content-Type: text/plain' print '' print 'Request successful'
We start by importing the subscriber file that we created earlier
and then define the URLs for our hub, callback, and the feed that we
will be performing the action on. Next, we create a new Subscriber
object, passing in the hub and
callback. Using that new object, we call either the subscribe(...)
or unsubscribe(...)
methods, passing in the feed
URL. Last, we check the response from the called method and, if True
, display a message stating that the
request was successful.
18.119.136.84