1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 """The task RPC server code.
7 This server is an XML-RPC server which serves code from
8 rpc_methods.RPCMethods.
10 This server will run until shutdown is called on the server object. This can
11 be achieved in 2 ways:
13 - Calling the Quit RPC method defined in RPCMethods
14 - Not receiving any calls within the idle_timeout_secs time.
21 import SimpleXMLRPCServer
24 #pylint: disable=relative-import
29 class RequestHandler(SimpleXMLRPCServer
.SimpleXMLRPCRequestHandler
):
30 """Restricts access to only specified IP address.
32 This call assumes the server is RPCServer.
36 """Verifies the task is authorized to perform RPCs."""
37 if self
.client_address
[0] != self
.server
.authorized_address
:
38 logging
.error('Received unauthorized RPC request from %s',
40 self
.send_response(403)
41 response
= 'Forbidden'
42 self
.send_header('Content-type', 'text/plain')
43 self
.send_header('Content-length', str(len(response
)))
45 self
.wfile
.write(response
)
47 return SimpleXMLRPCServer
.SimpleXMLRPCRequestHandler
.do_POST(self
)
50 class RPCServer(SimpleXMLRPCServer
.SimpleXMLRPCServer
,
51 SocketServer
.ThreadingMixIn
):
52 """Restricts all endpoints to only specified IP addresses."""
54 def __init__(self
, authorized_address
,
55 idle_timeout_secs
=common_lib
.DEFAULT_TIMEOUT_SECS
):
56 SimpleXMLRPCServer
.SimpleXMLRPCServer
.__init
__(
57 self
, (common_lib
.SERVER_ADDRESS
, common_lib
.SERVER_PORT
),
58 allow_none
=True, logRequests
=False,
59 requestHandler
=RequestHandler
)
61 self
.authorized_address
= authorized_address
62 self
.idle_timeout_secs
= idle_timeout_secs
63 self
.register_instance(rpc_methods
.RPCMethods(self
))
65 self
._shutdown
_requested
_event
= threading
.Event()
66 self
._rpc
_received
_event
= threading
.Event()
67 self
._idle
_thread
= threading
.Thread(target
=self
._CheckForIdleQuit
)
70 """Shutdown the server.
72 This overloaded method sets the _shutdown_requested_event to allow the
73 idle timeout thread to quit.
75 self
._shutdown
_requested
_event
.set()
76 SimpleXMLRPCServer
.SimpleXMLRPCServer
.shutdown(self
)
77 logging
.info('Server shutdown complete')
79 def serve_forever(self
, poll_interval
=0.5):
82 This overloaded method starts the idle timeout thread before calling
83 serve_forever. This ensures the idle timer thread doesn't get started
84 without the server running.
87 poll_interval: The interval to poll for shutdown.
89 logging
.info('RPC server starting')
90 self
._idle
_thread
.start()
91 SimpleXMLRPCServer
.SimpleXMLRPCServer
.serve_forever(self
, poll_interval
)
93 def _dispatch(self
, method
, params
):
94 """Dispatch the call to the correct method with the provided params.
96 This overloaded method adds logging to help trace connection and
100 method: The method name to call.
101 params: A tuple of parameters to pass.
104 The result of the parent class' _dispatch method.
106 logging
.debug('Calling %s%s', method
, params
)
107 self
._rpc
_received
_event
.set()
108 return SimpleXMLRPCServer
.SimpleXMLRPCServer
._dispatch
(self
, method
, params
)
110 def _CheckForIdleQuit(self
):
111 """Check for, and exit, if the server is idle for too long.
113 This method must be run in a separate thread to avoid a deadlock when
114 calling server.shutdown.
116 timeout
= time
.time() + self
.idle_timeout_secs
117 while time
.time() < timeout
:
118 if self
._shutdown
_requested
_event
.is_set():
119 # An external source called shutdown()
121 elif self
._rpc
_received
_event
.is_set():
122 logging
.debug('Resetting the idle timeout')
123 timeout
= time
.time() + self
.idle_timeout_secs
124 self
._rpc
_received
_event
.clear()
126 # We timed out, kill the server
127 logging
.warning('Shutting down the server due to the idle timeout')