blob: 9962477cc56185c8cb76e7044004ec661bfcfd71 [file] [log] [blame]
Martin Pantere26da7c2016-06-02 10:07:09 +00001"""RPC Implementation, originally written for the Python Idle IDE
Kurt B. Kaiserb4179362002-07-26 00:06:42 +00002
3For security reasons, GvR requested that Idle's Python execution server process
4connect to the Idle process, which listens for the connection. Since Idle has
Terry Jan Reedyc30b7b12013-03-11 17:57:08 -04005only one client per server, this was not a limitation.
Kurt B. Kaiserb4179362002-07-26 00:06:42 +00006
7 +---------------------------------+ +-------------+
Alexandre Vassalottice261952008-05-12 02:31:37 +00008 | socketserver.BaseRequestHandler | | SocketIO |
Kurt B. Kaiserb4179362002-07-26 00:06:42 +00009 +---------------------------------+ +-------------+
10 ^ | register() |
11 | | unregister()|
12 | +-------------+
13 | ^ ^
14 | | |
15 | + -------------------+ |
16 | | |
17 +-------------------------+ +-----------------+
18 | RPCHandler | | RPCClient |
19 | [attribute of RPCServer]| | |
20 +-------------------------+ +-----------------+
21
22The RPCServer handler class is expected to provide register/unregister methods.
23RPCHandler inherits the mix-in class SocketIO, which provides these methods.
24
25See the Idle run.main() docstring for further information on how this was
26accomplished in Idle.
27
28"""
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040029import builtins
30import copyreg
Terry Jan Reedyad0c57f2014-10-10 19:33:45 -040031import io
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040032import marshal
33import os
34import pickle
35import queue
Chui Tey5d2af632002-05-26 13:36:41 +000036import select
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040037import socket
Alexandre Vassalottice261952008-05-12 02:31:37 +000038import socketserver
Chui Tey5d2af632002-05-26 13:36:41 +000039import struct
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040040import sys
Chui Tey5d2af632002-05-26 13:36:41 +000041import threading
42import traceback
Chui Tey5d2af632002-05-26 13:36:41 +000043import types
Kurt B. Kaisera00050f2003-05-08 20:26:55 +000044
Chui Tey5d2af632002-05-26 13:36:41 +000045def unpickle_code(ms):
Terry Jan Reedy4d921582018-06-19 19:12:52 -040046 "Return code object from marshal string ms."
Chui Tey5d2af632002-05-26 13:36:41 +000047 co = marshal.loads(ms)
48 assert isinstance(co, types.CodeType)
49 return co
50
51def pickle_code(co):
Terry Jan Reedy4d921582018-06-19 19:12:52 -040052 "Return unpickle function and tuple with marshalled co code object."
Chui Tey5d2af632002-05-26 13:36:41 +000053 assert isinstance(co, types.CodeType)
54 ms = marshal.dumps(co)
55 return unpickle_code, (ms,)
56
Terry Jan Reedyad0c57f2014-10-10 19:33:45 -040057def dumps(obj, protocol=None):
Terry Jan Reedy4d921582018-06-19 19:12:52 -040058 "Return pickled (or marshalled) string for obj."
59 # IDLE passes 'None' to select pickle.DEFAULT_PROTOCOL.
Terry Jan Reedyad0c57f2014-10-10 19:33:45 -040060 f = io.BytesIO()
61 p = CodePickler(f, protocol)
62 p.dump(obj)
63 return f.getvalue()
Chui Tey5d2af632002-05-26 13:36:41 +000064
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040065
Terry Jan Reedyad0c57f2014-10-10 19:33:45 -040066class CodePickler(pickle.Pickler):
67 dispatch_table = {types.CodeType: pickle_code}
68 dispatch_table.update(copyreg.dispatch_table)
Chui Tey5d2af632002-05-26 13:36:41 +000069
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040070
Chui Tey5d2af632002-05-26 13:36:41 +000071BUFSIZE = 8*1024
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +000072LOCALHOST = '127.0.0.1'
Chui Tey5d2af632002-05-26 13:36:41 +000073
Alexandre Vassalottice261952008-05-12 02:31:37 +000074class RPCServer(socketserver.TCPServer):
Chui Tey5d2af632002-05-26 13:36:41 +000075
76 def __init__(self, addr, handlerclass=None):
77 if handlerclass is None:
78 handlerclass = RPCHandler
Alexandre Vassalottice261952008-05-12 02:31:37 +000079 socketserver.TCPServer.__init__(self, addr, handlerclass)
Chui Tey5d2af632002-05-26 13:36:41 +000080
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000081 def server_bind(self):
82 "Override TCPServer method, no bind() phase for connecting entity"
83 pass
Chui Tey5d2af632002-05-26 13:36:41 +000084
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000085 def server_activate(self):
86 """Override TCPServer method, connect() instead of listen()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000087
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000088 Due to the reversed connection, self.server_address is actually the
89 address of the Idle Client to which we are connecting.
Chui Tey5d2af632002-05-26 13:36:41 +000090
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000091 """
92 self.socket.connect(self.server_address)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000093
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000094 def get_request(self):
95 "Override TCPServer method, return already connected socket"
96 return self.socket, self.server_address
Chui Tey5d2af632002-05-26 13:36:41 +000097
Kurt B. Kaiser003091c2003-02-17 18:57:16 +000098 def handle_error(self, request, client_address):
Kurt B. Kaisere51529d2003-03-22 19:15:58 +000099 """Override TCPServer method
100
101 Error message goes to __stderr__. No error message if exiting
102 normally or socket raised EOF. Other exceptions not handled in
103 server code will cause os._exit.
104
105 """
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000106 try:
107 raise
108 except SystemExit:
109 raise
Kurt B. Kaisere51529d2003-03-22 19:15:58 +0000110 except:
Kurt B. Kaiser05293772003-03-22 20:11:14 +0000111 erf = sys.__stderr__
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000112 print('\n' + '-'*40, file=erf)
113 print('Unhandled server exception!', file=erf)
Amaury Forgeot d'Arcbed17102008-11-29 01:48:47 +0000114 print('Thread: %s' % threading.current_thread().name, file=erf)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000115 print('Client Address: ', client_address, file=erf)
116 print('Request: ', repr(request), file=erf)
Kurt B. Kaisere51529d2003-03-22 19:15:58 +0000117 traceback.print_exc(file=erf)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000118 print('\n*** Unrecoverable, server exiting!', file=erf)
119 print('-'*40, file=erf)
Kurt B. Kaiser05293772003-03-22 20:11:14 +0000120 os._exit(0)
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000121
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000122#----------------- end class RPCServer --------------------
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000123
Chui Tey5d2af632002-05-26 13:36:41 +0000124objecttable = {}
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000125request_queue = queue.Queue(0)
126response_queue = queue.Queue(0)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000127
Chui Tey5d2af632002-05-26 13:36:41 +0000128
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000129class SocketIO(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000130
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000131 nextseq = 0
132
Chui Tey5d2af632002-05-26 13:36:41 +0000133 def __init__(self, sock, objtable=None, debugging=None):
Benjamin Petersonb03ca4b2008-06-13 02:00:47 +0000134 self.sockthread = threading.current_thread()
Chui Tey5d2af632002-05-26 13:36:41 +0000135 if debugging is not None:
136 self.debugging = debugging
137 self.sock = sock
138 if objtable is None:
139 objtable = objecttable
140 self.objtable = objtable
Chui Tey5d2af632002-05-26 13:36:41 +0000141 self.responses = {}
142 self.cvars = {}
143
144 def close(self):
145 sock = self.sock
146 self.sock = None
147 if sock is not None:
148 sock.close()
149
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000150 def exithook(self):
151 "override for specific exit action"
Roger Serwy71c9e1a2013-03-31 01:00:15 -0500152 os._exit(0)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000153
Chui Tey5d2af632002-05-26 13:36:41 +0000154 def debug(self, *args):
155 if not self.debugging:
156 return
Amaury Forgeot d'Arcbed17102008-11-29 01:48:47 +0000157 s = self.location + " " + str(threading.current_thread().name)
Chui Tey5d2af632002-05-26 13:36:41 +0000158 for a in args:
159 s = s + " " + str(a)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000160 print(s, file=sys.__stderr__)
Chui Tey5d2af632002-05-26 13:36:41 +0000161
162 def register(self, oid, object):
163 self.objtable[oid] = object
164
165 def unregister(self, oid):
166 try:
167 del self.objtable[oid]
168 except KeyError:
169 pass
170
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000171 def localcall(self, seq, request):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000172 self.debug("localcall:", request)
Chui Tey5d2af632002-05-26 13:36:41 +0000173 try:
174 how, (oid, methodname, args, kwargs) = request
175 except TypeError:
176 return ("ERROR", "Bad request format")
Guido van Rossum811c4e02006-08-22 15:45:46 +0000177 if oid not in self.objtable:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000178 return ("ERROR", "Unknown object id: %r" % (oid,))
Chui Tey5d2af632002-05-26 13:36:41 +0000179 obj = self.objtable[oid]
180 if methodname == "__methods__":
181 methods = {}
182 _getmethods(obj, methods)
183 return ("OK", methods)
184 if methodname == "__attributes__":
185 attributes = {}
186 _getattributes(obj, attributes)
187 return ("OK", attributes)
188 if not hasattr(obj, methodname):
Walter Dörwald70a6b492004-02-12 17:35:32 +0000189 return ("ERROR", "Unsupported method name: %r" % (methodname,))
Chui Tey5d2af632002-05-26 13:36:41 +0000190 method = getattr(obj, methodname)
191 try:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000192 if how == 'CALL':
193 ret = method(*args, **kwargs)
194 if isinstance(ret, RemoteObject):
195 ret = remoteref(ret)
196 return ("OK", ret)
197 elif how == 'QUEUE':
198 request_queue.put((seq, (method, args, kwargs)))
199 return("QUEUED", None)
200 else:
201 return ("ERROR", "Unsupported message type: %s" % how)
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000202 except SystemExit:
203 raise
Andrew Svetlov05bab932012-03-14 13:22:12 -0700204 except KeyboardInterrupt:
205 raise
Andrew Svetlov0832af62012-12-18 23:10:48 +0200206 except OSError:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000207 raise
Andrew Svetlov05bab932012-03-14 13:22:12 -0700208 except Exception as ex:
209 return ("CALLEXC", ex)
Chui Tey5d2af632002-05-26 13:36:41 +0000210 except:
Kurt B. Kaisere852c192004-12-23 04:39:55 +0000211 msg = "*** Internal Error: rpc.py:SocketIO.localcall()\n\n"\
212 " Object: %s \n Method: %s \n Args: %s\n"
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000213 print(msg % (oid, method, args), file=sys.__stderr__)
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000214 traceback.print_exc(file=sys.__stderr__)
Kurt B. Kaiser8cd0def2003-01-31 05:06:43 +0000215 return ("EXCEPTION", None)
216
Chui Tey5d2af632002-05-26 13:36:41 +0000217 def remotecall(self, oid, methodname, args, kwargs):
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000218 self.debug("remotecall:asynccall: ", oid, methodname)
Chui Tey5d2af632002-05-26 13:36:41 +0000219 seq = self.asynccall(oid, methodname, args, kwargs)
Kurt B. Kaiser0930c432002-12-06 21:45:24 +0000220 return self.asyncreturn(seq)
Chui Tey5d2af632002-05-26 13:36:41 +0000221
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000222 def remotequeue(self, oid, methodname, args, kwargs):
223 self.debug("remotequeue:asyncqueue: ", oid, methodname)
224 seq = self.asyncqueue(oid, methodname, args, kwargs)
225 return self.asyncreturn(seq)
226
Chui Tey5d2af632002-05-26 13:36:41 +0000227 def asynccall(self, oid, methodname, args, kwargs):
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000228 request = ("CALL", (oid, methodname, args, kwargs))
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000229 seq = self.newseq()
Benjamin Petersonb03ca4b2008-06-13 02:00:47 +0000230 if threading.current_thread() != self.sockthread:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000231 cvar = threading.Condition()
232 self.cvars[seq] = cvar
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000233 self.debug(("asynccall:%d:" % seq), oid, methodname, args, kwargs)
234 self.putmessage((seq, request))
Chui Tey5d2af632002-05-26 13:36:41 +0000235 return seq
236
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000237 def asyncqueue(self, oid, methodname, args, kwargs):
238 request = ("QUEUE", (oid, methodname, args, kwargs))
239 seq = self.newseq()
Benjamin Petersonb03ca4b2008-06-13 02:00:47 +0000240 if threading.current_thread() != self.sockthread:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000241 cvar = threading.Condition()
242 self.cvars[seq] = cvar
243 self.debug(("asyncqueue:%d:" % seq), oid, methodname, args, kwargs)
244 self.putmessage((seq, request))
245 return seq
246
Chui Tey5d2af632002-05-26 13:36:41 +0000247 def asyncreturn(self, seq):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000248 self.debug("asyncreturn:%d:call getresponse(): " % seq)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000249 response = self.getresponse(seq, wait=0.05)
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000250 self.debug(("asyncreturn:%d:response: " % seq), response)
Chui Tey5d2af632002-05-26 13:36:41 +0000251 return self.decoderesponse(response)
252
253 def decoderesponse(self, response):
254 how, what = response
255 if how == "OK":
256 return what
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000257 if how == "QUEUED":
258 return None
Chui Tey5d2af632002-05-26 13:36:41 +0000259 if how == "EXCEPTION":
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000260 self.debug("decoderesponse: EXCEPTION")
261 return None
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000262 if how == "EOF":
263 self.debug("decoderesponse: EOF")
264 self.decode_interrupthook()
265 return None
Chui Tey5d2af632002-05-26 13:36:41 +0000266 if how == "ERROR":
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000267 self.debug("decoderesponse: Internal ERROR:", what)
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000268 raise RuntimeError(what)
Andrew Svetlov05bab932012-03-14 13:22:12 -0700269 if how == "CALLEXC":
270 self.debug("decoderesponse: Call Exception:", what)
271 raise what
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000272 raise SystemError(how, what)
Chui Tey5d2af632002-05-26 13:36:41 +0000273
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000274 def decode_interrupthook(self):
275 ""
276 raise EOFError
277
Chui Tey5d2af632002-05-26 13:36:41 +0000278 def mainloop(self):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000279 """Listen on socket until I/O not ready or EOF
280
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000281 pollresponse() will loop looking for seq number None, which
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000282 never comes, and exit on EOFError.
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000283
284 """
Chui Tey5d2af632002-05-26 13:36:41 +0000285 try:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000286 self.getresponse(myseq=None, wait=0.05)
Chui Tey5d2af632002-05-26 13:36:41 +0000287 except EOFError:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000288 self.debug("mainloop:return")
289 return
Chui Tey5d2af632002-05-26 13:36:41 +0000290
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000291 def getresponse(self, myseq, wait):
292 response = self._getresponse(myseq, wait)
Chui Tey5d2af632002-05-26 13:36:41 +0000293 if response is not None:
294 how, what = response
295 if how == "OK":
296 response = how, self._proxify(what)
297 return response
298
299 def _proxify(self, obj):
300 if isinstance(obj, RemoteProxy):
301 return RPCProxy(self, obj.oid)
Guido van Rossum13257902007-06-07 23:15:56 +0000302 if isinstance(obj, list):
Kurt B. Kaiser66aaf742007-08-09 18:00:23 +0000303 return list(map(self._proxify, obj))
Chui Tey5d2af632002-05-26 13:36:41 +0000304 # XXX Check for other types -- not currently needed
305 return obj
306
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000307 def _getresponse(self, myseq, wait):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000308 self.debug("_getresponse:myseq:", myseq)
Benjamin Petersonb03ca4b2008-06-13 02:00:47 +0000309 if threading.current_thread() is self.sockthread:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000310 # this thread does all reading of requests or responses
Chui Tey5d2af632002-05-26 13:36:41 +0000311 while 1:
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000312 response = self.pollresponse(myseq, wait)
Chui Tey5d2af632002-05-26 13:36:41 +0000313 if response is not None:
314 return response
315 else:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000316 # wait for notification from socket handling thread
317 cvar = self.cvars[myseq]
318 cvar.acquire()
Guido van Rossum811c4e02006-08-22 15:45:46 +0000319 while myseq not in self.responses:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000320 cvar.wait()
Chui Tey5d2af632002-05-26 13:36:41 +0000321 response = self.responses[myseq]
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000322 self.debug("_getresponse:%s: thread woke up: response: %s" %
323 (myseq, response))
Chui Tey5d2af632002-05-26 13:36:41 +0000324 del self.responses[myseq]
325 del self.cvars[myseq]
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000326 cvar.release()
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000327 return response
Chui Tey5d2af632002-05-26 13:36:41 +0000328
329 def newseq(self):
330 self.nextseq = seq = self.nextseq + 2
331 return seq
332
333 def putmessage(self, message):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000334 self.debug("putmessage:%d:" % message[0])
Chui Tey5d2af632002-05-26 13:36:41 +0000335 try:
Terry Jan Reedyad0c57f2014-10-10 19:33:45 -0400336 s = dumps(message)
Kurt B. Kaiserd6ab77d2004-01-21 19:21:11 +0000337 except pickle.PicklingError:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000338 print("Cannot pickle:", repr(message), file=sys.__stderr__)
Chui Tey5d2af632002-05-26 13:36:41 +0000339 raise
340 s = struct.pack("<i", len(s)) + s
341 while len(s) > 0:
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000342 try:
Kurt B. Kaiserd6ab77d2004-01-21 19:21:11 +0000343 r, w, x = select.select([], [self.sock], [])
344 n = self.sock.send(s[:BUFSIZE])
Kurt B. Kaiser935ea9a2005-05-10 03:44:24 +0000345 except (AttributeError, TypeError):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200346 raise OSError("socket no longer exists")
Serhiy Storchakaef948692015-05-20 16:15:02 +0300347 s = s[n:]
Chui Tey5d2af632002-05-26 13:36:41 +0000348
Kurt B. Kaiser2d726df2007-08-22 21:33:27 +0000349 buff = b''
Chui Tey5d2af632002-05-26 13:36:41 +0000350 bufneed = 4
351 bufstate = 0 # meaning: 0 => reading count; 1 => reading data
352
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000353 def pollpacket(self, wait):
Chui Tey5d2af632002-05-26 13:36:41 +0000354 self._stage0()
Kurt B. Kaiser2d726df2007-08-22 21:33:27 +0000355 if len(self.buff) < self.bufneed:
Kurt B. Kaiserd6ab77d2004-01-21 19:21:11 +0000356 r, w, x = select.select([self.sock.fileno()], [], [], wait)
357 if len(r) == 0:
Chui Tey5d2af632002-05-26 13:36:41 +0000358 return None
359 try:
360 s = self.sock.recv(BUFSIZE)
Andrew Svetlov0832af62012-12-18 23:10:48 +0200361 except OSError:
Chui Tey5d2af632002-05-26 13:36:41 +0000362 raise EOFError
363 if len(s) == 0:
364 raise EOFError
Kurt B. Kaiser2d726df2007-08-22 21:33:27 +0000365 self.buff += s
Chui Tey5d2af632002-05-26 13:36:41 +0000366 self._stage0()
367 return self._stage1()
368
369 def _stage0(self):
Kurt B. Kaiser2d726df2007-08-22 21:33:27 +0000370 if self.bufstate == 0 and len(self.buff) >= 4:
371 s = self.buff[:4]
372 self.buff = self.buff[4:]
Chui Tey5d2af632002-05-26 13:36:41 +0000373 self.bufneed = struct.unpack("<i", s)[0]
374 self.bufstate = 1
375
376 def _stage1(self):
Kurt B. Kaiser2d726df2007-08-22 21:33:27 +0000377 if self.bufstate == 1 and len(self.buff) >= self.bufneed:
378 packet = self.buff[:self.bufneed]
379 self.buff = self.buff[self.bufneed:]
Chui Tey5d2af632002-05-26 13:36:41 +0000380 self.bufneed = 4
381 self.bufstate = 0
382 return packet
383
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000384 def pollmessage(self, wait):
Chui Tey5d2af632002-05-26 13:36:41 +0000385 packet = self.pollpacket(wait)
386 if packet is None:
387 return None
388 try:
389 message = pickle.loads(packet)
Kurt B. Kaiserd6ab77d2004-01-21 19:21:11 +0000390 except pickle.UnpicklingError:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000391 print("-----------------------", file=sys.__stderr__)
392 print("cannot unpickle packet:", repr(packet), file=sys.__stderr__)
Chui Tey5d2af632002-05-26 13:36:41 +0000393 traceback.print_stack(file=sys.__stderr__)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000394 print("-----------------------", file=sys.__stderr__)
Chui Tey5d2af632002-05-26 13:36:41 +0000395 raise
396 return message
397
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000398 def pollresponse(self, myseq, wait):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000399 """Handle messages received on the socket.
400
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000401 Some messages received may be asynchronous 'call' or 'queue' requests,
402 and some may be responses for other threads.
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000403
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000404 'call' requests are passed to self.localcall() with the expectation of
405 immediate execution, during which time the socket is not serviced.
406
407 'queue' requests are used for tasks (which may block or hang) to be
408 processed in a different thread. These requests are fed into
409 request_queue by self.localcall(). Responses to queued requests are
410 taken from response_queue and sent across the link with the associated
411 sequence numbers. Messages in the queues are (sequence_number,
412 request/response) tuples and code using this module removing messages
413 from the request_queue is responsible for returning the correct
414 sequence number in the response_queue.
415
416 pollresponse() will loop until a response message with the myseq
417 sequence number is received, and will save other responses in
418 self.responses and notify the owning thread.
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000419
420 """
Chui Tey5d2af632002-05-26 13:36:41 +0000421 while 1:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000422 # send queued response if there is one available
423 try:
424 qmsg = response_queue.get(0)
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000425 except queue.Empty:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000426 pass
427 else:
428 seq, response = qmsg
429 message = (seq, ('OK', response))
430 self.putmessage(message)
431 # poll for message on link
432 try:
433 message = self.pollmessage(wait)
434 if message is None: # socket not ready
435 return None
436 except EOFError:
437 self.handle_EOF()
Chui Tey5d2af632002-05-26 13:36:41 +0000438 return None
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000439 except AttributeError:
440 return None
Chui Tey5d2af632002-05-26 13:36:41 +0000441 seq, resq = message
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000442 how = resq[0]
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000443 self.debug("pollresponse:%d:myseq:%s" % (seq, myseq))
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000444 # process or queue a request
445 if how in ("CALL", "QUEUE"):
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000446 self.debug("pollresponse:%d:localcall:call:" % seq)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000447 response = self.localcall(seq, resq)
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000448 self.debug("pollresponse:%d:localcall:response:%s"
449 % (seq, response))
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000450 if how == "CALL":
451 self.putmessage((seq, response))
452 elif how == "QUEUE":
453 # don't acknowledge the 'queue' request!
454 pass
Chui Tey5d2af632002-05-26 13:36:41 +0000455 continue
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000456 # return if completed message transaction
Chui Tey5d2af632002-05-26 13:36:41 +0000457 elif seq == myseq:
458 return resq
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000459 # must be a response for a different thread:
Chui Tey5d2af632002-05-26 13:36:41 +0000460 else:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000461 cv = self.cvars.get(seq, None)
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000462 # response involving unknown sequence number is discarded,
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000463 # probably intended for prior incarnation of server
Chui Tey5d2af632002-05-26 13:36:41 +0000464 if cv is not None:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000465 cv.acquire()
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000466 self.responses[seq] = resq
Chui Tey5d2af632002-05-26 13:36:41 +0000467 cv.notify()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000468 cv.release()
Chui Tey5d2af632002-05-26 13:36:41 +0000469 continue
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000470
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000471 def handle_EOF(self):
472 "action taken upon link being closed by peer"
473 self.EOFhook()
474 self.debug("handle_EOF")
475 for key in self.cvars:
476 cv = self.cvars[key]
477 cv.acquire()
478 self.responses[key] = ('EOF', None)
479 cv.notify()
480 cv.release()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000481 # call our (possibly overridden) exit function
482 self.exithook()
483
484 def EOFhook(self):
485 "Classes using rpc client/server can override to augment EOF action"
486 pass
487
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000488#----------------- end class SocketIO --------------------
Chui Tey5d2af632002-05-26 13:36:41 +0000489
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000490class RemoteObject(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000491 # Token mix-in class
492 pass
493
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400494
Chui Tey5d2af632002-05-26 13:36:41 +0000495def remoteref(obj):
496 oid = id(obj)
497 objecttable[oid] = obj
498 return RemoteProxy(oid)
499
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400500
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000501class RemoteProxy(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000502
503 def __init__(self, oid):
504 self.oid = oid
505
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400506
Alexandre Vassalottice261952008-05-12 02:31:37 +0000507class RPCHandler(socketserver.BaseRequestHandler, SocketIO):
Chui Tey5d2af632002-05-26 13:36:41 +0000508
Kurt B. Kaiser0930c432002-12-06 21:45:24 +0000509 debugging = False
510 location = "#S" # Server
Chui Tey5d2af632002-05-26 13:36:41 +0000511
512 def __init__(self, sock, addr, svr):
513 svr.current_handler = self ## cgt xxx
514 SocketIO.__init__(self, sock)
Alexandre Vassalottice261952008-05-12 02:31:37 +0000515 socketserver.BaseRequestHandler.__init__(self, sock, addr, svr)
Chui Tey5d2af632002-05-26 13:36:41 +0000516
Chui Tey5d2af632002-05-26 13:36:41 +0000517 def handle(self):
Alexandre Vassalottice261952008-05-12 02:31:37 +0000518 "handle() method required by socketserver"
Chui Tey5d2af632002-05-26 13:36:41 +0000519 self.mainloop()
520
521 def get_remote_proxy(self, oid):
522 return RPCProxy(self, oid)
523
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400524
Chui Tey5d2af632002-05-26 13:36:41 +0000525class RPCClient(SocketIO):
526
Kurt B. Kaiser0930c432002-12-06 21:45:24 +0000527 debugging = False
528 location = "#C" # Client
529
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000530 nextseq = 1 # Requests coming from the client are odd numbered
Chui Tey5d2af632002-05-26 13:36:41 +0000531
532 def __init__(self, address, family=socket.AF_INET, type=socket.SOCK_STREAM):
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000533 self.listening_sock = socket.socket(family, type)
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000534 self.listening_sock.bind(address)
535 self.listening_sock.listen(1)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000536
537 def accept(self):
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000538 working_sock, address = self.listening_sock.accept()
Kurt B. Kaiser74d93c82002-12-23 22:51:03 +0000539 if self.debugging:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000540 print("****** Connection request from ", address, file=sys.__stderr__)
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +0000541 if address[0] == LOCALHOST:
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000542 SocketIO.__init__(self, working_sock)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000543 else:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000544 print("** Invalid host: ", address, file=sys.__stderr__)
Andrew Svetlov0832af62012-12-18 23:10:48 +0200545 raise OSError
Chui Tey5d2af632002-05-26 13:36:41 +0000546
547 def get_remote_proxy(self, oid):
548 return RPCProxy(self, oid)
549
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400550
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000551class RPCProxy(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000552
553 __methods = None
554 __attributes = None
555
556 def __init__(self, sockio, oid):
557 self.sockio = sockio
558 self.oid = oid
559
560 def __getattr__(self, name):
561 if self.__methods is None:
562 self.__getmethods()
563 if self.__methods.get(name):
564 return MethodProxy(self.sockio, self.oid, name)
565 if self.__attributes is None:
566 self.__getattributes()
Guido van Rossum811c4e02006-08-22 15:45:46 +0000567 if name in self.__attributes:
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000568 value = self.sockio.remotecall(self.oid, '__getattribute__',
569 (name,), {})
570 return value
571 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000572 raise AttributeError(name)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000573
Chui Tey5d2af632002-05-26 13:36:41 +0000574 def __getattributes(self):
575 self.__attributes = self.sockio.remotecall(self.oid,
576 "__attributes__", (), {})
577
578 def __getmethods(self):
579 self.__methods = self.sockio.remotecall(self.oid,
580 "__methods__", (), {})
581
582def _getmethods(obj, methods):
583 # Helper to get a list of methods from an object
584 # Adds names to dictionary argument 'methods'
585 for name in dir(obj):
586 attr = getattr(obj, name)
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200587 if callable(attr):
Chui Tey5d2af632002-05-26 13:36:41 +0000588 methods[name] = 1
Guido van Rossum13257902007-06-07 23:15:56 +0000589 if isinstance(obj, type):
Chui Tey5d2af632002-05-26 13:36:41 +0000590 for super in obj.__bases__:
591 _getmethods(super, methods)
592
593def _getattributes(obj, attributes):
594 for name in dir(obj):
595 attr = getattr(obj, name)
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200596 if not callable(attr):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000597 attributes[name] = 1
Chui Tey5d2af632002-05-26 13:36:41 +0000598
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400599
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000600class MethodProxy(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000601
602 def __init__(self, sockio, oid, name):
603 self.sockio = sockio
604 self.oid = oid
605 self.name = name
606
607 def __call__(self, *args, **kwargs):
608 value = self.sockio.remotecall(self.oid, self.name, args, kwargs)
609 return value
610
Chui Tey5d2af632002-05-26 13:36:41 +0000611
Kurt B. Kaiser62685d32003-09-10 02:42:18 +0000612# XXX KBK 09Sep03 We need a proper unit test for this module. Previously
Benjamin Peterson8719ad52009-09-11 22:24:02 +0000613# existing test code was removed at Rev 1.27 (r34098).
Andrew Svetlovcd49d532012-03-25 11:43:02 +0300614
615def displayhook(value):
616 """Override standard display hook to use non-locale encoding"""
617 if value is None:
618 return
619 # Set '_' to None to avoid recursion
620 builtins._ = None
621 text = repr(value)
622 try:
623 sys.stdout.write(text)
624 except UnicodeEncodeError:
625 # let's use ascii while utf8-bmp codec doesn't present
626 encoding = 'ascii'
627 bytes = text.encode(encoding, 'backslashreplace')
628 text = bytes.decode(encoding, 'strict')
629 sys.stdout.write(text)
630 sys.stdout.write("\n")
631 builtins._ = value
Terry Jan Reedy4d921582018-06-19 19:12:52 -0400632
633
634if __name__ == '__main__':
635 from unittest import main
636 main('idlelib.idle_test.test_rpc', verbosity=2,)