diff --git a/ugly_hacks/santiago/protocols/http.py b/ugly_hacks/santiago/protocols/http.py index b9a3b7b13..783f11e01 100644 --- a/ugly_hacks/santiago/protocols/http.py +++ b/ugly_hacks/santiago/protocols/http.py @@ -40,7 +40,7 @@ class SantiagoHttpListener(santiago.SantiagoListener): Use it right.

Nice try, now try again with a request like:

-

http://localhost:8080/santiago/(requester)/(server)/(service)

+

http://localhost:8080/serve/(requester)/(server)/(service)

requster
james, ian
@@ -49,17 +49,17 @@ class SantiagoHttpListener(santiago.SantiagoListener):

This'll get you good results:

- - http://localhost:8080/santiago/james/nick/wiki + + http://localhost:8080/serve/james/wiki/nick

See the serving_to, serving_what, and me variables.

""" - def __init__(self, instance, port=8080): - super(SantiagoHttpListener, self).__init__(instance) - self.socket_port = port + 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() diff --git a/ugly_hacks/santiago/santiago.py b/ugly_hacks/santiago/santiago.py index 399ae7d54..ee684ad84 100644 --- a/ugly_hacks/santiago/santiago.py +++ b/ugly_hacks/santiago/santiago.py @@ -66,7 +66,7 @@ class Santiago(object): Each Santiago keeps track of the services it hosts, and other servers' Santiago services. A Santiago has no idea of and is not responsible for anybody else's services. - + """ self.instance = instance self.hosting = self.load_dict("hosting") @@ -95,7 +95,7 @@ class Santiago(object): def am_i(self, server): """Hello? Is it me you're looking for?""" - + return self.instance == server # Server-related tags @@ -106,7 +106,7 @@ class Santiago(object): post:: - location in cfg.santiago.hosting[service] + location in self.hosting[service] """ self.hosting[service].append(location) @@ -116,7 +116,7 @@ class Santiago(object): post:: - service in cfg.santiago.keys[key] + service in self.keys[key] """ self.keys[key].append(service) @@ -129,7 +129,7 @@ class Santiago(object): post:: - forall(locations, lambda x: x in cfg.santiago.servers[service][key]) + forall(locations, lambda x: x in self.servers[service][key]) """ self.servers[service][key] += locations @@ -139,7 +139,7 @@ class Santiago(object): def add_listener(self, listener): """Registers a protocol-specific listener.""" - + self.listeners.append(listener) def add_sender(self, sender): @@ -149,18 +149,19 @@ class Santiago(object): # processing related tags # ----------------------- - - def serve(self, key, service, server, hops, santiagi): + + def serve(self, key, service, server, hops, santiagi, listener): """Provide a requested service to a client.""" if santiagi is not None: self.learn_service("santiago", key, santiagi) if not self.am_i(server): - return self.proxy(key, service, server, hops=hops) + self.proxy(key, service, server, hops=hops) if service in self.keys[key]: - return self.hosting[service] + # TODO pick the senders more intelligently. + self.senders[0].ack(key, self.hosting[service], listener) def proxy(self, key, service, server, hops=3): """Passes a Santiago request off to another known host. @@ -183,11 +184,12 @@ class Santiago(object): class SantiagoListener(object): """Listens for requests on the santiago port.""" - def __init__(self, santiago): + def __init__(self, santiago, location): self.santiago = santiago + self.location = location def serve(self, key, service, server, hops, santiagi): - return self.santiago.serve(key, service, server, hops, santiagi) + return self.santiago.serve(key, service, server, hops, santiagi, self.location) class SantiagoSender(object): @@ -195,6 +197,14 @@ class SantiagoSender(object): def __init__(self, santiago): self.santiago = santiago + self.messages = list() + + def send(self): + """Sends all messages on the queue.""" + + answer = self.messages + self.messages = list() + return answer def request(self, destination, resource): """Sends a request for a resource to a known Santiago. @@ -216,7 +226,7 @@ class SantiagoSender(object): # TODO my request is signed with my GPG key, recipient encrypted. """ - pass # TODO: do. + pass # TODO: queue a request message. def nak(self): """Denies a requested resource to a Santiago. @@ -227,7 +237,7 @@ class SantiagoSender(object): """ pass - def ack(self): + def ack(self, key, location, listener=None): """A successful reply to a Santiago request. The response must include: @@ -239,7 +249,10 @@ class SantiagoSender(object): - The Santiago listener that received and accepted the request. """ - pass + self.messages.append({ + "to": key, + "location": location, + "reply-to": listener,}) def end(self): """Sent by the original requester, when it receives the server's @@ -252,7 +265,7 @@ class SantiagoSender(object): def proxy(self, key, service, server, hops): """Sends the request to another server.""" - + # TODO pull this off, another day. return ("%(key)s is requesting the %(service)s from %(server)s. " + self.santiago.instance + " is not %(server)s. " + diff --git a/ugly_hacks/santiago/test_santiago.py b/ugly_hacks/santiago/test_santiago.py index 9de53bd3b..d7d449669 100644 --- a/ugly_hacks/santiago/test_santiago.py +++ b/ugly_hacks/santiago/test_santiago.py @@ -14,30 +14,117 @@ """ import unittest +from protocols.http import SantiagoHttpSender, SantiagoHttpListener import santiago -# TODO import some http client and validate my responses! +import sys -class TestInitialRequest(unittest.TestCase): + +class SantiagoTest(unittest.TestCase): + """The base class for tests.""" + + def setUp(self): + self.santiago = santiago.Santiago("0x1") + + if sys.version_info < (2, 7): + """Add a poor man's forward compatibility.""" + + class ContainsError(AssertionError): + pass + + def assertIn(self, a, b): + if not a in b: + raise self.ContainsError("%s not in %s" % (a, b)) + +class TestDataModel(SantiagoTest): + """Test adding and removing services and data.""" + + def test_add_listener(self): + """Verify that we can add a listener.""" + + http_listener = SantiagoHttpListener(self.santiago, "localhost:8080") + self.santiago.add_listener(http_listener) + + self.assertIn(http_listener, self.santiago.listeners) + + def test_add_sender(self): + """Verify that we can add a sender.""" + + http_sender = SantiagoHttpSender(self.santiago) + self.santiago.add_sender(http_sender) + + self.assertIn(http_sender, self.santiago.senders) + + def test_provide_at_location(self): + """Verify that we can provide a service somewhere.""" + + service, location = ("something", "there") + self.santiago.provide_at_location(service, location) + + self.assertIn(location, self.santiago.hosting[service]) + + def test_provide_for_key(self): + """Verify we can provide a specific service to someone.""" + + service, key = ("something", "0x1") + self.santiago.provide_for_key(service, key) + + self.assertIn(service, self.santiago.keys[key]) + + +class TestServing(SantiagoTest): + """TODO: tests for: + + (normal serving + proxying) * (learning santiagi + not learning) + + """ + def setUp(self): + super(TestServing, self).setUp() + + self.santiago.provide_at_location("wiki", "192.168.0.13") + self.santiago.provide_for_key("wiki", "0x2") + + self.listener = santiago.SantiagoListener(self.santiago, "localhost:8080") + self.sender = santiago.SantiagoSender(self.santiago) + self.santiago.add_listener(self.listener) + self.santiago.add_sender(self.sender) + + def test_successful_serve(self): + """Make sure we get an expected, successful serving message back.""" + + self.listener.serve("0x2", "wiki", "0x1", 0, None) + expected = [ + { "to": "0x2", + "location": ["192.168.0.13"], + "reply-to": self.listener.location, }, ] + + self.assertEqual(self.sender.send(), expected) + + +class TestConsuming(SantiagoTest): + """TODO: tests for: + + (learning services) + + """ + pass + +class TestInitialRequest(SantiagoTest): """Testing the initial request. Does Santiago produce well-formed output when creating a service request? """ - def setup(self): - requester = santiago.SantiagoSender("initial_requester") - destination = santiago.SantiagoListener("initial_destination",8081) - - def test_valid_request(self): - """When requested, does Santiago send out an appropriate message?""" - - # TODO finish these and otehr tests - requester.request(destination="james", service="wiki") - -class TestInitialResponse(unittest.TestCase): pass -class TestForwardedRequest(unittest.TestCase): +class TestInitialResponse(SantiagoTest): pass -class TestForwardedResponse(unittest.TestCase): +class TestForwardedRequest(SantiagoTest): pass + +class TestForwardedResponse(SantiagoTest): + pass + + +if __name__ == "__main__": + unittest.main()