Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
michael@0 | 2 | # Use of this source code is governed by a BSD-style license that can be |
michael@0 | 3 | # found in the LICENSE file. |
michael@0 | 4 | |
michael@0 | 5 | """Functions that deals with local and device ports.""" |
michael@0 | 6 | |
michael@0 | 7 | import contextlib |
michael@0 | 8 | import fcntl |
michael@0 | 9 | import httplib |
michael@0 | 10 | import logging |
michael@0 | 11 | import os |
michael@0 | 12 | import re |
michael@0 | 13 | import socket |
michael@0 | 14 | import traceback |
michael@0 | 15 | |
michael@0 | 16 | import cmd_helper |
michael@0 | 17 | import constants |
michael@0 | 18 | |
michael@0 | 19 | |
michael@0 | 20 | #The following two methods are used to allocate the port source for various |
michael@0 | 21 | # types of test servers. Because some net relates tests can be run on shards |
michael@0 | 22 | # at same time, it's important to have a mechanism to allocate the port process |
michael@0 | 23 | # safe. In here, we implement the safe port allocation by leveraging flock. |
michael@0 | 24 | def ResetTestServerPortAllocation(): |
michael@0 | 25 | """Reset the port allocation to start from TEST_SERVER_PORT_FIRST. |
michael@0 | 26 | |
michael@0 | 27 | Returns: |
michael@0 | 28 | Returns True if reset successes. Otherwise returns False. |
michael@0 | 29 | """ |
michael@0 | 30 | try: |
michael@0 | 31 | with open(constants.TEST_SERVER_PORT_FILE, 'w') as fp: |
michael@0 | 32 | fp.write('%d' % constants.TEST_SERVER_PORT_FIRST) |
michael@0 | 33 | if os.path.exists(constants.TEST_SERVER_PORT_LOCKFILE): |
michael@0 | 34 | os.unlink(constants.TEST_SERVER_PORT_LOCKFILE) |
michael@0 | 35 | return True |
michael@0 | 36 | except Exception as e: |
michael@0 | 37 | logging.error(e) |
michael@0 | 38 | return False |
michael@0 | 39 | |
michael@0 | 40 | |
michael@0 | 41 | def AllocateTestServerPort(): |
michael@0 | 42 | """Allocate a port incrementally. |
michael@0 | 43 | |
michael@0 | 44 | Returns: |
michael@0 | 45 | Returns a valid port which should be in between TEST_SERVER_PORT_FIRST and |
michael@0 | 46 | TEST_SERVER_PORT_LAST. Returning 0 means no more valid port can be used. |
michael@0 | 47 | """ |
michael@0 | 48 | port = 0 |
michael@0 | 49 | ports_tried = [] |
michael@0 | 50 | try: |
michael@0 | 51 | fp_lock = open(constants.TEST_SERVER_PORT_LOCKFILE, 'w') |
michael@0 | 52 | fcntl.flock(fp_lock, fcntl.LOCK_EX) |
michael@0 | 53 | # Get current valid port and calculate next valid port. |
michael@0 | 54 | assert os.path.exists(constants.TEST_SERVER_PORT_FILE) |
michael@0 | 55 | with open(constants.TEST_SERVER_PORT_FILE, 'r+') as fp: |
michael@0 | 56 | port = int(fp.read()) |
michael@0 | 57 | ports_tried.append(port) |
michael@0 | 58 | while IsHostPortUsed(port): |
michael@0 | 59 | port += 1 |
michael@0 | 60 | ports_tried.append(port) |
michael@0 | 61 | if (port > constants.TEST_SERVER_PORT_LAST or |
michael@0 | 62 | port < constants.TEST_SERVER_PORT_FIRST): |
michael@0 | 63 | port = 0 |
michael@0 | 64 | else: |
michael@0 | 65 | fp.seek(0, os.SEEK_SET) |
michael@0 | 66 | fp.write('%d' % (port + 1)) |
michael@0 | 67 | except Exception as e: |
michael@0 | 68 | logging.info(e) |
michael@0 | 69 | finally: |
michael@0 | 70 | if fp_lock: |
michael@0 | 71 | fcntl.flock(fp_lock, fcntl.LOCK_UN) |
michael@0 | 72 | fp_lock.close() |
michael@0 | 73 | if port: |
michael@0 | 74 | logging.info('Allocate port %d for test server.', port) |
michael@0 | 75 | else: |
michael@0 | 76 | logging.error('Could not allocate port for test server. ' |
michael@0 | 77 | 'List of ports tried: %s', str(ports_tried)) |
michael@0 | 78 | return port |
michael@0 | 79 | |
michael@0 | 80 | |
michael@0 | 81 | def IsHostPortUsed(host_port): |
michael@0 | 82 | """Checks whether the specified host port is used or not. |
michael@0 | 83 | |
michael@0 | 84 | Uses -n -P to inhibit the conversion of host/port numbers to host/port names. |
michael@0 | 85 | |
michael@0 | 86 | Args: |
michael@0 | 87 | host_port: Port on host we want to check. |
michael@0 | 88 | |
michael@0 | 89 | Returns: |
michael@0 | 90 | True if the port on host is already used, otherwise returns False. |
michael@0 | 91 | """ |
michael@0 | 92 | port_info = '(127\.0\.0\.1)|(localhost)\:%d' % host_port |
michael@0 | 93 | # TODO(jnd): Find a better way to filter the port. |
michael@0 | 94 | re_port = re.compile(port_info, re.MULTILINE) |
michael@0 | 95 | if re_port.findall(cmd_helper.GetCmdOutput(['lsof', '-nPi:%d' % host_port])): |
michael@0 | 96 | return True |
michael@0 | 97 | return False |
michael@0 | 98 | |
michael@0 | 99 | |
michael@0 | 100 | def IsDevicePortUsed(adb, device_port, state=''): |
michael@0 | 101 | """Checks whether the specified device port is used or not. |
michael@0 | 102 | |
michael@0 | 103 | Args: |
michael@0 | 104 | adb: Instance of AndroidCommands for talking to the device. |
michael@0 | 105 | device_port: Port on device we want to check. |
michael@0 | 106 | state: String of the specified state. Default is empty string, which |
michael@0 | 107 | means any state. |
michael@0 | 108 | |
michael@0 | 109 | Returns: |
michael@0 | 110 | True if the port on device is already used, otherwise returns False. |
michael@0 | 111 | """ |
michael@0 | 112 | base_url = '127.0.0.1:%d' % device_port |
michael@0 | 113 | netstat_results = adb.RunShellCommand('netstat', log_result=False) |
michael@0 | 114 | for single_connect in netstat_results: |
michael@0 | 115 | # Column 3 is the local address which we want to check with. |
michael@0 | 116 | connect_results = single_connect.split() |
michael@0 | 117 | is_state_match = connect_results[5] == state if state else True |
michael@0 | 118 | if connect_results[3] == base_url and is_state_match: |
michael@0 | 119 | return True |
michael@0 | 120 | return False |
michael@0 | 121 | |
michael@0 | 122 | |
michael@0 | 123 | def IsHttpServerConnectable(host, port, tries=3, command='GET', path='/', |
michael@0 | 124 | expected_read='', timeout=2): |
michael@0 | 125 | """Checks whether the specified http server is ready to serve request or not. |
michael@0 | 126 | |
michael@0 | 127 | Args: |
michael@0 | 128 | host: Host name of the HTTP server. |
michael@0 | 129 | port: Port number of the HTTP server. |
michael@0 | 130 | tries: How many times we want to test the connection. The default value is |
michael@0 | 131 | 3. |
michael@0 | 132 | command: The http command we use to connect to HTTP server. The default |
michael@0 | 133 | command is 'GET'. |
michael@0 | 134 | path: The path we use when connecting to HTTP server. The default path is |
michael@0 | 135 | '/'. |
michael@0 | 136 | expected_read: The content we expect to read from the response. The default |
michael@0 | 137 | value is ''. |
michael@0 | 138 | timeout: Timeout (in seconds) for each http connection. The default is 2s. |
michael@0 | 139 | |
michael@0 | 140 | Returns: |
michael@0 | 141 | Tuple of (connect status, client error). connect status is a boolean value |
michael@0 | 142 | to indicate whether the server is connectable. client_error is the error |
michael@0 | 143 | message the server returns when connect status is false. |
michael@0 | 144 | """ |
michael@0 | 145 | assert tries >= 1 |
michael@0 | 146 | for i in xrange(0, tries): |
michael@0 | 147 | client_error = None |
michael@0 | 148 | try: |
michael@0 | 149 | with contextlib.closing(httplib.HTTPConnection( |
michael@0 | 150 | host, port, timeout=timeout)) as http: |
michael@0 | 151 | # Output some debug information when we have tried more than 2 times. |
michael@0 | 152 | http.set_debuglevel(i >= 2) |
michael@0 | 153 | http.request(command, path) |
michael@0 | 154 | r = http.getresponse() |
michael@0 | 155 | content = r.read() |
michael@0 | 156 | if r.status == 200 and r.reason == 'OK' and content == expected_read: |
michael@0 | 157 | return (True, '') |
michael@0 | 158 | client_error = ('Bad response: %s %s version %s\n ' % |
michael@0 | 159 | (r.status, r.reason, r.version) + |
michael@0 | 160 | '\n '.join([': '.join(h) for h in r.getheaders()])) |
michael@0 | 161 | except (httplib.HTTPException, socket.error) as e: |
michael@0 | 162 | # Probably too quick connecting: try again. |
michael@0 | 163 | exception_error_msgs = traceback.format_exception_only(type(e), e) |
michael@0 | 164 | if exception_error_msgs: |
michael@0 | 165 | client_error = ''.join(exception_error_msgs) |
michael@0 | 166 | # Only returns last client_error. |
michael@0 | 167 | return (False, client_error or 'Timeout') |