Started moving protocol-specific out of Santiago.

This commit is contained in:
Nick Daly 2012-04-01 01:29:38 -05:00
parent 2f3741f7a3
commit 7c35b58363
2 changed files with 40 additions and 158 deletions

View File

@ -1,97 +0,0 @@
import cherrypy
import santiago
from simplejson import JSONEncoder
encoder = JSONEncoder()
# dirty hacks for Debian Squeeze (stable)
# =======================================
def fix_old_cherrypy():
"""Make Squeeze's CherryPy forward-compatible."""
for y in range(0,3):
for x in range(0, 7):
print "WARNING",
print ""
print("You're using an old CherryPy version! We're faking it!")
print("Expect the unexpected! Raar!")
def jsonify_tool_callback(*args, **kwargs):
response = cherrypy.response
response.headers['Content-Type'] = 'application/json'
response.body = encoder.iterencode(response.body)
cherrypy.tools.json_out = cherrypy.Tool('before_finalize', jsonify_tool_callback, priority=30)
if cherrypy.__version__ < "3.2":
fix_old_cherrypy()
# actual HTTP Santiago classes.
# =============================
class SantiagoHttpListener(santiago.SantiagoListener):
"""Listens for connections on the HTTP protocol."""
DEFAULT_RESPONSE = """\
<html><head><title>Use it right.</title></head><body>
<p>Nice try, now try again with a request like:</p>
<p>http://localhost:8080/serve/(requester)/(server)/(service)</p>
<dl>
<dt>requster</dt><dd>james, ian</dd>
<dt>server</dt><dd>nick</dd>
<dt>service</dt><dd>wiki, web</dd>
</dl>
<p>This'll get you good results:</p>
<code><a href="http://localhost:8080/serve/james/wiki/nick">
http://localhost:8080/serve/james/wiki/nick</a></code>
<p>See the <code>serving_to</code>, <code>serving_what</code>, and
<code>me</code> variables.</p>
</body></html>"""
def __init__(self, instance, location="localhost:8080"):
super(SantiagoHttpListener, self).__init__(instance, location)
self.socket_port = location.rpartition(":")[2]
@cherrypy.expose
@cherrypy.tools.json_out()
def serve(self, key=None, service=None, server=None, hops=3, santiagi=None):
"""Handles an incoming request."""
return super(SantiagoHttpListener, self).serve(
key, service, server, hops, santiagi)
@cherrypy.expose
def index(self):
"""Do nothing, unless we're debugging."""
if santiago.DEBUG:
return self.DEFAULT_RESPONSE
class SantiagoHttpSender(santiago.SantiagoSender):
"""Responsible for answering HTTP requests."""
import urllib, urllib2
@cherrypy.tools.json_out()
def proxy(self, key, service, server, hops=3):
"""Forwards on a request to another Santiago."""
return super(SantiagoHttpSender, self).proxy(key, service, server, hops)
@cherrypy.tools.json_out()
def send(self):
"""Send messages to other Santiagi."""
for message in super(SantiagoSender, self).send():
if message["location"].startswith("http"):
urllib2.Request(message["location"],urllib.urlencode(message))

View File

