Hi, I cleaned and covered your implementation of PokerRestClient: http://pastebin.com/m1eb7a0a1
Name Stmts Exec Cover Missing --------------------------------------------------------------- ../pokernetwork/pokerrestclient 171 171 100% Feel free to review the attached patch. -- Johan Euphrosine <[email protected]>
Index: pokernetwork/pokerpackets.py
===================================================================
--- pokernetwork/pokerpackets.py (revision 6210)
+++ pokernetwork/pokerpackets.py (working copy)
@@ -4596,8 +4596,22 @@
)
Packet.infoDeclare(globals(), PacketPokerCreateTourney, Packet, "POKER_CREATE_TOURNEY", 166) # 166 # 0xa6 # %SEQ%
+########################################
+class PacketPokerLongPoll(Packet):
+ """ """
+
+ info = Packet.info
+Packet.infoDeclare(globals(), PacketPokerLongPoll, Packet, "POKER_LONG_POLL", 167) # 167 # 0xa7 # %SEQ%
+########################################
+class PacketPokerLongPollReturn(Packet):
+ """ """
+
+ info = Packet.info
+Packet.infoDeclare(globals(), PacketPokerLongPollReturn, Packet, "POKER_LONG_POLL_RETURN", 168) # 168 # 0xa8 # %SEQ%
+
+
_TYPES = range(50,169)
# Interpreted by emacs
Index: pokernetwork/pokerrestclient.py
===================================================================
--- pokernetwork/pokerrestclient.py (revision 0)
+++ pokernetwork/pokerrestclient.py (revision 0)
@@ -0,0 +1,240 @@
+#
+# Copyright (C) 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 defer, protocol, reactor, error
+from twisted.web import http, client
+from twisted.python.util import InsensitiveDict
+from twisted.python.runtime import seconds
+from pokernetwork.pokerpackets import *
+
+import pokersite
+
+class RestClientFactory(protocol.ClientFactory):
+
+ protocol = client.HTTPPageGetter
+
+ def __init__(self, host, port, path, data, timeout):
+ self.timeout = timeout
+ self.agent = "RestClient"
+ self.headers = InsensitiveDict()
+ self.headers.setdefault('Content-Length', len(data))
+ self.headers.setdefault("connection", "close")
+ self.method = 'POST'
+ self.url = 'http://' + host + ':' + str(port) + path
+ self.postdata = data
+ self.host = host
+ self.port = port
+ self.path = path
+ self.waiting = 1
+ self.deferred = defer.Deferred()
+ self.response_headers = None
+ self.cookies = {}
+
+ def __repr__(self):
+ return "<%s: %s>" % (self.__class__.__name__, self.url)
+
+ def buildProtocol(self, addr):
+ p = protocol.ClientFactory.buildProtocol(self, addr)
+ if self.timeout:
+ timeoutCall = reactor.callLater(self.timeout, p.timeout)
+ self.deferred.addBoth(self._cancelTimeout, timeoutCall)
+ return p
+
+ def _cancelTimeout(self, result, timeoutCall):
+ if timeoutCall.active():
+ timeoutCall.cancel()
+ return result
+
+ def gotHeaders(self, headers):
+ self.response_headers = headers
+
+ def gotStatus(self, version, status, message):
+ self.version, self.status, self.message = version, status, message
+
+ def page(self, page):
+ if self.waiting:
+ self.waiting = 0
+ self.deferred.callback(page)
+
+ def noPage(self, reason):
+ if self.waiting:
+ self.waiting = 0
+ self.deferred.errback(reason)
+
+ def clientConnectionFailed(self, _, reason):
+ if self.waiting:
+ self.waiting = 0
+ self.deferred.errback(reason)
+
+class PokerRestClient:
+
+ def __init__(self, host, port, path, verbose, timeout):
+ self.verbose = verbose
+ self.received = lambda packets: True;
+ self.queue = defer.succeed(True)
+ self.pendingLongPoll = False
+ self.minLongPollFrequency = 0.01
+ self.longPollFrequency = 0.1
+ self.sentTime = 0
+ self.host = host
+ self.port = port
+ self.path = path
+ self.timer = None
+ self.timeout = timeout
+ self.longPoll()
+
+ def message(self, string):
+ print 'PokerRestClient(%s) %s' % ( self.host + ':' + str(self.port), string )
+
+ def sendPacket(self, packet, data):
+ if self.pendingLongPoll:
+ if self.verbose > 3:
+ self.message('sendPacket PacketPokerLongPollReturn')
+ self.sendPacketData('{ "type": "PacketPokerLongPollReturn" }')
+ self.queue.addCallback(lambda status: self.sendPacketData(data))
+ if packet.type == PACKET_POKER_LONG_POLL:
+ self.pendingLongPoll = True
+
+ def receivePacket(self, data):
+ print "receivePacket", data
+ if self.pendingLongPoll:
+ self.scheduleLongPoll(0)
+ self.pendingLongPoll = False
+ args = simplejson.loads(data, encoding = 'UTF-8')
+ args = pokersite.fromutf8(args)
+ packets = pokersite.args2packets(args)
+ self.received(packets)
+
+ def receiveError(self, data):
+ self.errorPacket(data)
+
+ def errorPacket(self, reason):
+ self.received([ PacketError(message = str(reason)) ])
+
+ def sendPacketData(self, data):
+ factory = RestClientFactory(self.host, self.port, self.path, data, self.timeout)
+ reactor.connectTCP(self.host, self.port, factory)
+ factory.deferred.addCallbacks(self.receivePacket, self.receiveError)
+ self.queue.addCallback(lambda arg: factory.deferred)
+ self.sentTime = seconds()
+
+ def clearTimeout(self):
+ if self.timer and self.timer.active():
+ self.timer.cancel()
+ self.timer = None
+
+ def scheduleLongPoll(self, delta):
+ self.clearTimeout()
+ self.timer = reactor.callLater(max(self.minLongPollFrequency, self.longPollFrequency - delta), self.longPoll)
+
+ def longPoll(self):
+ if self.longPollFrequency > 0:
+ delta = seconds() - self.sentTime
+ in_line = len(self.queue.callbacks)
+ if in_line <= 0 and delta > self.longPollFrequency:
+ self.clearTimeout()
+ self.sendPacket(PacketPokerLongPoll(),'{ "type": "PacketPokerLongPoll" }');
+ else:
+ self.scheduleLongPoll(delta)
+
+class PokerProxyClient(http.HTTPClient):
+ """
+ Used by PokerProxyClientFactory to implement a simple web proxy.
+ """
+
+ def __init__(self, command, rest, version, headers, data, father):
+ self.father = father
+ self.command = command
+ self.rest = rest
+ if "proxy-connection" in headers:
+ del headers["proxy-connection"]
+ headers["connection"] = "close"
+ self.headers = headers
+ self.data = data
+
+ def connectionMade(self):
+ self.sendCommand(self.command, self.rest)
+ for header, value in self.headers.items():
+ self.sendHeader(header, value)
+ self.endHeaders()
+ self.transport.write(self.data)
+
+ def handleStatus(self, version, code, message):
+ self.father.setResponseCode(int(code), message)
+
+ def handleHeader(self, key, value):
+ self.father.setHeader(key, value)
+
+ def handleResponse(self, buffer):
+ self.father.write(buffer)
+
+ def connectionLost(self, reason):
+ self.father.finish()
+
+class PokerProxyClientFactory(protocol.ClientFactory):
+
+ serial = 0
+
+ protocol = PokerProxyClient
+
+ def __init__(self, command, rest, version, headers, data, father, verbose, destination):
+ self.father = father
+ self.command = command
+ self.rest = rest
+ self.headers = headers
+ self.data = data
+ self.version = version
+ self.deferred = defer.Deferred()
+ self.verbose = verbose
+ self.noisy = False
+ self.destination = destination
+ PokerProxyClientFactory.serial += 1
+ self.serial = PokerProxyClientFactory.serial
+
+ def message(self, string):
+ print 'PokerProxyRestClient(%d) %s' % ( self.serial, string )
+
+ def doStart(self):
+ if self.verbose >= 3:
+ self.message('START %s => %s' % ( self.data, self.destination ))
+ protocol.ClientFactory.doStart(self)
+
+ def doStop(self):
+ if self.verbose >= 3:
+ self.message('STOP')
+ protocol.ClientFactory.doStop(self)
+
+# def error(self, string):
+# self.message("*ERROR* " + str(string))
+
+ def buildProtocol(self, addr):
+ return self.protocol(self.command, self.rest, self.version,
+ self.headers, self.data, self.father)
+
+ def clientConnectionFailed(self, connector, reason):
+ if not self.deferred.called:
+ self.deferred.errback(reason)
+
+ def clientConnectionLost(self, connector, reason):
+ if not self.deferred.called:
+ if reason.check(error.ConnectionDone):
+ self.deferred.callback(True)
+ else:
+ self.deferred.errback(reason)
Index: tests/test-pokerrestclient.py.in
===================================================================
--- tests/test-pokerrestclient.py.in (revision 0)
+++ tests/test-pokerrestclient.py.in (revision 0)
@@ -0,0 +1,216 @@
+...@python@
+# -*- mode: python -*-
+#
+# Copyright (C) 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/>.
+#
+import sys, os
+sys.path.insert(0, "@srcdir@/..")
+sys.path.insert(0, "..")
+
+from twisted.trial import unittest, runner, reporter
+from twisted.internet import defer, reactor
+from twisted.internet import defer
+from twisted.python.util import InsensitiveDict
+
+from tests.testmessages import silence_all_messages
+verbose = int(os.environ.get('VERBOSE_T', '-1'))
+if verbose < 0: silence_all_messages()
+
+from tests import testclock
+
+from pokernetwork import pokerrestclient
+from pokernetwork import pokerservice
+from pokernetwork import pokernetworkconfig
+from pokernetwork import pokermemcache
+from pokernetwork import pokersite
+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>
+"""
+
+class PokerRestClientTestCase(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 setUp(self):
+ testclock._seconds_reset()
+ pokermemcache.memcache = pokermemcache.MemcacheMockup
+ pokermemcache.memcache_singleton = {}
+ self.destroyDb()
+ self.initServer()
+ # --------------------------------------------------------------
+ def tearDownServer(self):
+ self.server_site.stopFactory()
+ d = self.server_service.stopService()
+ d.addCallback(lambda x: self.server_port.stopListening())
+ return d
+ # --------------------------------------------------------------
+ def tearDown(self):
+ d = self.tearDownServer()
+ d.addCallback(self.destroyDb)
+ d.addCallback(lambda x: reactor.disconnectAll())
+ return d
+ # --------------------------------------------------------------
+ def test01_longPoll(self):
+ client = pokerrestclient.PokerRestClient('127.0.0.1', 19481, '/POKER_REST', verbose=6, timeout=10)
+ self.assertEquals(True, client.pendingLongPoll)
+ self.assertEquals(None, client.timer)
+ return client.queue
+ # --------------------------------------------------------------
+ def test02_longPollReturn(self):
+ client = pokerrestclient.PokerRestClient('127.0.0.1', 19481, '/POKER_REST', verbose=6, timeout=10)
+ message = client.message
+ def checkMessageString(string):
+ self.assertSubstring('PacketPokerLongPollReturn', string)
+ message(string)
+ client.message = checkMessageString
+ client.sendPacket(PacketPokerTableSelect(), '{"type": "PacketPokerTableSelect"}')
+ return client.queue
+ # --------------------------------------------------------------
+ def test03_longPollBis(self):
+ client = pokerrestclient.PokerRestClient('127.0.0.1', 19481, '/POKER_REST', verbose=6, timeout=10)
+ client.longPoll()
+ self.assertNotEquals(None, client.timer)
+ return client.queue
+
+class MockRequest:
+ def finish(self): pass
+ def setResponseCode(self, arg1, arg2): pass
+ def handlerHeader(self, arg1, arg2): pass
+ def setHeader(self, arg1, arg2): pass
+ def write(self, arg1): pass
+
+class MockReason():
+ def check(self, reason): return False
+
+class PokerProxyClientFactoryTestCase(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 setUp(self):
+ testclock._seconds_reset()
+ pokermemcache.memcache = pokermemcache.MemcacheMockup
+ pokermemcache.memcache_singleton = {}
+ self.destroyDb()
+ self.initServer()
+ # --------------------------------------------------------------
+ def tearDownServer(self):
+ self.server_site.stopFactory()
+ d = self.server_service.stopService()
+ d.addCallback(lambda x: self.server_port.stopListening())
+ return d
+ # --------------------------------------------------------------
+ def tearDown(self):
+ d = self.tearDownServer()
+ d.addCallback(self.destroyDb)
+ d.addCallback(lambda x: reactor.disconnectAll())
+ return d
+ # --------------------------------------------------------------
+ def test01_proxy(self):
+ data = '{"type": "PacketPing"}'
+ headers = InsensitiveDict()
+ headers.setdefault('Content-Length', len(data))
+ headers.setdefault("connection", "close")
+ headers.setdefault("proxy-connection", "foo")
+ host = '127.0.0.1'
+ port = 19481
+ path = '/POKER_REST'
+ factory = pokerrestclient.PokerProxyClientFactory('POST', path, '1.1', headers, data, MockRequest(), 6, host + ':' + str(port) + path)
+ reactor.connectTCP(host, port, factory)
+ factory.deferred.addCallback(self.assertNotEquals, None)
+ return factory.deferred
+ # --------------------------------------------------------------
+ def test02_proxy_failed(self):
+ factory = pokerrestclient.PokerProxyClientFactory("command", "rest", "version", "headers", "data", "father",
+ "verbose", "destination")
+ def errback(reason):
+ self.assertNotEquals(None, reason)
+ factory.deferred.addErrback(errback)
+ factory.clientConnectionFailed(None, MockReason())
+ return factory.deferred
+ # --------------------------------------------------------------
+ def test03_proxy_lost(self):
+ factory = pokerrestclient.PokerProxyClientFactory("command", "rest", "version", "headers", "data", "father",
+ "verbose", "destination")
+ def errback(reason):
+ self.assertNotEquals(None, reason)
+ factory.deferred.addErrback(errback)
+ factory.clientConnectionLost(None, MockReason())
+ return factory.deferred
+
+def Run():
+ loader = runner.TestLoader()
+# loader.methodPrefix = "test01"
+ suite = loader.suiteFactory()
+ suite.addTest(loader.loadClass(PokerRestClientTestCase))
+ suite.addTest(loader.loadClass(PokerProxyClientFactoryTestCase))
+ return runner.TrialRunner(
+ reporter.VerboseTextReporter,
+ tracebackFormat='default',
+ ).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-pokerrestclient.py ) ; ( cd ../tests ; make COVERAGE_FILES='../pokernetwork/pokerrestclient.py' VERBOSE_T=-1 TESTS='coverage-reset test-pokerrestclient.py coverage-report' check )"
+# End:
Index: configure.ac
===================================================================
--- configure.ac (revision 6210)
+++ configure.ac (working copy)
@@ -274,6 +274,7 @@
tests/test-leak-exarkun.py
tests/test-pokerserver.py
tests/test-pokerserver-run-load.py
+ tests/test-pokerrestclient.py
tests/testcurrency.php
tests/testpoker.php
tests/testbirthday.php
Index: Makefile.am
===================================================================
--- Makefile.am (revision 6210)
+++ Makefile.am (working copy)
@@ -82,6 +82,7 @@
pokernetwork/protocol.py \
pokernetwork/proxy.py \
pokernetwork/proxyfilter.py \
+ pokernetwork/pokerrestclient.py \
pokernetwork/server.py \
pokernetwork/user.py \
pokernetwork/userstats.py \
signature.asc
Description: This is a digitally signed message part
_______________________________________________ Pokersource-users mailing list [email protected] https://mail.gna.org/listinfo/pokersource-users
