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.
22 #pylint: disable=relative-import
26 import SimpleJSONRPCServer
29 class RequestHandler(SimpleJSONRPCServer
.SimpleJSONRPCRequestHandler
):
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 SimpleJSONRPCServer
.SimpleJSONRPCRequestHandler
.do_POST(self
)
50 class RpcServer(SimpleJSONRPCServer
.SimpleJSONRPCServer
,
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 SimpleJSONRPCServer
.SimpleJSONRPCServer
.__init
__(
57 self
, (common_lib
.SERVER_ADDRESS
, common_lib
.SERVER_PORT
),
58 allow_none
=True, logRequests
=False,
59 requestHandler
=RequestHandler
)
60 self
.authorized_address
= authorized_address
61 self
.idle_timeout_secs
= idle_timeout_secs
62 self
.register_instance(rpc_methods
.RPCMethods(self
))
64 self
._shutdown
_requested
_event
= threading
.Event()
65 self
._rpc
_received
_event
= threading
.Event()
66 self
._idle
_thread
= threading
.Thread(target
=self
._CheckForIdleQuit
)
69 """Shutdown the server.
71 This overloaded method sets the _shutdown_requested_event to allow the
72 idle timeout thread to quit.
74 self
._shutdown
_requested
_event
.set()
75 SimpleJSONRPCServer
.SimpleJSONRPCServer
.shutdown(self
)
76 logging
.info('Server shutdown complete')
78 def serve_forever(self
, poll_interval
=0.5):
81 This overloaded method starts the idle timeout thread before calling
82 serve_forever. This ensures the idle timer thread doesn't get started
83 without the server running.
86 poll_interval: The interval to poll for shutdown.
88 logging
.info('RPC server starting')
89 self
._idle
_thread
.start()
90 SimpleJSONRPCServer
.SimpleJSONRPCServer
.serve_forever(self
, poll_interval
)
92 def _dispatch(self
, method
, params
):
93 """Dispatch the call to the correct method with the provided params.
95 This overloaded method adds logging to help trace connection and
99 method: The method name to call.
100 params: A tuple of parameters to pass.
103 The result of the parent class' _dispatch method.
105 logging
.debug('Calling %s%s', method
, params
)
106 self
._rpc
_received
_event
.set()
107 return SimpleJSONRPCServer
.SimpleJSONRPCServer
._dispatch
(
108 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')
131 def Connect(server
, port
=common_lib
.SERVER_PORT
):
132 """Creates and returns a connection to an RPC server."""
133 addr
= 'http://%s:%d' % (server
, port
)
134 logging
.debug('Connecting to RPC server at %s', addr
)
135 return jsonrpclib
.ServerProxy(addr
, allow_none
=True)