#!/usr/bin/env python """ Copyright (C) 2004 - Josh Sled Placed into the public domain . Implements a generic /queue/ service in a RESTful manner. See and for the motivation. `./queue.py` to run; will bind to Port 8000. Access via http, of course. """ from twisted.web import server, resource from twisted.internet import reactor class Container(resource.Resource): """Simple Twisted Web container""" isLeaf = False def __init__(self): resource.Resource.__init__(self) self.register_services() def register_services(self): """Register /queue handler.""" self.putChild( 'queue', QueueHandler() ) def getChild( self, path, req ): if path == '': return self else: return resource.Resource.getChild( self, path, req ) def render_GET( self, request ): return """ /queue example

/queue

An example REST-ful /queue web service.

Copyright © 2004 jsled; placed the in public domain.


method path params effect
GET /queue returns an overview of the queues.
POST /queue name:String create the given resource. duh.
GET /queue/{name} Gets the queue with the given name, including a summary of it's contents.
POST /queue/{name} key:String
item:data
Creates /queue/{name}/{key} with content as per {item}
DELETE /queue/{name}/{key} deletes the indicated resource; returns the content one last time...
GET /queue/{name}/{key} retrieves the indicated resource ... duh.
DELETE /queue/{name} Deletes the queue.
""" class QueueHandler(resource.Resource): """ @TODO: * GET /queue/config * PUT /queue/config * HTTP auth... """ isLeaf = False def __init__(self): resource.Resource.__init__(self) self.queues = {} def getChild( self, path, request ): if path == '': return self elif self.queues.has_key(path): return self.queues[path] else: return NotFound(path) def render_GET( self, request ): """Handle GET /queue, resulting in link to form...""" queueName = None # else: ... request.setHeader( "Content-Type", "text/html" ) toRet = """ Create a Queue!

create a queue:



""" if len(self.queues) > 0: toRet += "
    " sortedNames = self.queues.keys() sortedNames.sort() for queueName in sortedNames: toRet += """
  • <queue about="/queue/%(name)s" size="%(queueSize)d" />
  • \n""" % { 'name': queueName, 'queueSize': self.queues[queueName].size() } toRet += "
" toRet += """ """ return toRet def render_POST( self, request ): name = request.args['name'][0] if self.queues.has_key(name): request.setResponseCode( 409, "Conflicts - /queue/%(name)s already exists." % { 'name': name } ) return """obj conflict; /queue/%(name)s already exists.""" % { 'name': name } # else: fallthrough... self.queues[name] = NamedQueue(self, name) loc = "/queue/%s" % ( name ) request.setResponseCode( 201 ) # Created request.setHeader( "Location", loc ) return loc class NamedQueue(resource.Resource): """A queue with an identity.""" isLeaf = False def __init__(self, parent, name): resource.Resource.__init__(self) self.name = name self.parent = parent self.items = {} def getChild(self,path,req): if path == '': return self elif self.items.has_key(path): return QueueItem( self, path ) else: return NotFound( request.uri ) def size(self): """ @returnType int @return size of the queue""" return len(self.items) def render_GET( self, request ): """GET /queue/foo/0 => item content.""" items = [] keys = self.items.keys() keys.sort() for key in keys: items.append( """ <elt about="/queue/%(name)s/%(key)s" />
\n""" % { 'name': self.name, 'key': key } ) toRet = """/queue/%(name)s



<queue about="/queue/%(name)s">
%(items)s </queue> """ % { 'name': self.name, 'items': "\n".join(items) } return toRet def render_POST( self, request ): """POST an item into the queue.""" key = request.args['key'][0] if self.items.has_key( key ): msg = "object conflict: /queue/%(name)s/%(key)s already exists" % { 'name':self.name, 'key':key } request.setResponseCode( 409, msg ) return msg content = request.args['item'][0] self.items[key] = content newUri = "/queue/%(name)s/%(key)s" % { 'name': self.name, 'key': key } request.setResponseCode( 201, "Created : %s" % (newUri) ) request.setHeader( "Location", newUri ) return newUri def render_DELETE( self, request ): del( self.parent.queues[self.name] ) request.setResponseCode( 204 ) # no-content return "" # satisfy twisted :/ class QueueItem (resource.Resource): """An item within a queue.""" isLeaf = True def __init__(self, named_queue, key): resource.Resource.__init__(self) self.queue = named_queue self.key = key def render_GET( self, request ): #request.setHeader( "Content-Type", "application/octet-stream" ) return self.queue.items[self.key] def render_DELETE( self, request ): """This is the atomic read-and-delete operation.""" content = self.queue.items[self.key] del(self.queue.items[self.key]) request.setResponseCode( 200, "BALEETED!" ) return content def render_PUT(self,request): """@@fixme""" request.setResponse( 505, "not implemented" ) return "not implmented" class NotFound (resource.Resource): """A generic 404 response.""" isLeaf = True def __init__(self, path): resource.Resource.__init__(self) self.path = path def render_GET(self,request): request.setResponseCode( 404, "not found" ) return "not found: %s" % ( self.path ) def runHttpd(): site = server.Site(Container()) print "binding on port 8000" reactor.listenTCP( 8000, site ) reactor.run() if __name__ == "__main__": runHttpd();