]> granicus.if.org Git - python/commitdiff
Reverse the RPC socket connection: Python execution server connects to
authorKurt B. Kaiser <kbk@shore.net>
Fri, 26 Jul 2002 00:06:42 +0000 (00:06 +0000)
committerKurt B. Kaiser <kbk@shore.net>
Fri, 26 Jul 2002 00:06:42 +0000 (00:06 +0000)
Idle client and localhost origin of connection is verified by client.
M PyShell.py
M rpc.py
M run.py

Lib/idlelib/PyShell.py
Lib/idlelib/rpc.py
Lib/idlelib/run.py

index f2ac36d46893bfb1f0c3f0a45582cab91023c900..a0a641ba329292b71d2bfa5d2f0ace76d4fedc78 100644 (file)
@@ -193,24 +193,30 @@ class ModifiedInterpreter(InteractiveInterpreter):
     def spawn_subprocess(self):
         port = 8833
         addr = ("localhost", port)
+        # Spawn the Python execution "server"
         w = ['-W' + s for s in sys.warnoptions]
         args = [sys.executable] + w + ["-c", "__import__('run').main()",
                                        str(port)]
         self.rpcpid = os.spawnv(os.P_NOWAIT, args[0], args)
-        for i in range(5):
+        # Idle starts listening for connection on localhost, retry since
+        # Idle may be restarted before port is available for rebinding
+        # XXX 25 July 2002 KBK Find out what is causing the delayed release!
+        for i in range(12):
             time.sleep(i)
             try:
                 self.rpcclt = rpc.RPCClient(addr)
                 break
             except socket.error, err:
-                if i > 3:
-                    print >>sys.__stderr__, "Socket error:", err, "; retry..."
+                if i < 5:
+                    print>>sys.__stderr__, ". ",
+                else:
+                    print>>sys.__stderr__,"\nIdle socket error: " + err[1]\
+                                                    + ", retrying..."
         else:
-            # XXX Make this a dialog?  #GvR
-            print >>sys.__stderr__, "Can't spawn subprocess!"
-            # XXX Add Stephen's error msg, resolve the two later... KBK 09Jun02
             display_port_binding_error()
             return
+        # Accept the connection from the Python execution server
+        self.rpcclt.accept()
         self.rpcclt.register("stdin", self.tkconsole)
         self.rpcclt.register("stdout", self.tkconsole.stdout)
         self.rpcclt.register("stderr", self.tkconsole.stderr)
index 60a6335ea11cfc3b039c5aef821efd68c732efe4..267dd60564bd3b8cc939996ba96f4d7fad669bc5 100644 (file)
@@ -1,22 +1,33 @@
-# ASCII-art documentation
-#
-#  +---------------------------------+ +----------+
-#  | SocketServer.BaseRequestHandler | | SocketIO |
-#  +---------------------------------+ +----------+
-#                  ^                      ^  ^
-#                  |                      |  |
-#                  | + -------------------+  |
-#                  | |                       |
-#  +-------------------------+        +-----------------+
-#  | RPCHandler              |        | RPCClient       |
-#  |-------------------------|        |-----------------|
-#  | register()              |        | remotecall()    |
-#  | unregister()            |        | register()      | 
-#  |                         |        | unregister()    |
-#  |                         |        | get_remote_proxy|
-#  +-------------------------+        +-----------------+
-#
-import sys 
+"""RPC Implemention, originally written for the Python Idle IDE
+
+For security reasons, GvR requested that Idle's Python execution server process
+connect to the Idle process, which listens for the connection.  Since Idle has
+has only one client per server, this was not a limitation.
+
+   +---------------------------------+ +-------------+
+   | SocketServer.BaseRequestHandler | | SocketIO    |
+   +---------------------------------+ +-------------+
+                   ^                   | register()  |
+                   |                   | unregister()|
+                   |                   +-------------+
+                   |                      ^  ^
+                   |                      |  |
+                   | + -------------------+  |
+                   | |                       |
+   +-------------------------+        +-----------------+
+   | RPCHandler              |        | RPCClient       |
+   | [attribute of RPCServer]|        |                 |
+   +-------------------------+        +-----------------+
+
+The RPCServer handler class is expected to provide register/unregister methods.
+RPCHandler inherits the mix-in class SocketIO, which provides these methods.
+
+See the Idle run.main() docstring for further information on how this was
+accomplished in Idle.
+
+"""
+
+import sys
 import socket
 import select
 import SocketServer
@@ -55,40 +66,29 @@ class RPCServer(SocketServer.TCPServer):
     def __init__(self, addr, handlerclass=None):
         if handlerclass is None:
             handlerclass = RPCHandler
-# XXX KBK 25Jun02 Not used in Idlefork, see register/unregister note below.
+# XXX KBK 25Jun02 Not used in Idlefork.
 #        self.objtable = objecttable 
         SocketServer.TCPServer.__init__(self, addr, handlerclass)
 
