[autotest] Include client's IP in RPC logging

Having the RPC requester's IP address in the rpc log and in the
metaDB will help us a lot to debug lab fire.

BUG=chromium:505568
TEST=Request a RPC to local apache from a remote machine.
DEPLOY=apache

Change-Id: I283f55d78eb29ff59cce6e487661adaf84dea84a
Reviewed-on: https://chromium-review.googlesource.com/282574
Reviewed-by: Dan Shi <dshi@chromium.org>
Reviewed-by: Paul Hobbs <phobbs@google.com>
Commit-Queue: Mungyung Ryu <mkryu@google.com>
Tested-by: Mungyung Ryu <mkryu@google.com>
diff --git a/frontend/afe/rpc_handler.py b/frontend/afe/rpc_handler.py
index a197239..ab3ed38 100644
--- a/frontend/afe/rpc_handler.py
+++ b/frontend/afe/rpc_handler.py
@@ -68,10 +68,10 @@
 
 
     def log_request(self, user, decoded_request, decoded_result,
-                    log_all=False):
+                    remote_ip, log_all=False):
         if log_all or should_log_message(decoded_request['method']):
-            msg = '%s:%s %s'  % (decoded_request['method'], user,
-                                 decoded_request['params'])
+            msg = '%s| %s:%s %s'  % (remote_ip, decoded_request['method'],
+                                     user, decoded_request['params'])
             if decoded_result['err']:
                 msg += '\n' + decoded_result['err_traceback']
                 rpcserver_logging.rpc_logger.error(msg)
@@ -84,13 +84,17 @@
 
 
     def handle_rpc_request(self, request):
+        remote_ip = self._get_remote_ip(request)
         user = models.User.current_user()
         json_request = self.raw_request_data(request)
         decoded_request = self.decode_request(json_request)
+
+        decoded_request['remote_ip'] = remote_ip
         decoded_result = self.dispatch_request(decoded_request)
         result = self.encode_result(decoded_result)
         if rpcserver_logging.LOGGING_ENABLED:
-            self.log_request(user, decoded_request, decoded_result)
+            self.log_request(user, decoded_request, decoded_result,
+                             remote_ip)
         return rpc_utils.raw_http_response(result)
 
 
@@ -132,3 +136,27 @@
                 continue
             decorated_function = RpcHandler._allow_keyword_args(attribute)
             setattr(self._rpc_methods, name, decorated_function)
+
+
+    def _get_remote_ip(self, request):
+        """Get the ip address of a RPC caller.
+
+        Returns the IP of the request, accounting for the possibility of
+        being behind a proxy.
+        If a Django server is behind a proxy, request.META["REMOTE_ADDR"] will
+        return the proxy server's IP, not the client's IP.
+        The proxy server would provide the client's IP in the
+        HTTP_X_FORWARDED_FOR header.
+
+        @param request: django.core.handlers.wsgi.WSGIRequest object.
+
+        @return: IP address of remote host as a string.
+                 Empty string if the IP cannot be found.
+        """
+        remote = request.META.get('HTTP_X_FORWARDED_FOR', None)
+        if remote:
+            # X_FORWARDED_FOR returns client1, proxy1, proxy2,...
+            remote = remote.split(',')[0].strip()
+        else:
+            remote = request.META.get('REMOTE_ADDR', '')
+        return remote