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()