On Fri, 2009-09-04 at 16:16 +0200, Johan Euphrosine wrote: > On Fri, 2009-09-04 at 16:04 +0200, Johan Euphrosine wrote: > > On Fri, 2009-09-04 at 12:47 +0200, Johan Euphrosine wrote: > > > 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, > > > > Here is a new patch, that I believe fix the issues you raised on > > #pokersource, let me know if it needs more love. > > > > Thanks. > > Here is an updated version of the patch.
Here is an updated version of the patch, with the fixes you suggested on IRC. -- Johan Euphrosine <pro...@aminche.com>
Index: pokernetwork/pokersite.py =================================================================== --- pokernetwork/pokersite.py (revision 6210) +++ pokernetwork/pokersite.py (working copy) @@ -2,6 +2,7 @@ # -*- py-indent-offset: 4; coding: iso-8859-1 -*- # # Copyright (C) 2008, 2009 Loic Dachary <l...@dachary.org> +# Copyright (C) 2009 Johan Euphrosine <pro...@aminche.com> # Copyright (C) 2008 Bradley M. Kuhn <bk...@ebb.org> # # This software's license gives you freedom; you can copy, convey, @@ -406,6 +407,12 @@ for path in settings.header.xpathEval("/server/rest_filter"): module = imp.load_source("poker_pipe", path.content) self.pipes.append(getattr(module, "rest_filter")) + resthost = settings.headerGetProperties("/server/resthost") + if resthost: + resthost = resthost[0] + self.resthost = ( resthost['host'], int(resthost['port']), resthost['path'] ) + else: + self.resthost = None def message(self, string): print "PokerSite: " + string @@ -432,7 +439,11 @@ def persistSession(self, session): if len(session.avatar.tables) <= 0 and len(session.avatar.tourneys) <= 0: session.expire() + if self.resthost: + self.memcache.delete(session.uid) return False + if self.resthost: + self.memcache.set(session.uid, self.resthost, time = self.cookieTimeout) return True def updateSession(self, session): @@ -465,7 +476,7 @@ if not isinstance(uid, str): raise Exception("uid is not str: '%s' %s" % (uid, type(uid))) if not isinstance(auth, str): - raise Exception("uid is not str: '%s' %s" % (uid, type(uid))) + raise Exception("auth is not str: '%s' %s" % (auth, type(auth))) memcache_serial = self.memcache.get(auth) if memcache_serial == None: # Index: pokernetwork/sessionproxyfilter.py =================================================================== --- pokernetwork/sessionproxyfilter.py (revision 0) +++ pokernetwork/sessionproxyfilter.py (revision 0) @@ -0,0 +1,55 @@ +# +# Copyright (C) 2008, 2009 Loic Dachary <l...@dachary.org> +# Copyright (C) 2009 Johan Euphrosine <pro...@aminche.com> +# +# 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] + + if uid: + resthost = site.memcache.get(uid) + if resthost: + (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 + return True Property changes on: pokernetwork/sessionproxyfilter.py ___________________________________________________________________ Added: svn:mergeinfo 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/sessionproxyfilter.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-sessionproxyfilter.py tests/test-nullfilter.py tests/test-webservice.py tests/test-protocol.py Index: tests/test-sessionproxyfilter.py.in =================================================================== --- tests/test-sessionproxyfilter.py.in (revision 0) +++ tests/test-sessionproxyfilter.py.in (revision 0) @@ -0,0 +1,186 @@ +...@python@ +# -*- mode: python -*- +# +# Copyright (C) 2008, 2009 Loic Dachary <l...@dachary.org> +# Copyright (C) 2009 Bradley M. Kuhn <bk...@ebb.org> +# Copyright (C) 2009 Johan Euphrosine <pro...@aminche.com> +# +# 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 sessionproxyfilter +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/sessionproxyfilter.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 SessionProxyFilterTestCase(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)) + d.addCallback(checkPing) + return d + # -------------------------------------------------------------- + def test02_tableJoin(self): + """ + Join a table thru a proxy. + """ + self.proxy_site.memcache.set('1bebebaffe', ('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(SessionProxyFilterTestCase)) + 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-sessionproxyfilter.py ) ; ( cd ../tests ; make COVERAGE_FILES='../pokernetwork/sessionproxyfilter.py' VERBOSE_T=-1 TESTS='coverage-reset test-sessionproxyfilter.py coverage-report' check )" +# End: Property changes on: tests/test-sessionproxyfilter.py.in ___________________________________________________________________ Added: svn:mergeinfo Index: tests/test-pokersite.py.in =================================================================== --- tests/test-pokersite.py.in (revision 6210) +++ tests/test-pokersite.py.in (working copy) @@ -2,6 +2,7 @@ # -*- mode: python -*- # # Copyright (C) 2008, 2009 Loic Dachary <l...@dachary.org> +# Copyright (C) 2009 Johan Euphrosine <pro...@aminche.com> # Copyright (C) 2008 Bradley M. Kuhn <bk...@ebb.org> # # This software's license gives you freedom; you can copy, convey, @@ -768,13 +769,24 @@ """ the session expires after persistSession, unless there are tables """ + settings_xml = """<?xml version="1.0" encoding="ISO-8859-1"?> +<server verbose="6"> +<resthost host="HOST" port="7777" path="PATH" /> +</server>""" + settings = pokernetworkconfig.Config([]) + settings.loadFromString(settings_xml) + service = PokerServiceMockup() + site = pokersite.PokerSite(settings, pokersite.PokerResource(service)) + session = self.site.makeSession('uid', 'auth') session.avatar.tables[1] = 'table' - self.assertEquals(True, self.site.persistSession(session)) + self.assertEquals(True, site.persistSession(session)) self.assertEquals(False, session.expired) + self.assertEquals(('HOST', 7777, 'PATH'), site.memcache.get('uid')) session.avatar.tables = [] - self.assertEquals(False, self.site.persistSession(session)) + self.assertEquals(False, site.persistSession(session)) self.assertEquals(True, session.expired) + self.assertEquals(None, site.memcache.get('uid')) def Run(): loader = runner.TestLoader() 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-sessionproxyfilter.py \ test-proxy.py \ test-pokerserver.py \ test-pokerserver-run-load.py \
signature.asc
Description: This is a digitally signed message part
_______________________________________________ Pokersource-users mailing list Pokersource-users@gna.org https://mail.gna.org/listinfo/pokersource-users