blob: aa8cbd36c47926daeab57a0ecce2a3204504e9f6 [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):
Serhiy Storchakada084702019-03-27 08:02:28 +020067 dispatch_table = {types.CodeType: pickle_code, **copyreg.dispatch_table}
Chui Tey5d2af632002-05-26 13:36:41 +000068
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040069
Chui Tey5d2af632002-05-26 13:36:41 +000070BUFSIZE = 8*1024
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +000071LOCALHOST = '127.0.0.1'
Chui Tey5d2af632002-05-26 13:36:41 +000072
Alexandre Vassalottice261952008-05-12 02:31:37 +000073class RPCServer(socketserver.TCPServer):
Chui Tey5d2af632002-05-26 13:36:41 +000074
75 def __init__(self, addr, handlerclass=None):
76 if handlerclass is None:
77 handlerclass = RPCHandler
Alexandre Vassalottice261952008-05-12 02:31:37 +000078 socketserver.TCPServer.__init__(self, addr, handlerclass)
Chui Tey5d2af632002-05-26 13:36:41 +000079
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000080 def server_bind(self):
81 "Override TCPServer method, no bind() phase for connecting entity"
82 pass
Chui Tey5d2af632002-05-26 13:36:41 +000083
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000084 def server_activate(self):
85 """Override TCPServer method, connect() instead of listen()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000086
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000087 Due to the reversed connection, self.server_address is actually the
88 address of the Idle Client to which we are connecting.
Chui Tey5d2af632002-05-26 13:36:41 +000089
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000090 """
91 self.socket.connect(self.server_address)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000092
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000093 def get_request(self):
94 "Override TCPServer method, return already connected socket"
95 return self.socket, self.server_address
Chui Tey5d2af632002-05-26 13:36:41 +000096
Kurt B. Kaiser003091c2003-02-17 18:57:16 +000097 def handle_error(self, request, client_address):
Kurt B. Kaisere51529d2003-03-22 19:15:58 +000098 """Override TCPServer method
99
100 Error message goes to __stderr__. No error message if exiting
101 normally or socket raised EOF. Other exceptions not handled in
102 server code will cause os._exit.
103
104 """
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000105 try:
106 raise
107 except SystemExit:
108 raise
Kurt B. Kaisere51529d2003-03-22 19:15:58 +0000109 except:
Kurt B. Kaiser05293772003-03-22 20:11:14 +0000110 erf = sys.__stderr__
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000111 print('\n' + '-'*40, file=erf)
112 print('Unhandled server exception!', file=erf)
Amaury Forgeot d'Arcbed17102008-11-29 01:48:47 +0000113 print('Thread: %s' % threading.current_thread().name, file=erf)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000114 print('Client Address: ', client_address, file=erf)
115 print('Request: ', repr(request), file=erf)
Kurt B. Kaisere51529d2003-03-22 19:15:58 +0000116 traceback.print_exc(file=erf)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000117 print('\n*** Unrecoverable, server exiting!', file=erf)
118 print('-'*40, file=erf)
Kurt B. Kaiser05293772003-03-22 20:11:14 +0000119 os._exit(0)
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000120
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000121#----------------- end class RPCServer --------------------
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000122
Chui Tey5d2af632002-05-26 13:36:41 +0000123objecttable = {}
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000124request_queue = queue.Queue(0)
125response_queue = queue.Queue(0)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000126
Chui Tey5d2af632002-05-26 13:36:41 +0000127
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000128class SocketIO(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000129
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000130 nextseq = 0
131
Chui Tey5d2af632002-05-26 13:36:41 +0000132 def __init__(self, sock, objtable=None, debugging=None):
Benjamin Petersonb03ca4b2008-06-13 02:00:47 +0000133 self.sockthread = threading.current_thread()
Chui Tey5d2af632002-05-26 13:36:41 +0000134 if debugging is not None:
135 self.debugging = debugging
136 self.sock = sock
137 if objtable is None:
138 objtable = objecttable
139 self.objtable = objtable
Chui Tey5d2af632002-05-26 13:36:41 +0000140 self.responses = {}
141 self.cvars = {}
142
143 def close(self):
144 sock = self.sock
145 self.sock = None
146 if sock is not None:
147 sock.close()
148
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000149 def exithook(self):
150 "override for specific exit action"
Roger Serwy71c9e1a2013-03-31 01:00:15 -0500151 os._exit(0)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000152
Chui Tey5d2af632002-05-26 13:36:41 +0000153 def debug(self, *args):
154 if not self.debugging:
155 return
Amaury Forgeot d'Arcbed17102008-11-29 01:48:47 +0000156 s = self.location + " " + str(threading.current_thread().name)
Chui Tey5d2af632002-05-26 13:36:41 +0000157 for a in args:
158 s = s + " " + str(a)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000159 print(s, file=sys.__stderr__)
Chui Tey5d2af632002-05-26 13:36:41 +0000160
161 def register(self, oid, object):
162 self.objtable[oid] = object
163
164 def unregister(self, oid):
165 try:
166 del self.objtable[oid]
167 except KeyError:
168 pass
169
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000170 def localcall(self, seq, request):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000171 self.debug("localcall:", request)
Chui Tey5d2af632002-05-26 13:36:41 +0000172 try:
173 how, (oid, methodname, args, kwargs) = request
174 except TypeError:
175 return ("ERROR", "Bad request format")
Guido van Rossum811c4e02006-08-22 15:45:46 +0000176 if oid not in self.objtable:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000177 return ("ERROR", "Unknown object id: %r" % (oid,))
Chui Tey5d2af632002-05-26 13:36:41 +0000178 obj = self.objtable[oid]
179 if methodname == "__methods__":
180 methods = {}
181 _getmethods(obj, methods)
182 return ("OK", methods)
183 if methodname == "__attributes__":
184 attributes = {}
185 _getattributes(obj, attributes)
186 return ("OK", attributes)
187 if not hasattr(obj, methodname):
Walter Dörwald70a6b492004-02-12 17:35:32 +0000188 return ("ERROR", "Unsupported method name: %r" % (methodname,))
Chui Tey5d2af632002-05-26 13:36:41 +0000189 method = getattr(obj, methodname)
190 try:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000191 if how == 'CALL':
192 ret = method(*args, **kwargs)
193 if isinstance(ret, RemoteObject):
194 ret = remoteref(ret)
195 return ("OK", ret)
196 elif how == 'QUEUE':
197 request_queue.put((seq, (method, args, kwargs)))
198 return("QUEUED", None)
199 else:
200 return ("ERROR", "Unsupported message type: %s" % how)
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000201 except SystemExit:
202 raise
Andrew Svetlov05bab932012-03-14 13:22:12 -0700203 except KeyboardInterrupt:
204 raise
Andrew Svetlov0832af62012-12-18 23:10:48 +0200205 except OSError:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000206 raise
Andrew Svetlov05bab932012-03-14 13:22:12 -0700207 except Exception as ex:
208 return ("CALLEXC", ex)
Chui Tey5d2af632002-05-26 13:36:41 +0000209 except:
Kurt B. Kaisere852c192004-12-23 04:39:55 +0000210 msg = "*** Internal Error: rpc.py:SocketIO.localcall()\n\n"\
211 " Object: %s \n Method: %s \n Args: %s\n"
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000212 print(msg % (oid, method, args), file=sys.__stderr__)
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000213 traceback.print_exc(file=sys.__stderr__)
Kurt B. Kaiser8cd0def2003-01-31 05:06:43 +0000214 return ("EXCEPTION", None)
215
Chui Tey5d2af632002-05-26 13:36:41 +0000216 def remotecall(self, oid, methodname, args, kwargs):
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000217 self.debug("remotecall:asynccall: ", oid, methodname)
Chui Tey5d2af632002-05-26 13:36:41 +0000218 seq = self.asynccall(oid, methodname, args, kwargs)
Kurt B. Kaiser0930c432002-12-06 21:45:24 +0000219 return self.asyncreturn(seq)
Chui Tey5d2af632002-05-26 13:36:41 +0000220
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000221 def remotequeue(self, oid, methodname, args, kwargs):
222 self.debug("remotequeue:asyncqueue: ", oid, methodname)
223 seq = self.asyncqueue(oid, methodname, args, kwargs)
224 return self.asyncreturn(seq)
225
Chui Tey5d2af632002-05-26 13:36:41 +0000226 def asynccall(self, oid, methodname, args, kwargs):
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000227 request = ("CALL", (oid, methodname, args, kwargs))
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000228 seq = self.newseq()
Benjamin Petersonb03ca4b2008-06-13 02:00:47 +0000229 if threading.current_thread() != self.sockthread:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000230 cvar = threading.Condition()
231 self.cvars[seq] = cvar
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000232 self.debug(("asynccall:%d:" % seq), oid, methodname, args, kwargs)
233 self.putmessage((seq, request))
Chui Tey5d2af632002-05-26 13:36:41 +0000234 return seq
235
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000236 def asyncqueue(self, oid, methodname, args, kwargs):
237 request = ("QUEUE", (oid, methodname, args, kwargs))
238 seq = self.newseq()
Benjamin Petersonb03ca4b2008-06-13 02:00:47 +0000239 if threading.current_thread() != self.sockthread:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000240 cvar = threading.Condition()
241 self.cvars[seq] = cvar
242 self.debug(("asyncqueue:%d:" % seq), oid, methodname, args, kwargs)
243 self.putmessage((seq, request))
244 return seq
245
Chui Tey5d2af632002-05-26 13:36:41 +0000246 def asyncreturn(self, seq):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000247 self.debug("asyncreturn:%d:call getresponse(): " % seq)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000248 response = self.getresponse(seq, wait=0.05)
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000249 self.debug(("asyncreturn:%d:response: " % seq), response)
Chui Tey5d2af632002-05-26 13:36:41 +0000250 return self.decoderesponse(response)
251
252 def decoderesponse(self, response):
253 how, what = response
254 if how == "OK":
255 return what
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000256 if how == "QUEUED":
257 return None
Chui Tey5d2af632002-05-26 13:36:41 +0000258 if how == "EXCEPTION":
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000259 self.debug("decoderesponse: EXCEPTION")
260 return None
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000261 if how == "EOF":
262 self.debug("decoderesponse: EOF")
263 self.decode_interrupthook()
264 return None
Chui Tey5d2af632002-05-26 13:36:41 +0000265 if how == "ERROR":
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000266 self.debug("decoderesponse: Internal ERROR:", what)
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000267 raise RuntimeError(what)
Andrew Svetlov05bab932012-03-14 13:22:12 -0700268 if how == "CALLEXC":
269 self.debug("decoderesponse: Call Exception:", what)
270 raise what
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000271 raise SystemError(how, what)
Chui Tey5d2af632002-05-26 13:36:41 +0000272
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000273 def decode_interrupthook(self):
274 ""
275 raise EOFError
276
Chui Tey5d2af632002-05-26 13:36:41 +0000277 def mainloop(self):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000278 """Listen on socket until I/O not ready or EOF
279
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000280 pollresponse() will loop looking for seq number None, which
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000281 never comes, and exit on EOFError.
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000282
283 """
Chui Tey5d2af632002-05-26 13:36:41 +0000284 try:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000285 self.getresponse(myseq=None, wait=0.05)
Chui Tey5d2af632002-05-26 13:36:41 +0000286 except EOFError:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000287 self.debug("mainloop:return")
288 return
Chui Tey5d2af632002-05-26 13:36:41 +0000289
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000290 def getresponse(self, myseq, wait):
291 response = self._getresponse(myseq, wait)
Chui Tey5d2af632002-05-26 13:36:41 +0000292 if response is not None:
293 how, what = response
294 if how == "OK":
295 response = how, self._proxify(what)
296 return response
297
298 def _proxify(self, obj):
299 if isinstance(obj, RemoteProxy):
300 return RPCProxy(self, obj.oid)
Guido van Rossum13257902007-06-07 23:15:56 +0000301 if isinstance(obj, list):
Kurt B. Kaiser66aaf742007-08-09 18:00:23 +0000302 return list(map(self._proxify, obj))
Chui Tey5d2af632002-05-26 13:36:41 +0000303 # XXX Check for other types -- not currently needed
304 return obj
305
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000306 def _getresponse(self, myseq, wait):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000307 self.debug("_getresponse:myseq:", myseq)
Benjamin Petersonb03ca4b2008-06-13 02:00:47 +0000308 if threading.current_thread() is self.sockthread:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000309 # this thread does all reading of requests or responses
Chui Tey5d2af632002-05-26 13:36:41 +0000310 while 1:
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000311 response = self.pollresponse(myseq, wait)
Chui Tey5d2af632002-05-26 13:36:41 +0000312 if response is not None:
313 return response
314 else:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000315 # wait for notification from socket handling thread
316 cvar = self.cvars[myseq]
317 cvar.acquire()
Guido van Rossum811c4e02006-08-22 15:45:46 +0000318 while myseq not in self.responses:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000319 cvar.wait()
Chui Tey5d2af632002-05-26 13:36:41 +0000320 response = self.responses[myseq]
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000321 self.debug("_getresponse:%s: thread woke up: response: %s" %
322 (myseq, response))
Chui Tey5d2af632002-05-26 13:36:41 +0000323 del self.responses[myseq]
324 del self.cvars[myseq]
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000325 cvar.release()
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000326 return response
Chui Tey5d2af632002-05-26 13:36:41 +0000327
328 def newseq(self):
329 self.nextseq = seq = self.nextseq + 2
330 return seq
331
332 def putmessage(self, message):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000333 self.debug("putmessage:%d:" % message[0])
Chui Tey5d2af632002-05-26 13:36:41 +0000334 try:
Terry Jan Reedyad0c57f2014-10-10 19:33:45 -0400335 s = dumps(message)
Kurt B. Kaiserd6ab77d2004-01-21 19:21:11 +0000336 except pickle.PicklingError:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000337 print("Cannot pickle:", repr(message), file=sys.__stderr__)
Chui Tey5d2af632002-05-26 13:36:41 +0000338 raise
339 s = struct.pack("<i", len(s)) + s
340 while len(s) > 0:
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000341 try:
Kurt B. Kaiserd6ab77d2004-01-21 19:21:11 +0000342 r, w, x = select.select([], [self.sock], [])
343 n = self.sock.send(s[:BUFSIZE])
Kurt B. Kaiser935ea9a2005-05-10 03:44:24 +0000344 except (AttributeError, TypeError):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200345 raise OSError("socket no longer exists")
Serhiy Storchakaef948692015-05-20 16:15:02 +0300346 s = s[n:]
Chui Tey5d2af632002-05-26 13:36:41 +0000347
Kurt B. Kaiser2d726df2007-08-22 21:33:27 +0000348 buff = b''
Chui Tey5d2af632002-05-26 13:36:41 +0000349 bufneed = 4
350 bufstate = 0 # meaning: 0 => reading count; 1 => reading data
351
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000352 def pollpacket(self, wait):
Chui Tey5d2af632002-05-26 13:36:41 +0000353 self._stage0()
Kurt B. Kaiser2d726df2007-08-22 21:33:27 +0000354 if len(self.buff) < self.bufneed:
Kurt B. Kaiserd6ab77d2004-01-21 19:21:11 +0000355 r, w, x = select.select([self.sock.fileno()], [], [], wait)
356 if len(r) == 0:
Chui Tey5d2af632002-05-26 13:36:41 +0000357 return None
358 try:
359 s = self.sock.recv(BUFSIZE)
Andrew Svetlov0832af62012-12-18 23:10:48 +0200360 except OSError:
Chui Tey5d2af632002-05-26 13:36:41 +0000361 raise EOFError
362 if len(s) == 0:
363 raise EOFError
Kurt B. Kaiser2d726df2007-08-22 21:33:27 +0000364 self.buff += s
Chui Tey5d2af632002-05-26 13:36:41 +0000365 self._stage0()
366 return self._stage1()
367
368 def _stage0(self):
Kurt B. Kaiser2d726df2007-08-22 21:33:27 +0000369 if self.bufstate == 0 and len(self.buff) >= 4:
370 s = self.buff[:4]
371 self.buff = self.buff[4:]
Chui Tey5d2af632002-05-26 13:36:41 +0000372 self.bufneed = struct.unpack("<i", s)[0]
373 self.bufstate = 1
374
375 def _stage1(self):
Kurt B. Kaiser2d726df2007-08-22 21:33:27 +0000376 if self.bufstate == 1 and len(self.buff) >= self.bufneed:
377 packet = self.buff[:self.bufneed]
378 self.buff = self.buff[self.bufneed:]
Chui Tey5d2af632002-05-26 13:36:41 +0000379 self.bufneed = 4
380 self.bufstate = 0
381 return packet
382
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000383 def pollmessage(self, wait):
Chui Tey5d2af632002-05-26 13:36:41 +0000384 packet = self.pollpacket(wait)
385 if packet is None:
386 return None
387 try:
388 message = pickle.loads(packet)
Kurt B. Kaiserd6ab77d2004-01-21 19:21:11 +0000389 except pickle.UnpicklingError:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000390 print("-----------------------", file=sys.__stderr__)
391 print("cannot unpickle packet:", repr(packet), file=sys.__stderr__)
Chui Tey5d2af632002-05-26 13:36:41 +0000392 traceback.print_stack(file=sys.__stderr__)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000393 print("-----------------------", file=sys.__stderr__)
Chui Tey5d2af632002-05-26 13:36:41 +0000394 raise
395 return message
396
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000397 def pollresponse(self, myseq, wait):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000398 """Handle messages received on the socket.
399
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000400 Some messages received may be asynchronous 'call' or 'queue' requests,
401 and some may be responses for other threads.
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000402
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000403 'call' requests are passed to self.localcall() with the expectation of
404 immediate execution, during which time the socket is not serviced.
405
406 'queue' requests are used for tasks (which may block or hang) to be
407 processed in a different thread. These requests are fed into
408 request_queue by self.localcall(). Responses to queued requests are
409 taken from response_queue and sent across the link with the associated
410 sequence numbers. Messages in the queues are (sequence_number,
411 request/response) tuples and code using this module removing messages
412 from the request_queue is responsible for returning the correct
413 sequence number in the response_queue.
414
415 pollresponse() will loop until a response message with the myseq
416 sequence number is received, and will save other responses in
417 self.responses and notify the owning thread.
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000418
419 """
Chui Tey5d2af632002-05-26 13:36:41 +0000420 while 1:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000421 # send queued response if there is one available
422 try:
423 qmsg = response_queue.get(0)
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000424 except queue.Empty:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000425 pass
426 else:
427 seq, response = qmsg
428 message = (seq, ('OK', response))
429 self.putmessage(message)
430 # poll for message on link
431 try:
432 message = self.pollmessage(wait)
433 if message is None: # socket not ready
434 return None
435 except EOFError:
436 self.handle_EOF()
Chui Tey5d2af632002-05-26 13:36:41 +0000437 return None
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000438 except AttributeError:
439 return None
Chui Tey5d2af632002-05-26 13:36:41 +0000440 seq, resq = message
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000441 how = resq[0]
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000442 self.debug("pollresponse:%d:myseq:%s" % (seq, myseq))
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000443 # process or queue a request
444 if how in ("CALL", "QUEUE"):
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000445 self.debug("pollresponse:%d:localcall:call:" % seq)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000446 response = self.localcall(seq, resq)
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000447 self.debug("pollresponse:%d:localcall:response:%s"
448 % (seq, response))
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000449 if how == "CALL":
450 self.putmessage((seq, response))
451 elif how == "QUEUE":
452 # don't acknowledge the 'queue' request!
453 pass
Chui Tey5d2af632002-05-26 13:36:41 +0000454 continue
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000455 # return if completed message transaction
Chui Tey5d2af632002-05-26 13:36:41 +0000456 elif seq == myseq:
457 return resq
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000458 # must be a response for a different thread:
Chui Tey5d2af632002-05-26 13:36:41 +0000459 else:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000460 cv = self.cvars.get(seq, None)
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000461 # response involving unknown sequence number is discarded,
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000462 # probably intended for prior incarnation of server
Chui Tey5d2af632002-05-26 13:36:41 +0000463 if cv is not None:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000464 cv.acquire()
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000465 self.responses[seq] = resq
Chui Tey5d2af632002-05-26 13:36:41 +0000466 cv.notify()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000467 cv.release()
Chui Tey5d2af632002-05-26 13:36:41 +0000468 continue
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000469
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000470 def handle_EOF(self):
471 "action taken upon link being closed by peer"
472 self.EOFhook()
473 self.debug("handle_EOF")
474 for key in self.cvars:
475 cv = self.cvars[key]
476 cv.acquire()
477 self.responses[key] = ('EOF', None)
478 cv.notify()
479 cv.release()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000480 # call our (possibly overridden) exit function
481 self.exithook()
482
483 def EOFhook(self):
484 "Classes using rpc client/server can override to augment EOF action"
485 pass
486
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000487#----------------- end class SocketIO --------------------
Chui Tey5d2af632002-05-26 13:36:41 +0000488
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000489class RemoteObject(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000490 # Token mix-in class
491 pass
492
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400493
Chui Tey5d2af632002-05-26 13:36:41 +0000494def remoteref(obj):
495 oid = id(obj)
496 objecttable[oid] = obj
497 return RemoteProxy(oid)
498
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400499
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000500class RemoteProxy(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000501
502 def __init__(self, oid):
503 self.oid = oid
504
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400505
Alexandre Vassalottice261952008-05-12 02:31:37 +0000506class RPCHandler(socketserver.BaseRequestHandler, SocketIO):
Chui Tey5d2af632002-05-26 13:36:41 +0000507
Kurt B. Kaiser0930c432002-12-06 21:45:24 +0000508 debugging = False
509 location = "#S" # Server
Chui Tey5d2af632002-05-26 13:36:41 +0000510
511 def __init__(self, sock, addr, svr):
512 svr.current_handler = self ## cgt xxx
513 SocketIO.__init__(self, sock)
Alexandre Vassalottice261952008-05-12 02:31:37 +0000514 socketserver.BaseRequestHandler.__init__(self, sock, addr, svr)
Chui Tey5d2af632002-05-26 13:36:41 +0000515
Chui Tey5d2af632002-05-26 13:36:41 +0000516 def handle(self):
Alexandre Vassalottice261952008-05-12 02:31:37 +0000517 "handle() method required by socketserver"
Chui Tey5d2af632002-05-26 13:36:41 +0000518 self.mainloop()
519
520 def get_remote_proxy(self, oid):
521 return RPCProxy(self, oid)
522
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400523
Chui Tey5d2af632002-05-26 13:36:41 +0000524class RPCClient(SocketIO):
525
Kurt B. Kaiser0930c432002-12-06 21:45:24 +0000526 debugging = False
527 location = "#C" # Client
528
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000529 nextseq = 1 # Requests coming from the client are odd numbered
Chui Tey5d2af632002-05-26 13:36:41 +0000530
531 def __init__(self, address, family=socket.AF_INET, type=socket.SOCK_STREAM):
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000532 self.listening_sock = socket.socket(family, type)
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000533 self.listening_sock.bind(address)
534 self.listening_sock.listen(1)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000535
536 def accept(self):
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000537 working_sock, address = self.listening_sock.accept()
Kurt B. Kaiser74d93c82002-12-23 22:51:03 +0000538 if self.debugging:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000539 print("****** Connection request from ", address, file=sys.__stderr__)
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +0000540 if address[0] == LOCALHOST:
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000541 SocketIO.__init__(self, working_sock)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000542 else:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000543 print("** Invalid host: ", address, file=sys.__stderr__)
Andrew Svetlov0832af62012-12-18 23:10:48 +0200544 raise OSError
Chui Tey5d2af632002-05-26 13:36:41 +0000545
546 def get_remote_proxy(self, oid):
547 return RPCProxy(self, oid)
548
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400549
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000550class RPCProxy(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000551
552 __methods = None
553 __attributes = None
554
555 def __init__(self, sockio, oid):
556 self.sockio = sockio
557 self.oid = oid
558
559 def __getattr__(self, name):
560 if self.__methods is None:
561 self.__getmethods()
562 if self.__methods.get(name):
563 return MethodProxy(self.sockio, self.oid, name)
564 if self.__attributes is None:
565 self.__getattributes()
Guido van Rossum811c4e02006-08-22 15:45:46 +0000566 if name in self.__attributes:
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000567 value = self.sockio.remotecall(self.oid, '__getattribute__',
568 (name,), {})
569 return value
570 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000571 raise AttributeError(name)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000572
Chui Tey5d2af632002-05-26 13:36:41 +0000573 def __getattributes(self):
574 self.__attributes = self.sockio.remotecall(self.oid,
575 "__attributes__", (), {})
576
577 def __getmethods(self):
578 self.__methods = self.sockio.remotecall(self.oid,
579 "__methods__", (), {})
580
581def _getmethods(obj, methods):
582 # Helper to get a list of methods from an object
583 # Adds names to dictionary argument 'methods'
584 for name in dir(obj):
585 attr = getattr(obj, name)
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200586 if callable(attr):
Chui Tey5d2af632002-05-26 13:36:41 +0000587 methods[name] = 1
Guido van Rossum13257902007-06-07 23:15:56 +0000588 if isinstance(obj, type):
Chui Tey5d2af632002-05-26 13:36:41 +0000589 for super in obj.__bases__:
590 _getmethods(super, methods)
591
592def _getattributes(obj, attributes):
593 for name in dir(obj):
594 attr = getattr(obj, name)
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200595 if not callable(attr):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000596 attributes[name] = 1
Chui Tey5d2af632002-05-26 13:36:41 +0000597
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400598
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000599class MethodProxy(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000600
601 def __init__(self, sockio, oid, name):
602 self.sockio = sockio
603 self.oid = oid
604 self.name = name
605
Serhiy Storchaka2085bd02019-06-01 11:00:15 +0300606 def __call__(self, /, *args, **kwargs):
Chui Tey5d2af632002-05-26 13:36:41 +0000607 value = self.sockio.remotecall(self.oid, self.name, args, kwargs)
608 return value
609
Chui Tey5d2af632002-05-26 13:36:41 +0000610
Kurt B. Kaiser62685d32003-09-10 02:42:18 +0000611# XXX KBK 09Sep03 We need a proper unit test for this module. Previously
Benjamin Peterson8719ad52009-09-11 22:24:02 +0000612# existing test code was removed at Rev 1.27 (r34098).
Andrew Svetlovcd49d532012-03-25 11:43:02 +0300613
614def displayhook(value):
615 """Override standard display hook to use non-locale encoding"""
616 if value is None:
617 return
618 # Set '_' to None to avoid recursion
619 builtins._ = None
620 text = repr(value)
621 try:
622 sys.stdout.write(text)
623 except UnicodeEncodeError:
624 # let's use ascii while utf8-bmp codec doesn't present
625 encoding = 'ascii'
626 bytes = text.encode(encoding, 'backslashreplace')
627 text = bytes.decode(encoding, 'strict')
628 sys.stdout.write(text)
629 sys.stdout.write("\n")
630 builtins._ = value
Terry Jan Reedy4d921582018-06-19 19:12:52 -0400631
632
633if __name__ == '__main__':
634 from unittest import main
635 main('idlelib.idle_test.test_rpc', verbosity=2,)