On Thu, 2009-09-03 at 23:43 +0200, Emilie Anceau wrote: > Hi, > > It would be great to implement the following in the trunk, with the > associated tests. The general idea is that all sessions for a given > uid=HASH are proxied (same technique as proxyfilter) to the server that > is responsible for this session uid. This server is supposed to have a > pokeravatar instance with explained enabled and is therefore the > "explain" server. > > The uid=HASH must be associated to the resthost responsible for the > session in memcache. A restfilter is implemented to lookup the memcache > with the given uid. If one is found (one that is not the current > server), then the request is proxied to the designated server. > > If no entry is found in memcache, the request is handled by the server. > At the end of the handling of the request ( check persist session in > pokersite.py ), if the session persists ( no need to change the > criterion for which the session persists ), the uid=HASH entry for the > resthost URL of the current server is written in memcache. This must be > done on each request in order to refresh the memcache entry and prevent > it from expiring.
Hi, Feel free to review the attached patch, to tell me if it goes in the right direction, and what remains to be done. Thanks. -- Johan Euphrosine <[email protected]>
Index: Makefile.am
===================================================================
--- Makefile.am (revision 6211)
+++ Makefile.am (working copy)
@@ -82,6 +82,7 @@
pokernetwork/protocol.py \
pokernetwork/proxy.py \
pokernetwork/proxyfilter.py \
+ pokernetwork/explainfilter.py \
pokernetwork/pokerrestclient.py \
pokernetwork/server.py \
pokernetwork/user.py \
Index: configure.ac
===================================================================
--- configure.ac (revision 6211)
+++ configure.ac (working copy)
@@ -265,6 +265,7 @@
tests/test-pokertable.py
tests/test-proxy.py
tests/test-proxyfilter.py
+ tests/test-explainfilter.py
tests/test-nullfilter.py
tests/test-webservice.py
tests/test-protocol.py
Index: tests/Makefile.am
===================================================================
--- tests/Makefile.am (revision 6211)
+++ tests/Makefile.am (working copy)
@@ -88,6 +88,7 @@
test-pokernetworkconfig.py \
test-proxyfilter.py \
test-nullfilter.py \
+ test-explainfilter.py \
test-proxy.py \
test-pokerserver.py \
test-pokerserver-run-load.py \
Index: pokernetwork/explainfilter.py
===================================================================
--- pokernetwork/explainfilter.py (revision 0)
+++ pokernetwork/explainfilter.py (revision 0)
@@ -0,0 +1,61 @@
+#
+# Copyright (C) 2008, 2009 Loic Dachary <[email protected]>
+# Copyright (C) 2009 Johan Euphrosine <[email protected]>
+#
+# This software's license gives you freedom; you can copy, convey,
+# propagate, redistribute and/or modify this program under the terms of
+# the GNU Affero General Public License (AGPL) as published by the Free
+# Software Foundation (FSF), either version 3 of the License, or (at your
+# option) any later version of the AGPL published by the FSF.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program in a file in the toplevel directory called
+# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
+#
+
+from twisted.internet import reactor
+from pokernetwork.pokerrestclient import PokerProxyClientFactory
+import simplejson
+
+local_reactor = reactor
+
+#
+# return a value if all actions were complete
+#
+def rest_filter(site, request, packet):
+ if request.finished:
+ #
+ # the request has been answered by a filter earlier in the chain
+ #
+ return True
+ service = site.resource.service
+ uid = request.args.get('uid', [''])[0]
+
+ requestHost = request.channel.transport.getHost()
+ host = requestHost.host
+ port = requestHost.port
+ path = request.path
+ if uid:
+ url = site.memcache.get(uid)
+ if url:
+ resthost = simplejson.loads(url)
+ (host, port, path) = [str(s) for s in resthost]
+ parts = request.uri.split('?', 1)
+ if len(parts) > 1:
+ path += '?' + parts[1]
+ request.content.seek(0, 0)
+ header = request.getAllHeaders()
+ data = request.content.read()
+ clientFactory = PokerProxyClientFactory(
+ request.method, path, request.clientproto,
+ header, data, request,
+ service.verbose, host + ':' + str(port) + path)
+ local_reactor.connectTCP(host, int(port), clientFactory)
+ return clientFactory.deferred
+ site.memcache.set(uid, simplejson.dumps((host, port, path)))
+ return True
Index: tests/test-explainfilter.py.in
===================================================================
--- tests/test-explainfilter.py.in (revision 0)
+++ tests/test-explainfilter.py.in (revision 0)
@@ -0,0 +1,187 @@
+...@python@
+# -*- mode: python -*-
+#
+# Copyright (C) 2008, 2009 Loic Dachary <[email protected]>
+# Copyright (C) 2009 Bradley M. Kuhn <[email protected]>
+# Copyright (C) 2009 Johan Euphrosine <[email protected]>
+#
+# This software's license gives you freedom; you can copy, convey,
+# propagate, redistribute and/or modify this program under the terms of
+# the GNU Affero General Public License (AGPL) as published by the Free
+# Software Foundation (FSF), either version 3 of the License, or (at your
+# option) any later version of the AGPL published by the FSF.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program in a file in the toplevel directory called
+# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
+#
+import sys, os
+sys.path.insert(0, "@srcdir@/..")
+sys.path.insert(0, "..")
+
+import simplejson
+
+from twisted.trial import unittest, runner, reporter
+from twisted.internet import defer, reactor
+from twisted.application import internet
+from twisted.python import failure
+from twisted.python.runtime import seconds
+import twisted.internet.base
+twisted.internet.base.DelayedCall.debug = True
+
+from twisted.web import client, http
+
+from tests import testmessages
+verbose = int(os.environ.get('VERBOSE_T', '-1'))
+if verbose < 0: testmessages.silence_all_messages()
+
+from tests import testclock
+
+from pokernetwork import pokermemcache
+from pokernetwork import pokersite
+from pokernetwork import pokernetworkconfig
+from pokernetwork import pokerservice
+from pokernetwork import explainfilter
+from pokernetwork.pokerpackets import *
+
+settings_xml_server = """<?xml version="1.0" encoding="ISO-8859-1"?>
+<server verbose="6" ping="300000" autodeal="yes" simultaneous="4" chat="yes" >
+ <delays autodeal="20" round="0" position="0" showdown="0" autodeal_max="1" finish="0" messages="60" />
+
+ <table name="Table1" variant="holdem" betting_structure="100-200-no-limit" seats="10" player_timeout="60" currency_serial="1" />
+ <table name="Table2" variant="holdem" betting_structure="100-200-no-limit" seats="10" player_timeout="60" currency_serial="1" />
+
+ <listen tcp="19481" />
+ <resthost host="127.0.0.1" port="19481" path="/POKER_REST" />
+
+ <cashier acquire_timeout="5" pokerlock_queue_timeout="30" user_create="yes" />
+ <database name="pokernetworktest" host="localhost" user="pokernetworktest" password="pokernetwork"
+ root_user="@MYSQL_TEST_DBROOT@" root_password="@MYSQL_TEST_DBROOT_PASSWORD@" schema="@srcdir@/../../database/schema.sql" command="@MYSQL@" />
+ <path>.. ../@srcdir@ @POKER_ENGINE_PKGSYSCONFDIR@ @POKER_NETWORK_PKGSYSCONFDIR@</path>
+ <users temporary="BOT"/>
+</server>
+"""
+
+settings_xml_proxy = """<?xml version="1.0" encoding="ISO-8859-1"?>
+<server verbose="6" ping="300000" autodeal="yes" simultaneous="4" chat="yes" >
+ <delays autodeal="20" round="0" position="0" showdown="0" autodeal_max="1" finish="0" messages="60" />
+
+ <listen tcp="19480" />
+
+ <rest_filter>../@srcdir@/../pokernetwork/explainfilter.py</rest_filter>
+
+ <cashier acquire_timeout="5" pokerlock_queue_timeout="30" user_create="yes" />
+ <database name="pokernetworktest" host="localhost" user="pokernetworktest" password="pokernetwork"
+ root_user="@MYSQL_TEST_DBROOT@" root_password="@MYSQL_TEST_DBROOT_PASSWORD@" schema="@srcdir@/../../database/schema.sql" command="@MYSQL@" />
+ <path>.. ../@srcdir@ @POKER_ENGINE_PKGSYSCONFDIR@ @POKER_NETWORK_PKGSYSCONFDIR@</path>
+ <users temporary="BOT"/>
+</server>
+"""
+class ExplainFilterTestCase(unittest.TestCase):
+ def destroyDb(self, arg = None):
+ if len("@MYSQL_TEST_DBROOT_PASSWORD@") > 0:
+ os.system("@MYSQL@ -u @MYSQL_TEST_DBROOT@ --password='@MYSQL_TEST_DBROOT_PASSWORD@' -e 'DROP DATABASE IF EXISTS pokernetworktest'")
+ else:
+ os.system("@MYSQL@ -u @MYSQL_TEST_DBROOT@ -e 'DROP DATABASE IF EXISTS pokernetworktest'")
+ # --------------------------------------------------------------
+ def initServer(self):
+ settings = pokernetworkconfig.Config([])
+ settings.loadFromString(settings_xml_server)
+ self.server_service = pokerservice.PokerService(settings)
+ self.server_service.disconnectAll = lambda: True
+ self.server_service.startService()
+ self.server_site = pokersite.PokerSite(settings, pokerservice.PokerRestTree(self.server_service))
+ self.server_port = reactor.listenTCP(19481, self.server_site, interface="127.0.0.1")
+ # --------------------------------------------------------------
+ def initProxy(self):
+ settings = pokernetworkconfig.Config([])
+ settings.loadFromString(settings_xml_proxy)
+ self.proxy_service = pokerservice.PokerService(settings)
+ self.proxy_service.disconnectAll = lambda: True
+ self.proxy_service.startService()
+ self.proxy_site = pokersite.PokerSite(settings, pokerservice.PokerRestTree(self.proxy_service))
+ self.proxy_port = reactor.listenTCP(19480, self.proxy_site, interface="127.0.0.1")
+ # --------------------------------------------------------------
+ def setUp(self):
+ testclock._seconds_reset()
+ pokermemcache.memcache = pokermemcache.MemcacheMockup
+ pokermemcache.memcache_singleton = {}
+ self.destroyDb()
+ self.initServer()
+ self.initProxy()
+ # --------------------------------------------------------------
+ def tearDownServer(self):
+ self.server_site.stopFactory()
+ d = self.server_service.stopService()
+ d.addCallback(lambda x: self.server_port.stopListening())
+ return d
+ # --------------------------------------------------------------
+ def tearDownProxy(self):
+ self.proxy_site.stopFactory()
+ d = self.proxy_service.stopService()
+ d.addCallback(lambda x: self.proxy_port.stopListening())
+ return d
+ # --------------------------------------------------------------
+ def tearDown(self):
+ d = defer.DeferredList((
+ self.tearDownServer(),
+ self.tearDownProxy()
+ ))
+ d.addCallback(self.destroyDb)
+ d.addCallback(lambda x: reactor.disconnectAll())
+ return d
+ # --------------------------------------------------------------
+ def test01_ping_proxy(self):
+ """
+ Ping to the proxy.
+ """
+ d = client.getPage("http://127.0.0.1:19480/POKER_REST?uid=1bebebaffe&auth=deadbeef",
+ postdata = '{"type": "PacketPing"}')
+ def checkPing(result):
+ self.assertEqual('[]', str(result))
+ self.assertNotEquals(None, self.proxy_site.memcache.get('1bebebaffe'))
+ d.addCallback(checkPing)
+ return d
+ # --------------------------------------------------------------
+ def test02_tableJoin(self):
+ """
+ Join a table thru a proxy.
+ """
+ self.proxy_site.memcache.set('1bebebaffe', simplejson.dumps(('127.0.0.1', 19481, '/POKER_REST')))
+ d = client.getPage("http://127.0.0.1:19480/POKER_REST?uid=1bebebaffe&auth=deadbeef",
+ postdata = '{"type":"PacketPokerTableJoin","game_id":1}')
+ def checkTable(result):
+ print result
+ packets = simplejson.JSONDecoder().decode(result)
+ self.assertEqual('PacketPokerTable', packets[0]['type'])
+ self.assertEqual('Table1', packets[0]['name'])
+ d.addCallback(checkTable)
+ return d
+
+################################################################################
+def Run():
+ loader = runner.TestLoader()
+# loader.methodPrefix = "test06"
+ suite = loader.suiteFactory()
+ suite.addTest(loader.loadClass(ExplainFilterTestCase))
+ return runner.TrialRunner(
+ reporter.VerboseTextReporter,
+ tracebackFormat='default',
+# logfile = '-',
+ ).run(suite)
+
+if __name__ == '__main__':
+ if Run().wasSuccessful():
+ sys.exit(0)
+ else:
+ sys.exit(1)
+################################################################################
+# Interpreted by emacs
+# Local Variables:
+# compile-command: "( cd .. ; ./config.status tests/test-explainfilter.py ) ; ( cd ../tests ; make COVERAGE_FILES='../pokernetwork/explainfilter.py' VERBOSE_T=-1 TESTS='coverage-reset test-explainfilter.py coverage-report' check )"
+# End:
signature.asc
Description: This is a digitally signed message part
_______________________________________________ Pokersource-users mailing list [email protected] https://mail.gna.org/listinfo/pokersource-users
