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
49 try:
50 import _thread
51 except ImportError:
52 import thread as _thread
53
54 import traceback
55
56 try:
57 from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
58 except ImportError:
59 from SimpleXMLRPCServer import SimpleXMLRPCServer
60 from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler
61
62 try:
63 import socketserver
64 except ImportError:
65 import SocketServer as socketserver
66
67 import rosgraph.network
68
70 """Small helper version to check an object is a string in a way that works
71 for both Python 2 and 3
72 """
73 try:
74 return isinstance(s, basestring)
75 except NameError:
76 return isinstance(s, str)
77
80 if 0:
81 SimpleXMLRPCRequestHandler.log_message(self, format, *args)
82
84 """
85 Adds ThreadingMixin to SimpleXMLRPCServer to support multiple concurrent
86 requests via threading. Also makes logging toggleable.
87 """
88 - def __init__(self, addr, log_requests=1):
89 """
90 Overrides SimpleXMLRPCServer to set option to allow_reuse_address.
91 """
92
93
94
95 self.allow_reuse_address = True
96
97
98 self.request_queue_size = min(socket.SOMAXCONN, 128)
99 if rosgraph.network.use_ipv6():
100 logger = logging.getLogger('xmlrpc')
101
102
103
104
105 SimpleXMLRPCServer.__init__(self, addr, SilenceableXMLRPCRequestHandler, log_requests, bind_and_activate=False)
106 self.address_family = socket.AF_INET6
107 self.socket = socket.socket(self.address_family, self.socket_type)
108 logger.info('binding ipv6 xmlrpc socket to' + str(addr))
109
110
111 self.server_bind()
112 self.server_activate()
113 logger.info('bound to ' + str(self.socket.getsockname()[0:2]))
114 else:
115 SimpleXMLRPCServer.__init__(self, addr, SilenceableXMLRPCRequestHandler, log_requests)
116
118 """
119 override ThreadingMixin, which sends errors to stderr
120 """
121 if logging and traceback:
122 logger = logging.getLogger('xmlrpc')
123 if logger:
124 logger.error(traceback.format_exc())
125
127 """
128 Adds ThreadingMixin to SimpleXMLRPCServer to support multiple concurrent
129 requests via forking. Also makes logging toggleable.
130 """
132 SimpleXMLRPCServer.__init__(self, addr, request_handler, log_requests)
133
134
136 """
137 Base handler API for handlers used with XmlRpcNode. Public methods will be
138 exported as XML RPC methods.
139 """
140
142 """
143 callback into handler to inform it of XML-RPC URI
144 """
145 pass
146
148 """
149 callback into handler to inform it of shutdown
150 """
151 pass
152
154 """
155 Generic XML-RPC node. Handles the additional complexity of binding
156 an XML-RPC server to an arbitrary port.
157 XmlRpcNode is initialized when the uri field has a value.
158 """
159
160 - def __init__(self, port=0, rpc_handler=None, on_run_error=None):
161 """
162 XML RPC Node constructor
163 :param port: port to use for starting XML-RPC API. Set to 0 or omit to bind to any available port, ``int``
164 :param rpc_handler: XML-RPC API handler for node, `XmlRpcHandler`
165 :param on_run_error: function to invoke if server.run() throws
166 Exception. Server always terminates if run() throws, but this
167 enables cleanup routines to be invoked if server goes down, as
168 well as include additional debugging. ``fn(Exception)``
169 """
170 super(XmlRpcNode, self).__init__()
171
172 self.handler = rpc_handler
173 self.uri = None
174 self.server = None
175 if port and isstring(port):
176 port = int(port)
177 self.port = port
178 self.is_shutdown = False
179 self.on_run_error = on_run_error
180
182 """
183 Terminate i/o connections for this server.
184
185 :param reason: human-readable debug string, ``str``
186 """
187 self.is_shutdown = True
188 if self.server:
189 server = self.server
190 handler = self.handler
191 self.handler = self.server = self.port = self.uri = None
192 if handler:
193 handler._shutdown(reason)
194 if server:
195 server.socket.close()
196 server.server_close()
197
199 """
200 Initiate a thread to run the XML RPC server. Uses thread.start_new_thread.
201 """
202 _thread.start_new_thread(self.run, ())
203
205 """
206 Sets the XML-RPC URI. Defined as a separate method as a hood
207 for subclasses to bootstrap initialization. Should not be called externally.
208
209 :param uri: XMLRPC URI, ``str``
210 """
211 self.uri = uri
212
214 try:
215 self._run()
216 except Exception as e:
217 if self.is_shutdown:
218 pass
219 elif self.on_run_error is not None:
220 self.on_run_error(e)
221 else:
222 raise
223
224
226 logger = logging.getLogger('xmlrpc')
227 try:
228 log_requests = 0
229 port = self.port or 0
230
231 bind_address = rosgraph.network.get_bind_address()
232 logger.info("XML-RPC server binding to %s:%d" % (bind_address, port))
233
234 self.server = ThreadingXMLRPCServer((bind_address, port), log_requests)
235 self.port = self.server.server_address[1]
236 if not self.port:
237 self.port = self.server.socket.getsockname()[1]
238
239 assert self.port, "Unable to retrieve local address binding"
240
241
242
243
244
245 uri = None
246 override = rosgraph.network.get_address_override()
247 if override:
248 uri = 'http://%s:%s/'%(override, self.port)
249 else:
250 try:
251 hostname = socket.gethostname()
252 if hostname and not hostname == 'localhost' and not hostname.startswith('127.') and hostname != '::':
253 uri = 'http://%s:%s/'%(hostname, self.port)
254 except:
255 pass
256 if not uri:
257 uri = 'http://%s:%s/'%(rosgraph.network.get_local_address(), self.port)
258 self.set_uri(uri)
259
260 logger.info("Started XML-RPC server [%s]", self.uri)
261
262 self.server.register_multicall_functions()
263 self.server.register_instance(self.handler)
264
265 except socket.error as e:
266 if e.errno == 98:
267 msg = "ERROR: Unable to start XML-RPC server, port %s is already in use"%self.port
268 else:
269 msg = "ERROR: Unable to start XML-RPC server: %s" % e.strerror
270 logger.error(msg)
271 print(msg)
272 raise
273
274 if self.handler is not None:
275 self.handler._ready(self.uri)
276 logger.info("xml rpc node: starting XML-RPC server")
277
279 """
280 Main processing thread body.
281 :raises: :exc:`socket.error` If server cannot bind
282
283 """
284 self._run_init()
285 while not self.is_shutdown:
286 try:
287 self.server.serve_forever()
288 except (IOError, select.error) as e:
289
290
291
292 if self.is_shutdown:
293 pass
294 elif e.errno != 4:
295 self.is_shutdown = True
296 logging.getLogger('xmlrpc').error("serve forever IOError: %s, %s"%(e.errno, e.strerror))
297