blob: 8f57edb836dec8a7c68a53c191a5d94bd730fc79 [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):
46 co = marshal.loads(ms)
47 assert isinstance(co, types.CodeType)
48 return co
49
50def pickle_code(co):
51 assert isinstance(co, types.CodeType)
52 ms = marshal.dumps(co)
53 return unpickle_code, (ms,)
54
Terry Jan Reedyad0c57f2014-10-10 19:33:45 -040055def dumps(obj, protocol=None):
56 f = io.BytesIO()
57 p = CodePickler(f, protocol)
58 p.dump(obj)
59 return f.getvalue()
Chui Tey5d2af632002-05-26 13:36:41 +000060
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040061
Terry Jan Reedyad0c57f2014-10-10 19:33:45 -040062class CodePickler(pickle.Pickler):
63 dispatch_table = {types.CodeType: pickle_code}
64 dispatch_table.update(copyreg.dispatch_table)
Chui Tey5d2af632002-05-26 13:36:41 +000065
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040066
Chui Tey5d2af632002-05-26 13:36:41 +000067BUFSIZE = 8*1024
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +000068LOCALHOST = '127.0.0.1'
Chui Tey5d2af632002-05-26 13:36:41 +000069
Alexandre Vassalottice261952008-05-12 02:31:37 +000070class RPCServer(socketserver.TCPServer):
Chui Tey5d2af632002-05-26 13:36:41 +000071
72 def __init__(self, addr, handlerclass=None):
73 if handlerclass is None:
74 handlerclass = RPCHandler
Alexandre Vassalottice261952008-05-12 02:31:37 +000075 socketserver.TCPServer.__init__(self, addr, handlerclass)
Chui Tey5d2af632002-05-26 13:36:41 +000076
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000077 def server_bind(self):
78 "Override TCPServer method, no bind() phase for connecting entity"
79 pass
Chui Tey5d2af632002-05-26 13:36:41 +000080
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000081 def server_activate(self):
82 """Override TCPServer method, connect() instead of listen()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000083
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000084 Due to the reversed connection, self.server_address is actually the
85 address of the Idle Client to which we are connecting.
Chui Tey5d2af632002-05-26 13:36:41 +000086
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000087 """
88 self.socket.connect(self.server_address)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000089
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000090 def get_request(self):
91 "Override TCPServer method, return already connected socket"
92 return self.socket, self.server_address
Chui Tey5d2af632002-05-26 13:36:41 +000093
Kurt B. Kaiser003091c2003-02-17 18:57:16 +000094 def handle_error(self, request, client_address):
Kurt B. Kaisere51529d2003-03-22 19:15:58 +000095 """Override TCPServer method
96
97 Error message goes to __stderr__. No error message if exiting
98 normally or socket raised EOF. Other exceptions not handled in
99 server code will cause os._exit.
100
101 """
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000102 try:
103 raise
104 except SystemExit:
105 raise
Kurt B. Kaisere51529d2003-03-22 19:15:58 +0000106 except:
Kurt B. Kaiser05293772003-03-22 20:11:14 +0000107 erf = sys.__stderr__
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000108 print('\n' + '-'*40, file=erf)
109 print('Unhandled server exception!', file=erf)
Amaury Forgeot d'Arcbed17102008-11-29 01:48:47 +0000110 print('Thread: %s' % threading.current_thread().name, file=erf)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000111 print('Client Address: ', client_address, file=erf)
112 print('Request: ', repr(request), file=erf)
Kurt B. Kaisere51529d2003-03-22 19:15:58 +0000113 traceback.print_exc(file=erf)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000114 print('\n*** Unrecoverable, server exiting!', file=erf)
115 print('-'*40, file=erf)
Kurt B. Kaiser05293772003-03-22 20:11:14 +0000116 os._exit(0)
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000117
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000118#----------------- end class RPCServer --------------------
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000119
Chui Tey5d2af632002-05-26 13:36:41 +0000120objecttable = {}
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000121request_queue = queue.Queue(0)
122response_queue = queue.Queue(0)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000123
Chui Tey5d2af632002-05-26 13:36:41 +0000124
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000125class SocketIO(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000126
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000127 nextseq = 0
128
Chui Tey5d2af632002-05-26 13:36:41 +0000129 def __init__(self, sock, objtable=None, debugging=None):
Benjamin Petersonb03ca4b2008-06-13 02:00:47 +0000130 self.sockthread = threading.current_thread()
Chui Tey5d2af632002-05-26 13:36:41 +0000131 if debugging is not None:
132 self.debugging = debugging
133 self.sock = sock
134 if objtable is None:
135 objtable = objecttable
136 self.objtable = objtable
Chui Tey5d2af632002-05-26 13:36:41 +0000137 self.responses = {}
138 self.cvars = {}
139
140 def close(self):
141 sock = self.sock
142 self.sock = None
143 if sock is not None:
144 sock.close()
145
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000146 def exithook(self):
147 "override for specific exit action"
Roger Serwy71c9e1a2013-03-31 01:00:15 -0500148 os._exit(0)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000149
Chui Tey5d2af632002-05-26 13:36:41 +0000150 def debug(self, *args):
151 if not self.debugging:
152 return
Amaury Forgeot d'Arcbed17102008-11-29 01:48:47 +0000153 s = self.location + " " + str(threading.current_thread().name)
Chui Tey5d2af632002-05-26 13:36:41 +0000154 for a in args:
155 s = s + " " + str(a)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000156 print(s, file=sys.__stderr__)
Chui Tey5d2af632002-05-26 13:36:41 +0000157
158 def register(self, oid, object):
159 self.objtable[oid] = object
160
161 def unregister(self, oid):
162 try:
163 del self.objtable[oid]
164 except KeyError:
165 pass
166
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000167 def localcall(self, seq, request):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000168 self.debug("localcall:", request)
Chui Tey5d2af632002-05-26 13:36:41 +0000169 try:
170 how, (oid, methodname, args, kwargs) = request
171 except TypeError:
172 return ("ERROR", "Bad request format")
Guido van Rossum811c4e02006-08-22 15:45:46 +0000173 if oid not in self.objtable:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000174 return ("ERROR", "Unknown object id: %r" % (oid,))
Chui Tey5d2af632002-05-26 13:36:41 +0000175 obj = self.objtable[oid]
176 if methodname == "__methods__":
177 methods = {}
178 _getmethods(obj, methods)
179 return ("OK", methods)
180 if methodname == "__attributes__":
181 attributes = {}
182 _getattributes(obj, attributes)
183 return ("OK", attributes)
184 if not hasattr(obj, methodname):
Walter Dörwald70a6b492004-02-12 17:35:32 +0000185 return ("ERROR", "Unsupported method name: %r" % (methodname,))
Chui Tey5d2af632002-05-26 13:36:41 +0000186 method = getattr(obj, methodname)
187 try:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000188 if how == 'CALL':
189 ret = method(*args, **kwargs)
190 if isinstance(ret, RemoteObject):
191 ret = remoteref(ret)
192 return ("OK", ret)
193 elif how == 'QUEUE':
194 request_queue.put((seq, (method, args, kwargs)))
195 return("QUEUED", None)
196 else:
197 return ("ERROR", "Unsupported message type: %s" % how)
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000198 except SystemExit:
199 raise
Andrew Svetlov05bab932012-03-14 13:22:12 -0700200 except KeyboardInterrupt:
201 raise
Andrew Svetlov0832af62012-12-18 23:10:48 +0200202 except OSError:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000203 raise
Andrew Svetlov05bab932012-03-14 13:22:12 -0700204 except Exception as ex:
205 return ("CALLEXC", ex)
Chui Tey5d2af632002-05-26 13:36:41 +0000206 except:
Kurt B. Kaisere852c192004-12-23 04:39:55 +0000207 msg = "*** Internal Error: rpc.py:SocketIO.localcall()\n\n"\
208 " Object: %s \n Method: %s \n Args: %s\n"
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000209 print(msg % (oid, method, args), file=sys.__stderr__)
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000210 traceback.print_exc(file=sys.__stderr__)
Kurt B. Kaiser8cd0def2003-01-31 05:06:43 +0000211 return ("EXCEPTION", None)
212
Chui Tey5d2af632002-05-26 13:36:41 +0000213 def remotecall(self, oid, methodname, args, kwargs):
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000214 self.debug("remotecall:asynccall: ", oid, methodname)
Chui Tey5d2af632002-05-26 13:36:41 +0000215 seq = self.asynccall(oid, methodname, args, kwargs)
Kurt B. Kaiser0930c432002-12-06 21:45:24 +0000216 return self.asyncreturn(seq)
Chui Tey5d2af632002-05-26 13:36:41 +0000217
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000218 def remotequeue(self, oid, methodname, args, kwargs):
219 self.debug("remotequeue:asyncqueue: ", oid, methodname)
220 seq = self.asyncqueue(oid, methodname, args, kwargs)
221 return self.asyncreturn(seq)
222
Chui Tey5d2af632002-05-26 13:36:41 +0000223 def asynccall(self, oid, methodname, args, kwargs):
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000224 request = ("CALL", (oid, methodname, args, kwargs))
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000225 seq = self.newseq()
Benjamin Petersonb03ca4b2008-06-13 02:00:47 +0000226 if threading.current_thread() != self.sockthread:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000227 cvar = threading.Condition()
228 self.cvars[seq] = cvar
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000229 self.debug(("asynccall:%d:" % seq), oid, methodname, args, kwargs)
230 self.putmessage((seq, request))
Chui Tey5d2af632002-05-26 13:36:41 +0000231 return seq
232
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000233 def asyncqueue(self, oid, methodname, args, kwargs):
234 request = ("QUEUE", (oid, methodname, args, kwargs))
235 seq = self.newseq()
Benjamin Petersonb03ca4b2008-06-13 02:00:47 +0000236 if threading.current_thread() != self.sockthread:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000237 cvar = threading.Condition()
238 self.cvars[seq] = cvar
239 self.debug(("asyncqueue:%d:" % seq), oid, methodname, args, kwargs)
240 self.putmessage((seq, request))
241 return seq
242
Chui Tey5d2af632002-05-26 13:36:41 +0000243 def asyncreturn(self, seq):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000244 self.debug("asyncreturn:%d:call getresponse(): " % seq)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000245 response = self.getresponse(seq, wait=0.05)
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000246 self.debug(("asyncreturn:%d:response: " % seq), response)
Chui Tey5d2af632002-05-26 13:36:41 +0000247 return self.decoderesponse(response)
248
249 def decoderesponse(self, response):
250 how, what = response
251 if how == "OK":
252 return what
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000253 if how == "QUEUED":
254 return None
Chui Tey5d2af632002-05-26 13:36:41 +0000255 if how == "EXCEPTION":
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000256 self.debug("decoderesponse: EXCEPTION")
257 return None
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000258 if how == "EOF":
259 self.debug("decoderesponse: EOF")
260 self.decode_interrupthook()
261 return None
Chui Tey5d2af632002-05-26 13:36:41 +0000262 if how == "ERROR":
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000263 self.debug("decoderesponse: Internal ERROR:", what)
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000264 raise RuntimeError(what)
Andrew Svetlov05bab932012-03-14 13:22:12 -0700265 if how == "CALLEXC":
266 self.debug("decoderesponse: Call Exception:", what)
267 raise what
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000268 raise SystemError(how, what)
Chui Tey5d2af632002-05-26 13:36:41 +0000269
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000270 def decode_interrupthook(self):
271 ""
272 raise EOFError
273
Chui Tey5d2af632002-05-26 13:36:41 +0000274 def mainloop(self):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000275 """Listen on socket until I/O not ready or EOF
276
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000277 pollresponse() will loop looking for seq number None, which
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000278 never comes, and exit on EOFError.
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000279
280 """
Chui Tey5d2af632002-05-26 13:36:41 +0000281 try:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000282 self.getresponse(myseq=None, wait=0.05)
Chui Tey5d2af632002-05-26 13:36:41 +0000283 except EOFError:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000284 self.debug("mainloop:return")
285 return
Chui Tey5d2af632002-05-26 13:36:41 +0000286
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000287 def getresponse(self, myseq, wait):
288 response = self._getresponse(myseq, wait)
Chui Tey5d2af632002-05-26 13:36:41 +0000289 if response is not None:
290 how, what = response
291 if how == "OK":
292 response = how, self._proxify(what)
293 return response
294
295 def _proxify(self, obj):
296 if isinstance(obj, RemoteProxy):
297 return RPCProxy(self, obj.oid)
Guido van Rossum13257902007-06-07 23:15:56 +0000298 if isinstance(obj, list):
Kurt B. Kaiser66aaf742007-08-09 18:00:23 +0000299 return list(map(self._proxify, obj))
Chui Tey5d2af632002-05-26 13:36:41 +0000300 # XXX Check for other types -- not currently needed
301 return obj
302
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000303 def _getresponse(self, myseq, wait):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000304 self.debug("_getresponse:myseq:", myseq)
Benjamin Petersonb03ca4b2008-06-13 02:00:47 +0000305 if threading.current_thread() is self.sockthread:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000306 # this thread does all reading of requests or responses
Chui Tey5d2af632002-05-26 13:36:41 +0000307 while 1:
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000308 response = self.pollresponse(myseq, wait)
Chui Tey5d2af632002-05-26 13:36:41 +0000309 if response is not None:
310 return response
311 else:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000312 # wait for notification from socket handling thread
313 cvar = self.cvars[myseq]
314 cvar.acquire()
Guido van Rossum811c4e02006-08-22 15:45:46 +0000315 while myseq not in self.responses:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000316 cvar.wait()
Chui Tey5d2af632002-05-26 13:36:41 +0000317 response = self.responses[myseq]
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000318 self.debug("_getresponse:%s: thread woke up: response: %s" %
319 (myseq, response))
Chui Tey5d2af632002-05-26 13:36:41 +0000320 del self.responses[myseq]
321 del self.cvars[myseq]
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000322 cvar.release()
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000323 return response
Chui Tey5d2af632002-05-26 13:36:41 +0000324
325 def newseq(self):
326 self.nextseq = seq = self.nextseq + 2
327 return seq
328
329 def putmessage(self, message):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000330 self.debug("putmessage:%d:" % message[0])
Chui Tey5d2af632002-05-26 13:36:41 +0000331 try:
Terry Jan Reedyad0c57f2014-10-10 19:33:45 -0400332 s = dumps(message)
Kurt B. Kaiserd6ab77d2004-01-21 19:21:11 +0000333 except pickle.PicklingError:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000334 print("Cannot pickle:", repr(message), file=sys.__stderr__)
Chui Tey5d2af632002-05-26 13:36:41 +0000335 raise
336 s = struct.pack("<i", len(s)) + s
337 while len(s) > 0:
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000338 try:
Kurt B. Kaiserd6ab77d2004-01-21 19:21:11 +0000339 r, w, x = select.select([], [self.sock], [])
340 n = self.sock.send(s[:BUFSIZE])
Kurt B. Kaiser935ea9a2005-05-10 03:44:24 +0000341 except (AttributeError, TypeError):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200342 raise OSError("socket no longer exists")
Serhiy Storchakaef948692015-05-20 16:15:02 +0300343 s = s[n:]
Chui Tey5d2af632002-05-26 13:36:41 +0000344
Kurt B. Kaiser2d726df2007-08-22 21:33:27 +0000345 buff = b''
Chui Tey5d2af632002-05-26 13:36:41 +0000346 bufneed = 4
347 bufstate = 0 # meaning: 0 => reading count; 1 => reading data
348
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000349 def pollpacket(self, wait):
Chui Tey5d2af632002-05-26 13:36:41 +0000350 self._stage0()
Kurt B. Kaiser2d726df2007-08-22 21:33:27 +0000351 if len(self.buff) < self.bufneed:
Kurt B. Kaiserd6ab77d2004-01-21 19:21:11 +0000352 r, w, x = select.select([self.sock.fileno()], [], [], wait)
353 if len(r) == 0:
Chui Tey5d2af632002-05-26 13:36:41 +0000354 return None
355 try:
356 s = self.sock.recv(BUFSIZE)
Andrew Svetlov0832af62012-12-18 23:10:48 +0200357 except OSError:
Chui Tey5d2af632002-05-26 13:36:41 +0000358 raise EOFError
359 if len(s) == 0:
360 raise EOFError
Kurt B. Kaiser2d726df2007-08-22 21:33:27 +0000361 self.buff += s
Chui Tey5d2af632002-05-26 13:36:41 +0000362 self._stage0()
363 return self._stage1()
364
365 def _stage0(self):
Kurt B. Kaiser2d726df2007-08-22 21:33:27 +0000366 if self.bufstate == 0 and len(self.buff) >= 4:
367 s = self.buff[:4]
368 self.buff = self.buff[4:]
Chui Tey5d2af632002-05-26 13:36:41 +0000369 self.bufneed = struct.unpack("<i", s)[0]
370 self.bufstate = 1
371
372 def _stage1(self):
Kurt B. Kaiser2d726df2007-08-22 21:33:27 +0000373 if self.bufstate == 1 and len(self.buff) >= self.bufneed:
374 packet = self.buff[:self.bufneed]
375 self.buff = self.buff[self.bufneed:]
Chui Tey5d2af632002-05-26 13:36:41 +0000376 self.bufneed = 4
377 self.bufstate = 0
378 return packet
379
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000380 def pollmessage(self, wait):
Chui Tey5d2af632002-05-26 13:36:41 +0000381 packet = self.pollpacket(wait)
382 if packet is None:
383 return None
384 try:
385 message = pickle.loads(packet)
Kurt B. Kaiserd6ab77d2004-01-21 19:21:11 +0000386 except pickle.UnpicklingError:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000387 print("-----------------------", file=sys.__stderr__)
388 print("cannot unpickle packet:", repr(packet), file=sys.__stderr__)
Chui Tey5d2af632002-05-26 13:36:41 +0000389 traceback.print_stack(file=sys.__stderr__)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000390 print("-----------------------", file=sys.__stderr__)
Chui Tey5d2af632002-05-26 13:36:41 +0000391 raise
392 return message
393
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000394 def pollresponse(self, myseq, wait):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000395 """Handle messages received on the socket.
396
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000397 Some messages received may be asynchronous 'call' or 'queue' requests,
398 and some may be responses for other threads.
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000399
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000400 'call' requests are passed to self.localcall() with the expectation of
401 immediate execution, during which time the socket is not serviced.
402
403 'queue' requests are used for tasks (which may block or hang) to be
404 processed in a different thread. These requests are fed into
405 request_queue by self.localcall(). Responses to queued requests are
406 taken from response_queue and sent across the link with the associated
407 sequence numbers. Messages in the queues are (sequence_number,
408 request/response) tuples and code using this module removing messages
409 from the request_queue is responsible for returning the correct
410 sequence number in the response_queue.
411
412 pollresponse() will loop until a response message with the myseq
413 sequence number is received, and will save other responses in
414 self.responses and notify the owning thread.
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000415
416 """
Chui Tey5d2af632002-05-26 13:36:41 +0000417 while 1:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000418 # send queued response if there is one available
419 try:
420 qmsg = response_queue.get(0)
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000421 except queue.Empty:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000422 pass
423 else:
424 seq, response = qmsg
425 message = (seq, ('OK', response))
426 self.putmessage(message)
427 # poll for message on link
428 try:
429 message = self.pollmessage(wait)
430 if message is None: # socket not ready
431 return None
432 except EOFError:
433 self.handle_EOF()
Chui Tey5d2af632002-05-26 13:36:41 +0000434 return None
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000435 except AttributeError:
436 return None
Chui Tey5d2af632002-05-26 13:36:41 +0000437 seq, resq = message
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000438 how = resq[0]
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000439 self.debug("pollresponse:%d:myseq:%s" % (seq, myseq))
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000440 # process or queue a request
441 if how in ("CALL", "QUEUE"):
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000442 self.debug("pollresponse:%d:localcall:call:" % seq)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000443 response = self.localcall(seq, resq)
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000444 self.debug("pollresponse:%d:localcall:response:%s"
445 % (seq, response))
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000446 if how == "CALL":
447 self.putmessage((seq, response))
448 elif how == "QUEUE":
449 # don't acknowledge the 'queue' request!
450 pass
Chui Tey5d2af632002-05-26 13:36:41 +0000451 continue
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000452 # return if completed message transaction
Chui Tey5d2af632002-05-26 13:36:41 +0000453 elif seq == myseq:
454 return resq
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000455 # must be a response for a different thread:
Chui Tey5d2af632002-05-26 13:36:41 +0000456 else:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000457 cv = self.cvars.get(seq, None)
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000458 # response involving unknown sequence number is discarded,
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000459 # probably intended for prior incarnation of server
Chui Tey5d2af632002-05-26 13:36:41 +0000460 if cv is not None:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000461 cv.acquire()
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000462 self.responses[seq] = resq
Chui Tey5d2af632002-05-26 13:36:41 +0000463 cv.notify()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000464 cv.release()
Chui Tey5d2af632002-05-26 13:36:41 +0000465 continue
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000466
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000467 def handle_EOF(self):
468 "action taken upon link being closed by peer"
469 self.EOFhook()
470 self.debug("handle_EOF")
471 for key in self.cvars:
472 cv = self.cvars[key]
473 cv.acquire()
474 self.responses[key] = ('EOF', None)
475 cv.notify()
476 cv.release()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000477 # call our (possibly overridden) exit function
478 self.exithook()
479
480 def EOFhook(self):
481 "Classes using rpc client/server can override to augment EOF action"
482 pass
483
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000484#----------------- end class SocketIO --------------------
Chui Tey5d2af632002-05-26 13:36:41 +0000485
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000486class RemoteObject(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000487 # Token mix-in class
488 pass
489
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400490
Chui Tey5d2af632002-05-26 13:36:41 +0000491def remoteref(obj):
492 oid = id(obj)
493 objecttable[oid] = obj
494 return RemoteProxy(oid)
495
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400496
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000497class RemoteProxy(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000498
499 def __init__(self, oid):
500 self.oid = oid
501
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400502
Alexandre Vassalottice261952008-05-12 02:31:37 +0000503class RPCHandler(socketserver.BaseRequestHandler, SocketIO):
Chui Tey5d2af632002-05-26 13:36:41 +0000504
Kurt B. Kaiser0930c432002-12-06 21:45:24 +0000505 debugging = False
506 location = "#S" # Server
Chui Tey5d2af632002-05-26 13:36:41 +0000507
508 def __init__(self, sock, addr, svr):
509 svr.current_handler = self ## cgt xxx
510 SocketIO.__init__(self, sock)
Alexandre Vassalottice261952008-05-12 02:31:37 +0000511 socketserver.BaseRequestHandler.__init__(self, sock, addr, svr)
Chui Tey5d2af632002-05-26 13:36:41 +0000512
Chui Tey5d2af632002-05-26 13:36:41 +0000513 def handle(self):
Alexandre Vassalottice261952008-05-12 02:31:37 +0000514 "handle() method required by socketserver"
Chui Tey5d2af632002-05-26 13:36:41 +0000515 self.mainloop()
516
517 def get_remote_proxy(self, oid):
518 return RPCProxy(self, oid)
519
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400520
Chui Tey5d2af632002-05-26 13:36:41 +0000521class RPCClient(SocketIO):
522
Kurt B. Kaiser0930c432002-12-06 21:45:24 +0000523 debugging = False
524 location = "#C" # Client
525
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000526 nextseq = 1 # Requests coming from the client are odd numbered
Chui Tey5d2af632002-05-26 13:36:41 +0000527
528 def __init__(self, address, family=socket.AF_INET, type=socket.SOCK_STREAM):
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000529 self.listening_sock = socket.socket(family, type)
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000530 self.listening_sock.bind(address)
531 self.listening_sock.listen(1)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000532
533 def accept(self):
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000534 working_sock, address = self.listening_sock.accept()
Kurt B. Kaiser74d93c82002-12-23 22:51:03 +0000535 if self.debugging:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000536 print("****** Connection request from ", address, file=sys.__stderr__)
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +0000537 if address[0] == LOCALHOST:
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000538 SocketIO.__init__(self, working_sock)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000539 else:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000540 print("** Invalid host: ", address, file=sys.__stderr__)
Andrew Svetlov0832af62012-12-18 23:10:48 +0200541 raise OSError
Chui Tey5d2af632002-05-26 13:36:41 +0000542
543 def get_remote_proxy(self, oid):
544 return RPCProxy(self, oid)
545
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400546
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000547class RPCProxy(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000548
549 __methods = None
550 __attributes = None
551
552 def __init__(self, sockio, oid):
553 self.sockio = sockio
554 self.oid = oid
555
556 def __getattr__(self, name):
557 if self.__methods is None:
558 self.__getmethods()
559 if self.__methods.get(name):
560 return MethodProxy(self.sockio, self.oid, name)
561 if self.__attributes is None:
562 self.__getattributes()
Guido van Rossum811c4e02006-08-22 15:45:46 +0000563 if name in self.__attributes:
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000564 value = self.sockio.remotecall(self.oid, '__getattribute__',
565 (name,), {})
566 return value
567 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000568 raise AttributeError(name)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000569
Chui Tey5d2af632002-05-26 13:36:41 +0000570 def __getattributes(self):
571 self.__attributes = self.sockio.remotecall(self.oid,
572 "__attributes__", (), {})
573
574 def __getmethods(self):
575 self.__methods = self.sockio.remotecall(self.oid,
576 "__methods__", (), {})
577
578def _getmethods(obj, methods):
579 # Helper to get a list of methods from an object
580 # Adds names to dictionary argument 'methods'
581 for name in dir(obj):
582 attr = getattr(obj, name)
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200583 if callable(attr):
Chui Tey5d2af632002-05-26 13:36:41 +0000584 methods[name] = 1
Guido van Rossum13257902007-06-07 23:15:56 +0000585 if isinstance(obj, type):
Chui Tey5d2af632002-05-26 13:36:41 +0000586 for super in obj.__bases__:
587 _getmethods(super, methods)
588
589def _getattributes(obj, attributes):
590 for name in dir(obj):
591 attr = getattr(obj, name)
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200592 if not callable(attr):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000593 attributes[name] = 1
Chui Tey5d2af632002-05-26 13:36:41 +0000594
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400595
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +0000596class MethodProxy(object):
Chui Tey5d2af632002-05-26 13:36:41 +0000597
598 def __init__(self, sockio, oid, name):
599 self.sockio = sockio
600 self.oid = oid
601 self.name = name
602
603 def __call__(self, *args, **kwargs):
604 value = self.sockio.remotecall(self.oid, self.name, args, kwargs)
605 return value
606
Chui Tey5d2af632002-05-26 13:36:41 +0000607
Kurt B. Kaiser62685d32003-09-10 02:42:18 +0000608# XXX KBK 09Sep03 We need a proper unit test for this module. Previously
Benjamin Peterson8719ad52009-09-11 22:24:02 +0000609# existing test code was removed at Rev 1.27 (r34098).
Andrew Svetlovcd49d532012-03-25 11:43:02 +0300610
611def displayhook(value):
612 """Override standard display hook to use non-locale encoding"""
613 if value is None:
614 return
615 # Set '_' to None to avoid recursion
616 builtins._ = None
617 text = repr(value)
618 try:
619 sys.stdout.write(text)
620 except UnicodeEncodeError:
621 # let's use ascii while utf8-bmp codec doesn't present
622 encoding = 'ascii'
623 bytes = text.encode(encoding, 'backslashreplace')
624 text = bytes.decode(encoding, 'strict')
625 sys.stdout.write(text)
626 sys.stdout.write("\n")
627 builtins._ = value