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.
- Have a decent control mechanism.
FIXME: Split into protocol-specific listeners and senders.
FIXME: add that whole pgp thing.
FIXME: remove @cherrypy.expose from everything but index.
TODO: add doctests
"""
import cherrypy
from collections import defaultdict as DefaultDict
#import gnupg
import httplib, urllib
import gnupg
import sys
try:
@ -54,10 +53,8 @@ def load_data(server, item):
FIXME: use withsqlite instead.
"""
data = ""
with open("%s_%s" % (server, item)) as infile:
data = eval(infile.read())
return data
return eval(infile.read())
class SimpleSantiago(object):
@ -103,6 +100,20 @@ class SimpleSantiago(object):
def _create_listeners(self):
"""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():
method = "_create_%s_listener" % protocol
@ -111,25 +122,6 @@ class SimpleSantiago(object):
except KeyError:
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):
"""Verify whether this server is the specified server."""
@ -166,7 +158,6 @@ class SimpleSantiago(object):
except KeyError:
pass
@cherrypy.expose
def query(self, host, service):
"""Request a service from another Santiago.
@ -178,7 +169,7 @@ class SimpleSantiago(object):
self.request(host, self.me, host, self.me,
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):
"""Send a request to another Santiago service.
@ -188,40 +179,15 @@ class SimpleSantiago(object):
# best guess reply_to if we don't know.
reply_to = reply_to or self.get_host_locations(to, "santiago")
request = self.gpg.sign_encrypt({
"from": from_, "to": to, "host": host, "client": client,
"service": service, "locations": locations or "",
"reply_to": reply_to})
for destination in self.get_client_locations(to, "santiago"):
getattr(self, destination.split(":")[0] + "_request") \
(from_, to, host, client,
service, locations, destination, reply_to)
getattr(self, destination.split(":")[0] + "_request")(request)
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 "",
"reply_to": reply_to})
proxy = self.senders["https"]
# TODO: Does HTTPSConnection require the cert and key?
# 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):
def incoming_request(self, **kwargs):
"""Provide a service to a client.
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
original host as well as me.
TODO: add tests.
"""
self.request(self.me, to, host, client,
service, reply_to)
@ -343,14 +311,13 @@ class SimpleSantiago(object):
self.learn_service(host, service, locations)
self.requests[host].remove(service)
@cherrypy.expose
def save_server(self):
"""Save all operational data to files.
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)
try:
@ -359,6 +326,18 @@ class SimpleSantiago(object):
except:
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__":
# FIXME: convert this to the withsqlite setup.