1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 from __future__ import print_function
36
37 """
38 Common XML-RPC for higher-level libraries running XML-RPC libraries in
39 ROS. In particular, this library provides common handling for URI
40 calculation based on ROS environment variables.
41
42 The common entry point for most libraries is the L{XmlRpcNode} class.
43 """
44
45 import logging
46 import select
47 import socket
48 import string
49
50 try:
51 import _thread
52 except ImportError:
53 import thread as _thread
54
55 import traceback
56
57 try:
58 from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
59 except ImportError:
60 from SimpleXMLRPCServer import SimpleXMLRPCServer
61 from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler
62
63 try:
64 import socketserver
65 except ImportError:
66 import SocketServer as socketserver
67
68 import roslib.network
69 import roslib.exceptions
70
72 """Small helper version to check an object is a string in a way that works
73 for both Python 2 and 3
74 """
75 try:
76 return isinstance(s, basestring)
77 except NameError:
78 return isinstance(s, str)
79
82 if 0:
83 SimpleXMLRPCRequestHandler.log_message(self, format, *args)
84
86 """
87 Adds ThreadingMixin to SimpleXMLRPCServer to support multiple concurrent
88 requests via threading. Also makes logging toggleable.
89 """
90 - def __init__(self, addr, log_requests=1):
91 """
92 Overrides SimpleXMLRPCServer to set option to allow_reuse_address.
93 """
94
95
96
97 self.allow_reuse_address = True
98 SimpleXMLRPCServer.__init__(self, addr, SilenceableXMLRPCRequestHandler, log_requests)
99
101 """
102 override ThreadingMixin, which sends errors to stderr
103 """
104 if logging and traceback:
105 logger = logging.getLogger('xmlrpc')
106 if logger:
107 logger.error(traceback.format_exc())
108
110 """
111 Adds ThreadingMixin to SimpleXMLRPCServer to support multiple concurrent
112 requests via forking. Also makes logging toggleable.
113 """
115 SimpleXMLRPCServer.__init__(self, addr, request_handler, log_requests)
116
117
119 """
120 Base handler API for handlers used with XmlRpcNode. Public methods will be
121 exported as XML RPC methods.
122 """
123
125 """
126 callback into handler to inform it of XML-RPC URI
127 """
128 pass
129
131 """
132 Generic XML-RPC node. Handles the additional complexity of binding
133 an XML-RPC server to an arbitrary port.
134 XmlRpcNode is initialized when the uri field has a value.
135 """
136
137 - def __init__(self, port=0, rpc_handler=None, on_run_error=None):
138 """
139 XML RPC Node constructor
140 @param port: port to use for starting XML-RPC API. Set to 0 or omit to bind to any available port.
141 @type port: int
142 @param rpc_handler: XML-RPC API handler for node.
143 @type rpc_handler: XmlRpcHandler
144 @param on_run_error: function to invoke if server.run() throws
145 Exception. Server always terminates if run() throws, but this
146 enables cleanup routines to be invoked if server goes down, as
147 well as include additional debugging.
148 @type on_run_error: fn(Exception)
149 """
150 super(XmlRpcNode, self).__init__()
151
152 self.handler = rpc_handler
153 self.uri = None
154 self.server = None
155 if port and isstring(port):
156 port = int(port)
157 self.port = port
158 self.is_shutdown = False
159 self.on_run_error = on_run_error
160
162 """
163 Terminate i/o connections for this server.
164 @param reason: human-readable debug string
165 @type reason: str
166 """
167 self.is_shutdown = True
168 if self.server:
169 server = self.server
170 handler = self.handler
171 self.handler = self.server = self.port = self.uri = None
172 if handler:
173 handler._shutdown(reason)
174 if server:
175 server.socket.close()
176 server.server_close()
177
179 """
180 Initiate a thread to run the XML RPC server. Uses thread.start_new_thread.
181 """
182 _thread.start_new_thread(self.run, ())
183
185 """
186 Sets the XML-RPC URI. Defined as a separate method as a hood
187 for subclasses to bootstrap initialization. Should not be called externally.
188 @param uri: XMLRPC URI.
189 @type uri: str
190 """
191 self.uri = uri
192
194 try:
195 self._run()
196 except Exception as e:
197 if self.is_shutdown:
198 pass
199 elif self.on_run_error is not None:
200 self.on_run_error(e)
201 else:
202 raise
203
205 """
206 Main processing thread body.
207 @raise socket.error: If server cannot bind
208 @raise roslib.exceptions.ROSLibException: If unknown error occurs
209 """
210 logger = logging.getLogger('xmlrpc')
211 try:
212 log_requests = 0
213 port = self.port or 0
214
215 bind_address = roslib.network.get_bind_address()
216 logger.info("XML-RPC server binding to %s"%bind_address)
217
218 self.server = ThreadingXMLRPCServer((bind_address, port), log_requests)
219 self.port = self.server.server_address[1]
220 if not self.port:
221 self.port = self.server.socket.getsockname()[1]
222 if not self.port:
223 raise roslib.exceptions.ROSLibException("Unable to retrieve local address binding")
224
225
226
227
228
229 uri = None
230 override = roslib.network.get_address_override()
231 if override:
232 uri = 'http://%s:%s/'%(override, self.port)
233 else:
234 try:
235 hostname = socket.gethostname()
236 if hostname and not hostname == 'localhost' and not hostname.startswith('127.'):
237 uri = 'http://%s:%s/'%(hostname, self.port)
238 except:
239 pass
240 if not uri:
241 uri = 'http://%s:%s/'%(roslib.network.get_local_address(), self.port)
242 self.set_uri(uri)
243
244
245 logger.info("Started XML-RPC server [%s]", self.uri)
246
247 self.server.register_multicall_functions()
248 self.server.register_instance(self.handler)
249
250 except socket.error as e:
251 (n, errstr) = e
252 if n == 98:
253 msg = "ERROR: Unable to start XML-RPC server, port %s is already in use"%self.port
254 else:
255 msg = "ERROR: Unable to start XML-RPC server: %s"%errstr
256 logger.error(msg)
257 print(msg)
258 raise
259
260 if self.handler is not None:
261 self.handler._ready(self.uri)
262 logger.info("xml rpc node: starting XML-RPC server")
263 while not self.is_shutdown:
264 try:
265 self.server.serve_forever()
266 except (IOError, select.error) as e:
267 (errno, errstr) = e
268
269
270
271 if self.is_shutdown:
272 pass
273 elif errno != 4:
274 self.is_shutdown = True
275 logger.error("serve forever IOError: %s, %s"%(errno, errstr))
276