-    # XXX KBK 25Jun02 Following method is not used (yet)
-#      def verify_request(self, request, client_address):
-#          host, port = client_address
-#          if host != "127.0.0.1":
-#              print "Disallowed host:", host
-#              return 0
-#          else:
-#              return 1
-
-# XXX KBK 25Jun02 The handlerclass is expected to provide register/unregister
-#                 methods.  In Idle, RPCServer is instantiated with
-#                 handlerclass MyHandler, which in turn inherits the
-#                 register/unregister methods from the mix-in class SocketIO.
-#                 It is true that this is asymmetric with the RPCClient's use
-#                 of register/unregister, but I guess that's how a SocketServer
-#                 is supposed to work.
-
-#                 Exactly how this gets set up is convoluted.  When the
-#                 TCPServer is instantiated, it creates an instance of
-#                 run.MyHandler and calls its handle() method.  handle()
-#                 instantiates a run.Executive, passing it a reference to the
-#                 MyHandler object.  That reference is saved as an attribute of
-#                 the Executive instance.  The Executive methods have access to
-#                 the reference and can pass it on to entities that they
-#                 command (e.g. RemoteDebugger.Debugger.start_debugger()).  The
-#                 latter, in turn, can call MyHandler(SocketIO)
-#                 register/unregister methods via the reference to register and
-#                 unregister themselves.  Whew.
-
-    # The following two methods are not currently used in Idlefork.
+    def server_bind(self):
+        "Override TCPServer method, no bind() phase for connecting entity"
+        pass
+
+    def server_activate(self):
+        """Override TCPServer method, connect() instead of listen()
+        
+        Due to the reversed connection, self.server_address is actually the
+        address of the Idle Client to which we are connecting.
+
+        """
+        self.socket.connect(self.server_address)
+        
+    def get_request(self):
+        "Override TCPServer method, return already connected socket"
+        return self.socket, self.server_address
+        
+
+# XXX The following two methods are not currently used in Idlefork.
 #      def register(self, oid, object):
 #          self.objtable[oid] = object
 
@@ -364,6 +364,8 @@ class SocketIO:
                     cv.notify()
                 self.statelock.release()
                 continue
+            
+#----------------- end class SocketIO --------------------
 
 class RemoteObject:
     # Token mix-in class
@@ -388,15 +390,8 @@ class RPCHandler(SocketServer.BaseRequestHandler, SocketIO):
         SocketIO.__init__(self, sock)
         SocketServer.BaseRequestHandler.__init__(self, sock, addr, svr)
 
-    def setup(self):
-        SocketServer.BaseRequestHandler.setup(self)
-        print >>sys.__stderr__, "Connection from", self.client_address
-
-    def finish(self):
-        print >>sys.__stderr__, "End connection from", self.client_address
-        SocketServer.BaseRequestHandler.finish(self)
-
     def handle(self):
+        "handle() method required by SocketServer"
         self.mainloop()
 
     def get_remote_proxy(self, oid):
@@ -404,12 +399,21 @@ class RPCHandler(SocketServer.BaseRequestHandler, SocketIO):
 
 class RPCClient(SocketIO):
 
-    nextseq = 1 # Requests coming from the client are odd
+    nextseq = 1 # Requests coming from the client are odd numbered
 
     def __init__(self, address, family=socket.AF_INET, type=socket.SOCK_STREAM):
-        sock = socket.socket(family, type)
-        sock.connect(address)
-        SocketIO.__init__(self, sock)
+        self.sock = socket.socket(family, type)
+        self.sock.bind(address)
+        self.sock.listen(1)
+
+    def accept(self):
+        newsock, address = self.sock.accept()
+        if address[0] == '127.0.0.1':
+            print>>sys.__stderr__, "Idle accepted connection from ", address
+            SocketIO.__init__(self, newsock)
+        else:
+            print>>sys.__stderr__, "Invalid host: ", address
+            raise socket.error
 
     def get_remote_proxy(self, oid):
         return RPCProxy(self, oid)
@@ -477,6 +481,7 @@ class MethodProxy:
 #
 
 def testServer(addr):
+    # XXX 25 Jul 02 KBK needs update to use rpc.py register/unregister methods
     class RemotePerson:
         def __init__(self,name):
             self.name = name 
@@ -505,10 +510,8 @@ def testServer(addr):
     svr.handle_request()  # process once only
 
 def testClient(addr):
-
-    #
-    # demonstrates RPC Client
-    #
+    "demonstrates RPC Client"
+    # XXX 25 Jul 02 KBK needs update to use rpc.py register/unregister methods
     import time
     clt=RPCClient(addr)
     thomas = clt.get_remote_proxy("thomas")
@@ -523,7 +526,6 @@ def testClient(addr):
     print "Done."
     print 
     time.sleep(2)
-    
     # demonstrates remote server calling local instance
     class LocalPerson:
         def __init__(self,name):
index 9f26c1690f144ccaf504a8c40fa6f0dd604feda2..5db652eb2ce16d7fee6f09eaa51ad0bf5a21c1de 100644 (file)
@@ -1,13 +1,44 @@
 import sys
+import time
+import socket
 import rpc
 
 def main():
+    """Start the Python execution server in a subprocess
+
+    In Idle, RPCServer is instantiated with handlerclass MyHandler, which
+    inherits register/unregister methods from RPCHandler via the mix-in class
+    SocketIO.
+
+    When the RPCServer is instantiated, the TCPServer initialization creates an
+    instance of run.MyHandler and calls its handle() method.  handle()
+    instantiates a run.Executive, passing it a reference to the MyHandler
+    object.  That reference is saved as an attribute of the Executive instance.
+    The Executive methods have access to the reference and can pass it on to
+    entities that they command (e.g. RemoteDebugger.Debugger.start_debugger()).
+    The latter, in turn, can call MyHandler(SocketIO) register/unregister
+    methods via the reference to register and unregister themselves.
+
+    """
     port = 8833
     if sys.argv[1:]:
         port = int(sys.argv[1])
     sys.argv[:] = [""]
     addr = ("localhost", port)
-    svr = rpc.RPCServer(addr, MyHandler)
+    for i in range(12):
+        time.sleep(i)
+        try:
+            svr = rpc.RPCServer(addr, MyHandler)
+            break
+        except socket.error, err:
+            if i < 5:
+                print>>sys.__stderr__, ".. ",
+            else:
+                print>>sys.__stderr__,"\nPython subprocess socket error: "\
+                                              + err[1] + ", retrying...."
+    else:
+        print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
+        sys.exit()
     svr.handle_request() # A single request only
 
 class MyHandler(rpc.RPCHandler):