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 \

Attachment: 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

Reply via email to