1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/webrtc/trunk/build/android/pylib/ports.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,167 @@ 1.4 +# Copyright (c) 2012 The Chromium Authors. All rights reserved. 1.5 +# Use of this source code is governed by a BSD-style license that can be 1.6 +# found in the LICENSE file. 1.7 + 1.8 +"""Functions that deals with local and device ports.""" 1.9 + 1.10 +import contextlib 1.11 +import fcntl 1.12 +import httplib 1.13 +import logging 1.14 +import os 1.15 +import re 1.16 +import socket 1.17 +import traceback 1.18 + 1.19 +import cmd_helper 1.20 +import constants 1.21 + 1.22 + 1.23 +#The following two methods are used to allocate the port source for various 1.24 +# types of test servers. Because some net relates tests can be run on shards 1.25 +# at same time, it's important to have a mechanism to allocate the port process 1.26 +# safe. In here, we implement the safe port allocation by leveraging flock. 1.27 +def ResetTestServerPortAllocation(): 1.28 + """Reset the port allocation to start from TEST_SERVER_PORT_FIRST. 1.29 + 1.30 + Returns: 1.31 + Returns True if reset successes. Otherwise returns False. 1.32 + """ 1.33 + try: 1.34 + with open(constants.TEST_SERVER_PORT_FILE, 'w') as fp: 1.35 + fp.write('%d' % constants.TEST_SERVER_PORT_FIRST) 1.36 + if os.path.exists(constants.TEST_SERVER_PORT_LOCKFILE): 1.37 + os.unlink(constants.TEST_SERVER_PORT_LOCKFILE) 1.38 + return True 1.39 + except Exception as e: 1.40 + logging.error(e) 1.41 + return False 1.42 + 1.43 + 1.44 +def AllocateTestServerPort(): 1.45 + """Allocate a port incrementally. 1.46 + 1.47 + Returns: 1.48 + Returns a valid port which should be in between TEST_SERVER_PORT_FIRST and 1.49 + TEST_SERVER_PORT_LAST. Returning 0 means no more valid port can be used. 1.50 + """ 1.51 + port = 0 1.52 + ports_tried = [] 1.53 + try: 1.54 + fp_lock = open(constants.TEST_SERVER_PORT_LOCKFILE, 'w') 1.55 + fcntl.flock(fp_lock, fcntl.LOCK_EX) 1.56 + # Get current valid port and calculate next valid port. 1.57 + assert os.path.exists(constants.TEST_SERVER_PORT_FILE) 1.58 + with open(constants.TEST_SERVER_PORT_FILE, 'r+') as fp: 1.59 + port = int(fp.read()) 1.60 + ports_tried.append(port) 1.61 + while IsHostPortUsed(port): 1.62 + port += 1 1.63 + ports_tried.append(port) 1.64 + if (port > constants.TEST_SERVER_PORT_LAST or 1.65 + port < constants.TEST_SERVER_PORT_FIRST): 1.66 + port = 0 1.67 + else: 1.68 + fp.seek(0, os.SEEK_SET) 1.69 + fp.write('%d' % (port + 1)) 1.70 + except Exception as e: 1.71 + logging.info(e) 1.72 + finally: 1.73 + if fp_lock: 1.74 + fcntl.flock(fp_lock, fcntl.LOCK_UN) 1.75 + fp_lock.close() 1.76 + if port: 1.77 + logging.info('Allocate port %d for test server.', port) 1.78 + else: 1.79 + logging.error('Could not allocate port for test server. ' 1.80 + 'List of ports tried: %s', str(ports_tried)) 1.81 + return port 1.82 + 1.83 + 1.84 +def IsHostPortUsed(host_port): 1.85 + """Checks whether the specified host port is used or not. 1.86 + 1.87 + Uses -n -P to inhibit the conversion of host/port numbers to host/port names. 1.88 + 1.89 + Args: 1.90 + host_port: Port on host we want to check. 1.91 + 1.92 + Returns: 1.93 + True if the port on host is already used, otherwise returns False. 1.94 + """ 1.95 + port_info = '(127\.0\.0\.1)|(localhost)\:%d' % host_port 1.96 + # TODO(jnd): Find a better way to filter the port. 1.97 + re_port = re.compile(port_info, re.MULTILINE) 1.98 + if re_port.findall(cmd_helper.GetCmdOutput(['lsof', '-nPi:%d' % host_port])): 1.99 + return True 1.100 + return False 1.101 + 1.102 + 1.103 +def IsDevicePortUsed(adb, device_port, state=''): 1.104 + """Checks whether the specified device port is used or not. 1.105 + 1.106 + Args: 1.107 + adb: Instance of AndroidCommands for talking to the device. 1.108 + device_port: Port on device we want to check. 1.109 + state: String of the specified state. Default is empty string, which 1.110 + means any state. 1.111 + 1.112 + Returns: 1.113 + True if the port on device is already used, otherwise returns False. 1.114 + """ 1.115 + base_url = '127.0.0.1:%d' % device_port 1.116 + netstat_results = adb.RunShellCommand('netstat', log_result=False) 1.117 + for single_connect in netstat_results: 1.118 + # Column 3 is the local address which we want to check with. 1.119 + connect_results = single_connect.split() 1.120 + is_state_match = connect_results[5] == state if state else True 1.121 + if connect_results[3] == base_url and is_state_match: 1.122 + return True 1.123 + return False 1.124 + 1.125 + 1.126 +def IsHttpServerConnectable(host, port, tries=3, command='GET', path='/', 1.127 + expected_read='', timeout=2): 1.128 + """Checks whether the specified http server is ready to serve request or not. 1.129 + 1.130 + Args: 1.131 + host: Host name of the HTTP server. 1.132 + port: Port number of the HTTP server. 1.133 + tries: How many times we want to test the connection. The default value is 1.134 + 3. 1.135 + command: The http command we use to connect to HTTP server. The default 1.136 + command is 'GET'. 1.137 + path: The path we use when connecting to HTTP server. The default path is 1.138 + '/'. 1.139 + expected_read: The content we expect to read from the response. The default 1.140 + value is ''. 1.141 + timeout: Timeout (in seconds) for each http connection. The default is 2s. 1.142 + 1.143 + Returns: 1.144 + Tuple of (connect status, client error). connect status is a boolean value 1.145 + to indicate whether the server is connectable. client_error is the error 1.146 + message the server returns when connect status is false. 1.147 + """ 1.148 + assert tries >= 1 1.149 + for i in xrange(0, tries): 1.150 + client_error = None 1.151 + try: 1.152 + with contextlib.closing(httplib.HTTPConnection( 1.153 + host, port, timeout=timeout)) as http: 1.154 + # Output some debug information when we have tried more than 2 times. 1.155 + http.set_debuglevel(i >= 2) 1.156 + http.request(command, path) 1.157 + r = http.getresponse() 1.158 + content = r.read() 1.159 + if r.status == 200 and r.reason == 'OK' and content == expected_read: 1.160 + return (True, '') 1.161 + client_error = ('Bad response: %s %s version %s\n ' % 1.162 + (r.status, r.reason, r.version) + 1.163 + '\n '.join([': '.join(h) for h in r.getheaders()])) 1.164 + except (httplib.HTTPException, socket.error) as e: 1.165 + # Probably too quick connecting: try again. 1.166 + exception_error_msgs = traceback.format_exception_only(type(e), e) 1.167 + if exception_error_msgs: 1.168 + client_error = ''.join(exception_error_msgs) 1.169 + # Only returns last client_error. 1.170 + return (False, client_error or 'Timeout')