Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(51)

Side by Side Diff: build/android/pylib/ports.py

Issue 634803002: Make host port availability detection more robust. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Allow reuseaddr before binding Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « build/android/pylib/chrome_test_server_spawner.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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')
OLDNEW
« no previous file with comments | « build/android/pylib/chrome_test_server_spawner.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698