Added a querying API.

This commit is contained in:
Nick Daly 2012-05-23 20:53:57 -05:00
parent 36f8fff888
commit 5a6f25f300
2 changed files with 100 additions and 9 deletions

View File

@ -1,4 +1,19 @@
"""The HTTPS Santiago listener and sender."""
"""The HTTPS Santiago listener and sender.
FIXME: use a reasonable RESTful API.
- https://appmecha.wordpress.com/2008/10/27/cherrypy-gae-routing-2/
- http://tools.cherrypy.org/wiki/RestfulDispatch
- http://docs.cherrypy.org/dev/refman/_cpdispatch.html
- http://www.infoq.com/articles/rest-introduction
- http://www.infoq.com/articles/rest-anti-patterns
- http://stackoverflow.com/a/920181
- http://docs.cherrypy.org/dev/progguide/REST.html
It's been about five times too long since I've looked at this sort of
thing. Stupid everything-is-GET antipattern.
"""
from santiago import SantiagoListener, SantiagoSender
@ -7,6 +22,14 @@ import httplib, urllib, urlparse
import sys
import logging
def jsonify_tool_callback(*args, **kwargs):
response = cherrypy.response
response.headers['Content-Type'] = 'application/json'
response.body = encoder.iterencode(response.body)
if cherrypy.__version__ < "3.2":
cherrypy.tools.json_out = cherrypy.Tool('before_finalize', jsonify_tool_callback, priority=30)
class Listener(SantiagoListener):
def __init__(self, santiago, socket_port=0,
@ -38,18 +61,53 @@ class Listener(SantiagoListener):
logging.exception(e)
@cherrypy.expose
def query(self, host, service):
def learn(self, host, service):
"""Request a resource from another Santiago client.
TODO: add request whitelisting.
"""
if not cherrypy.request.remote.ip.startswith("127.0.0"):
# TODO enforce restfulness, POST, and build a request form.
# if not cherrypy.request.method == "POST":
# return
if not cherrypy.request.remote.ip.startswith("127.0.0."):
logging.debug("protocols.https.query: Request from non-local IP")
return
self.santiago.query(host, service)
return super(Listener, self).learn(host, service)
@cherrypy.expose
@cherrypy.tools.json_out()
def where(self, host, service):
"""Show where a host is providing me services."""
if not cherrypy.request.remote.ip.startswith("127.0.0."):
logging.debug("protocols.https.query: Request from non-local IP")
return
return list(super(Listener, self).where(host, service))
@cherrypy.expose
def provide(self, client, service, location):
"""Provide a service for the client at the location."""
if not cherrypy.request.remote.ip.startswith("127.0.0."):
logging.debug("protocols.https.query: Request from non-local IP")
return
return super(Listener, self).provide(client, service, location)
@cherrypy.expose
def pdb(self):
"""Set a trace."""
if not cherrypy.request.remote.ip.startswith("127.0.0."):
logging.debug("protocols.https.query: Request from non-local IP")
return
import pdb; pdb.set_trace()
class Sender(SantiagoSender):
def __init__(self, santiago, proxy_host, proxy_port):

View File

@ -171,6 +171,11 @@ class Santiago(object):
def learn_service(self, host, service, locations):
"""Learn a service somebody else hosts for me."""
if service not in self.consuming:
self.consuming[service] = dict()
if host not in self.consuming[service]:
self.consuming[service][host] = set()
if locations:
self.consuming[service][host] = (
@ -179,8 +184,15 @@ class Santiago(object):
def provide_service(self, client, service, locations):
"""Start hosting a service for somebody else."""
if client not in self.hosting:
self.hosting[client] = dict()
if service not in self.hosting[client]:
self.hosting[client][service] = set()
if locations:
self.hosting[client][service].union(locations)
self.hosting[client][service] = (
self.hosting[client][service] | locations)
def get_host_locations(self, client, service):
"""Return where I'm hosting the service for the client.
@ -201,7 +213,6 @@ class Santiago(object):
except KeyError as e:
logging.exception(e)
def query(self, host, service):
"""Request a service from another Santiago.
@ -464,6 +475,7 @@ class Santiago(object):
self.requests[host].remove(service)
# clean buffers
# TODO clean up after 5 minutes to allow all hosts to reply?
if not self.requests[host]:
del self.requests[host]
@ -598,6 +610,29 @@ class SantiagoListener(SantiagoConnector):
def incoming_request(self, request):
self.santiago.incoming_request(request)
def where(self, host, service):
"""Return where the named host provides me a service.
If no service is provided, return None.
TODO: unittest
"""
return self.santiago.get_client_locations(host, service)
def learn(self, host, service):
"""Request a service from another Santiago client.
TODO: add request whitelisting.
"""
return self.santiago.query(host, service)
def provide(self, client, service, location):
"""Provide a service for the client at the location."""
return self.santiago.provide_service(client, service, set([location]))
class SantiagoSender(SantiagoConnector):
"""Generic Santiago Sender superclass.
@ -621,11 +656,9 @@ if __name__ == "__main__":
"ssl_private_key": cert }, }
senders = { "https": { "proxy_host": "localhost",
"proxy_port": 8118} }
hosting = { mykey: { "santiago": set( ["https://localhost:8080",
"https://somestuff" ] )}}
hosting = { mykey: { "santiago": set( ["https://localhost:8080"] )}}
consuming = { "santiago": { mykey: set( ["https://localhost:8080"] )}}
# load the Santiago
santiago = Santiago(listeners, senders,
hosting, consuming,
me=mykey)