When SocketError occurs, an event may not be notified to an other client.
It is because rpc_clients is changed in the loop.

Signed-off-by: Satoshi Kobayashi <[email protected]>
---
 ryu/app/ws_topology.py                 |  9 ++++--
 ryu/tests/unit/app/test_ws_topology.py | 54 ++++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 2 deletions(-)
 create mode 100644 ryu/tests/unit/app/test_ws_topology.py

diff --git a/ryu/app/ws_topology.py b/ryu/app/ws_topology.py
index 1d5fb82..0685d99 100644
--- a/ryu/app/ws_topology.py
+++ b/ryu/app/ws_topology.py
@@ -35,7 +35,8 @@ $ sudo mn --controller=remote --topo linear,2
 """  # noqa
 
 from socket import error as SocketError
-from tinyrpc.exc import InvalidReplyError
+from ryu.contrib.tinyrpc.exc import InvalidReplyError
+
 
 from ryu.app.wsgi import (
     ControllerBase,
@@ -83,6 +84,7 @@ class WebSocketTopology(app_manager.RyuApp):
         self._rpc_broadcall('event_link_delete', msg)
 
     def _rpc_broadcall(self, func_name, msg):
+        disconnected_clients = []
         for rpc_client in self.rpc_clients:
             # NOTE: Although broadcasting is desired,
             #       RPCClient#get_proxy(one_way=True) does not work well
@@ -91,10 +93,13 @@ class WebSocketTopology(app_manager.RyuApp):
                 getattr(rpc_server, func_name)(msg)
             except SocketError:
                 self.logger.debug('WebSocket disconnected: %s' % rpc_client.ws)
-                self.rpc_clients.remove(rpc_client)
+                disconnected_clients.append(rpc_client)
             except InvalidReplyError as e:
                 self.logger.error(e)
 
+        for client in disconnected_clients:
+            self.rpc_clients.remove(client)
+
 
 class WebSocketTopologyController(ControllerBase):
 
diff --git a/ryu/tests/unit/app/test_ws_topology.py 
b/ryu/tests/unit/app/test_ws_topology.py
new file mode 100644
index 0000000..6eaaa0e
--- /dev/null
+++ b/ryu/tests/unit/app/test_ws_topology.py
@@ -0,0 +1,54 @@
+# Copyright (C) 2013 Stratosphere Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+import unittest
+from socket import error as SocketError
+
+import mock
+
+from ryu.app.ws_topology import WebSocketTopology
+
+
+class Test_ws_topology(unittest.TestCase):
+
+    def test_when_sock_error(self):
+        args = {
+            'wsgi': mock.Mock(),
+        }
+        app = WebSocketTopology(**args)
+
+        rpc_client_mock1 = mock.Mock()
+        config = {
+            'get_proxy.return_value.event_link_add.side_effect': SocketError,
+        }
+        rpc_client_mock1.configure_mock(**config)
+
+        rpc_client_mock2 = mock.Mock()
+
+        app.rpc_clients = [
+            rpc_client_mock1,
+            rpc_client_mock2,
+        ]
+
+        ev_mock = mock.Mock()
+        app._event_link_add_handler(ev_mock)
+
+        rpc_client_mock1.get_proxy.assert_called_once_with()
+        rpc_client_mock2.get_proxy.assert_called_once_with()
+
+if __name__ == "__main__":
+    unittest.main()
-- 
1.8.5.2 (Apple Git-48)


------------------------------------------------------------------------------
Slashdot TV.  
Video for Nerds.  Stuff that matters.
http://tv.slashdot.org/
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to