summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKurt B. Kaiser <kbk@shore.net>2002-07-26 00:06:42 (GMT)
committerKurt B. Kaiser <kbk@shore.net>2002-07-26 00:06:42 (GMT)
commitb417936d4080004b6a7811fb411c6f77db4cc262 (patch)
tree0f36813466ebe3c7f58f7cc0ee7244b9558570a2
parentdb40afaabe09062fcf0ff683a9f5ed7df275fd40 (diff)
downloadcpython-b417936d4080004b6a7811fb411c6f77db4cc262.zip
cpython-b417936d4080004b6a7811fb411c6f77db4cc262.tar.gz
cpython-b417936d4080004b6a7811fb411c6f77db4cc262.tar.bz2
Reverse the RPC socket connection: Python execution server connects to
Idle client and localhost origin of connection is verified by client. M PyShell.py M rpc.py M run.py
-rw-r--r--Lib/idlelib/PyShell.py18
-rw-r--r--Lib/idlelib/rpc.py136
-rw-r--r--Lib/idlelib/run.py33
3 files changed, 113 insertions, 74 deletions
diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py
index f2ac36d..a0a641b 100644
--- a/Lib/idlelib/PyShell.py
+++ b/Lib/idlelib/PyShell.py
@@ -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)
diff --git a/Lib/idlelib/rpc.py b/Lib/idlelib/rpc.py
index 60a6335..267dd60 100644
--- a/Lib/idlelib/rpc.py
+++ b/Lib/idlelib/rpc.py
@@ -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):
diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py
index 9f26c16..5db652e 100644
--- a/Lib/idlelib/run.py
+++ b/Lib/idlelib/run.py
@@ -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):