@ -29,15 +29,14 @@ We also don't:
- Use a reasonable data-store. - Use a reasonable data-store.
- Have a decent control mechanism. - Have a decent control mechanism.
FIXME: Split into protocol-specific listeners and senders.
FIXME: add that whole pgp thing. FIXME: add that whole pgp thing.
FIXME: remove @cherrypy.expose from everything but index. FIXME: remove @cherrypy.expose from everything but index.
TODO: add doctests TODO: add doctests
""" """
import cherrypy
from collections import defaultdict as DefaultDict from collections import defaultdict as DefaultDict
#import gnupg import gnupg
import httplib, urllib
import sys import sys
try: try:
@ -54,10 +53,8 @@ def load_data(server, item):
FIXME: use withsqlite instead. FIXME: use withsqlite instead.
""" """
data = ""
with open("%s_%s" % (server, item)) as infile: with open("%s_%s" % (server, item)) as infile:
data = eval(infile.read()) return eval(infile.read())
return data
class SimpleSantiago(object): class SimpleSantiago(object):
@ -103,6 +100,20 @@ class SimpleSantiago(object):
def _create_listeners(self): def _create_listeners(self):
"""Iterates through each known protocol creating listeners for all.""" """Iterates through each known protocol creating listeners for all."""
# FIXME: Split into protocol-specific listeners and senders.
# def configure_gui(self):
# """Launch the gui specified in the launcher's config files.
#
# """
# gui_name = self.config.get(self.META_DATA, "gui")
# import_name = "guis.%(gui_name)s.gemrb_gui_%(gui_name)s" % locals()
# __import__(import_name) # TODO import every gui in the gui dir until one succeeds
#
# gui_module = sys.modules[import_name]
#
# self.gui = gui_module.Gui(self)
for protocol in self.listeners.iterkeys(): for protocol in self.listeners.iterkeys():
method = "_create_%s_listener" % protocol method = "_create_%s_listener" % protocol
@ -111,25 +122,6 @@ class SimpleSantiago(object):
except KeyError: except KeyError:
pass pass
def _create_http_listener(self, *args, **kwargs):
"""Register an HTTP listener.
Merely a wrapper for _create_https_listener.
"""
self._create_https_listener(*args, **kwargs)
def _create_https_listener(self, socket_port=0,
ssl_certificate="", ssl_private_key=""):
"""Registers an HTTPS listener."""
cherrypy.server.socket_port = socket_port
cherrypy.server.ssl_certificate = ssl_certificate
cherrypy.server.ssl_private_key = ssl_private_key
# reach deep into the voodoo to actually serve the index
SimpleSantiago.index.__dict__["exposed"] = True
def am_i(self, server): def am_i(self, server):
"""Verify whether this server is the specified server.""" """Verify whether this server is the specified server."""
@ -166,7 +158,6 @@ class SimpleSantiago(object):
except KeyError: except KeyError:
pass pass
@cherrypy.expose
def query(self, host, service): def query(self, host, service):
"""Request a service from another Santiago. """Request a service from another Santiago.
@ -178,7 +169,7 @@ class SimpleSantiago(object):
self.request(host, self.me, host, self.me, self.request(host, self.me, host, self.me,
service, None, self.get_client_locations(host, "santiago")) service, None, self.get_client_locations(host, "santiago"))
def request(self, from_, to, host, client, def outgoing_request(self, from_, to, host, client,
service, locations, reply_to): service, locations, reply_to):
"""Send a request to another Santiago service. """Send a request to another Santiago service.
@ -188,40 +179,15 @@ class SimpleSantiago(object):
# best guess reply_to if we don't know. # best guess reply_to if we don't know.
reply_to = reply_to or self.get_host_locations(to, "santiago") reply_to = reply_to or self.get_host_locations(to, "santiago")
for destination in self.get_client_locations(to, "santiago"): request = self.gpg.sign_encrypt({
getattr(self, destination.split(":")[0] + "_request") \ "from": from_, "to": to, "host": host, "client": client,
(from_, to, host, client,
service, locations, destination, reply_to)
def https_request(self, from_, to, host, client,
service, locations, destination, reply_to):
"""Send an HTTPS request to each Santiago client.
Don't queue, just immediately send the reply to each location we know.
It's both simple and as reliable as possible.
TODO: pgp sign and encrypt
"""
params = urllib.urlencode(
{"from": from_, "to": to, "host": host, "client": client,
"service": service, "locations": locations or "", "service": service, "locations": locations or "",
"reply_to": reply_to}) "reply_to": reply_to})
proxy = self.senders["https"] for destination in self.get_client_locations(to, "santiago"):
getattr(self, destination.split(":")[0] + "_request")(request)
# TODO: Does HTTPSConnection require the cert and key? def incoming_request(self, **kwargs):
# Is the fact that the server has it sufficient? I think so.
connection = httplib.HTTPSConnection(destination.split("//")[1])
if sys.version_info >= (2, 7):
connection.set_tunnel(proxy["host"], proxy["port"])
connection.request("GET", "/?%s" % params)
connection.close()
def index(self, **kwargs):
"""Provide a service to a client. """Provide a service to a client.
This tag doesn't do any real processing, it just catches and hides This tag doesn't do any real processing, it just catches and hides
@ -309,6 +275,8 @@ class SimpleSantiago(object):
Attempt to contact the other Santiago and ask it to reply both to the Attempt to contact the other Santiago and ask it to reply both to the
original host as well as me. original host as well as me.
TODO: add tests.
""" """
self.request(self.me, to, host, client, self.request(self.me, to, host, client,
service, reply_to) service, reply_to)
@ -343,14 +311,13 @@ class SimpleSantiago(object):
self.learn_service(host, service, locations) self.learn_service(host, service, locations)
self.requests[host].remove(service) self.requests[host].remove(service)
@cherrypy.expose
def save_server(self): def save_server(self):
"""Save all operational data to files. """Save all operational data to files.
Save all files with the ``self.me`` prefix. Save all files with the ``self.me`` prefix.
""" """
for datum in ("hosting", "consuming", "listeners", "senders"): for datum in ("hosting", "consuming"):
name = "%s_%s" % (self.me, datum) name = "%s_%s" % (self.me, datum)
try: try:
@ -359,6 +326,18 @@ class SimpleSantiago(object):
except: except:
pass pass
class SantiagoListener(object):
"""Generic Santiago Listener superclass."""
def __init__(self, santiago):
self.santiago = santiago
class SantiagoSender(object):
"""Generic Santiago Sender superclass."""
def __init__(self, santiago):
self.santiago = santiago
if __name__ == "__main__": if __name__ == "__main__":
# FIXME: convert this to the withsqlite setup. # FIXME: convert this to the withsqlite setup.