| OLD | NEW |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """Functions that deal with local and device ports.""" | 5 """Functions that deal with local and device ports.""" |
| 6 | 6 |
| 7 import contextlib | 7 import contextlib |
| 8 import fcntl | 8 import fcntl |
| 9 import httplib | 9 import httplib |
| 10 import logging | 10 import logging |
| 11 import os | 11 import os |
| 12 import re | |
| 13 import socket | 12 import socket |
| 14 import traceback | 13 import traceback |
| 15 | 14 |
| 16 from pylib import cmd_helper | |
| 17 from pylib import constants | 15 from pylib import constants |
| 18 | 16 |
| 19 | 17 |
| 20 # The following two methods are used to allocate the port source for various | 18 # The following two methods are used to allocate the port source for various |
| 21 # types of test servers. Because some net-related tests can be run on shards at | 19 # types of test servers. Because some net-related tests can be run on shards at |
| 22 # same time, it's important to have a mechanism to allocate the port | 20 # same time, it's important to have a mechanism to allocate the port |
| 23 # process-safe. In here, we implement the safe port allocation by leveraging | 21 # process-safe. In here, we implement the safe port allocation by leveraging |
| 24 # flock. | 22 # flock. |
| 25 def ResetTestServerPortAllocation(): | 23 def ResetTestServerPortAllocation(): |
| 26 """Resets the port allocation to start from TEST_SERVER_PORT_FIRST. | 24 """Resets the port allocation to start from TEST_SERVER_PORT_FIRST. |
| (...skipping 23 matching lines...) Expand all Loading... |
| 50 ports_tried = [] | 48 ports_tried = [] |
| 51 try: | 49 try: |
| 52 fp_lock = open(constants.TEST_SERVER_PORT_LOCKFILE, 'w') | 50 fp_lock = open(constants.TEST_SERVER_PORT_LOCKFILE, 'w') |
| 53 fcntl.flock(fp_lock, fcntl.LOCK_EX) | 51 fcntl.flock(fp_lock, fcntl.LOCK_EX) |
| 54 # Get current valid port and calculate next valid port. | 52 # Get current valid port and calculate next valid port. |
| 55 if not os.path.exists(constants.TEST_SERVER_PORT_FILE): | 53 if not os.path.exists(constants.TEST_SERVER_PORT_FILE): |
| 56 ResetTestServerPortAllocation() | 54 ResetTestServerPortAllocation() |
| 57 with open(constants.TEST_SERVER_PORT_FILE, 'r+') as fp: | 55 with open(constants.TEST_SERVER_PORT_FILE, 'r+') as fp: |
| 58 port = int(fp.read()) | 56 port = int(fp.read()) |
| 59 ports_tried.append(port) | 57 ports_tried.append(port) |
| 60 while IsHostPortUsed(port): | 58 while not IsHostPortAvailable(port): |
| 61 port += 1 | 59 port += 1 |
| 62 ports_tried.append(port) | 60 ports_tried.append(port) |
| 63 if (port > constants.TEST_SERVER_PORT_LAST or | 61 if (port > constants.TEST_SERVER_PORT_LAST or |
| 64 port < constants.TEST_SERVER_PORT_FIRST): | 62 port < constants.TEST_SERVER_PORT_FIRST): |
| 65 port = 0 | 63 port = 0 |
| 66 else: | 64 else: |
| 67 fp.seek(0, os.SEEK_SET) | 65 fp.seek(0, os.SEEK_SET) |
| 68 fp.write('%d' % (port + 1)) | 66 fp.write('%d' % (port + 1)) |
| 69 except Exception as e: | 67 except Exception as e: |
| 70 logging.info(e) | 68 logging.error(e) |
| 71 finally: | 69 finally: |
| 72 if fp_lock: | 70 if fp_lock: |
| 73 fcntl.flock(fp_lock, fcntl.LOCK_UN) | 71 fcntl.flock(fp_lock, fcntl.LOCK_UN) |
| 74 fp_lock.close() | 72 fp_lock.close() |
| 75 if port: | 73 if port: |
| 76 logging.info('Allocate port %d for test server.', port) | 74 logging.info('Allocate port %d for test server.', port) |
| 77 else: | 75 else: |
| 78 logging.error('Could not allocate port for test server. ' | 76 logging.error('Could not allocate port for test server. ' |
| 79 'List of ports tried: %s', str(ports_tried)) | 77 'List of ports tried: %s', str(ports_tried)) |
| 80 return port | 78 return port |
| 81 | 79 |
| 82 | 80 |
| 83 def IsHostPortUsed(host_port): | 81 def IsHostPortAvailable(host_port): |
| 84 """Checks whether the specified host port is used or not. | 82 """Checks whether the specified host port is available. |
| 85 | |
| 86 Uses -n -P to inhibit the conversion of host/port numbers to host/port names. | |
| 87 | 83 |
| 88 Args: | 84 Args: |
| 89 host_port: Port on host we want to check. | 85 host_port: Port on host to check. |
| 90 | 86 |
| 91 Returns: | 87 Returns: |
| 92 True if the port on host is already used, otherwise returns False. | 88 True if the port on host is available, otherwise returns False. |
| 93 """ | 89 """ |
| 94 port_info = '(\*)|(127\.0\.0\.1)|(localhost):%d' % host_port | 90 s = socket.socket() |
| 95 # TODO(jnd): Find a better way to filter the port. Note that connecting to the | 91 try: |
| 96 # socket and closing it would leave it in the TIME_WAIT state. Setting | 92 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
| 97 # SO_LINGER on it and then closing it makes the Python HTTP server crash. | 93 s.bind(('', host_port)) |
| 98 re_port = re.compile(port_info, re.MULTILINE) | 94 s.close() |
| 99 if re_port.search(cmd_helper.GetCmdOutput(['lsof', '-nPi:%d' % host_port])): | |
| 100 return True | 95 return True |
| 101 return False | 96 except socket.error: |
| 97 return False |
| 102 | 98 |
| 103 | 99 |
| 104 def IsDevicePortUsed(device, device_port, state=''): | 100 def IsDevicePortUsed(device, device_port, state=''): |
| 105 """Checks whether the specified device port is used or not. | 101 """Checks whether the specified device port is used or not. |
| 106 | 102 |
| 107 Args: | 103 Args: |
| 108 device: A DeviceUtils instance. | 104 device: A DeviceUtils instance. |
| 109 device_port: Port on device we want to check. | 105 device_port: Port on device we want to check. |
| 110 state: String of the specified state. Default is empty string, which | 106 state: String of the specified state. Default is empty string, which |
| 111 means any state. | 107 means any state. |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 167 client_error = ('Bad response: %s %s version %s\n ' % | 163 client_error = ('Bad response: %s %s version %s\n ' % |
| 168 (r.status, r.reason, r.version) + | 164 (r.status, r.reason, r.version) + |
| 169 '\n '.join([': '.join(h) for h in r.getheaders()])) | 165 '\n '.join([': '.join(h) for h in r.getheaders()])) |
| 170 except (httplib.HTTPException, socket.error) as e: | 166 except (httplib.HTTPException, socket.error) as e: |
| 171 # Probably too quick connecting: try again. | 167 # Probably too quick connecting: try again. |
| 172 exception_error_msgs = traceback.format_exception_only(type(e), e) | 168 exception_error_msgs = traceback.format_exception_only(type(e), e) |
| 173 if exception_error_msgs: | 169 if exception_error_msgs: |
| 174 client_error = ''.join(exception_error_msgs) | 170 client_error = ''.join(exception_error_msgs) |
| 175 # Only returns last client_error. | 171 # Only returns last client_error. |
| 176 return (False, client_error or 'Timeout') | 172 return (False, client_error or 'Timeout') |
| OLD | NEW |