Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / build / android / pylib / ports.py
blobfa0345b6b96efb01d40fe58ea7f355e514e091a6
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
3 # found in the LICENSE file.
5 """Functions that deal with local and device ports."""
7 import contextlib
8 import fcntl
9 import httplib
10 import logging
11 import os
12 import socket
13 import traceback
15 from pylib import constants
18 # The following two methods are used to allocate the port source for various
19 # types of test servers. Because some net-related tests can be run on shards at
20 # same time, it's important to have a mechanism to allocate the port
21 # process-safe. In here, we implement the safe port allocation by leveraging
22 # flock.
23 def ResetTestServerPortAllocation():
24 """Resets the port allocation to start from TEST_SERVER_PORT_FIRST.
26 Returns:
27 Returns True if reset successes. Otherwise returns False.
28 """
29 try:
30 with open(constants.TEST_SERVER_PORT_FILE, 'w') as fp:
31 fp.write('%d' % constants.TEST_SERVER_PORT_FIRST)
32 if os.path.exists(constants.TEST_SERVER_PORT_LOCKFILE):
33 os.unlink(constants.TEST_SERVER_PORT_LOCKFILE)
34 return True
35 except Exception as e:
36 logging.error(e)
37 return False
40 def AllocateTestServerPort():
41 """Allocates a port incrementally.
43 Returns:
44 Returns a valid port which should be in between TEST_SERVER_PORT_FIRST and
45 TEST_SERVER_PORT_LAST. Returning 0 means no more valid port can be used.
46 """
47 port = 0
48 ports_tried = []
49 try:
50 fp_lock = open(constants.TEST_SERVER_PORT_LOCKFILE, 'w')
51 fcntl.flock(fp_lock, fcntl.LOCK_EX)
52 # Get current valid port and calculate next valid port.
53 if not os.path.exists(constants.TEST_SERVER_PORT_FILE):
54 ResetTestServerPortAllocation()
55 with open(constants.TEST_SERVER_PORT_FILE, 'r+') as fp:
56 port = int(fp.read())
57 ports_tried.append(port)
58 while not IsHostPortAvailable(port):
59 port += 1
60 ports_tried.append(port)
61 if (port > constants.TEST_SERVER_PORT_LAST or
62 port < constants.TEST_SERVER_PORT_FIRST):
63 port = 0
64 else:
65 fp.seek(0, os.SEEK_SET)
66 fp.write('%d' % (port + 1))
67 except Exception as e:
68 logging.error(e)
69 finally:
70 if fp_lock:
71 fcntl.flock(fp_lock, fcntl.LOCK_UN)
72 fp_lock.close()
73 if port:
74 logging.info('Allocate port %d for test server.', port)
75 else:
76 logging.error('Could not allocate port for test server. '
77 'List of ports tried: %s', str(ports_tried))
78 return port
81 def IsHostPortAvailable(host_port):
82 """Checks whether the specified host port is available.
84 Args:
85 host_port: Port on host to check.
87 Returns:
88 True if the port on host is available, otherwise returns False.
89 """
90 s = socket.socket()
91 try:
92 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
93 s.bind(('', host_port))
94 s.close()
95 return True
96 except socket.error:
97 return False
100 def IsDevicePortUsed(device, device_port, state=''):
101 """Checks whether the specified device port is used or not.
103 Args:
104 device: A DeviceUtils instance.
105 device_port: Port on device we want to check.
106 state: String of the specified state. Default is empty string, which
107 means any state.
109 Returns:
110 True if the port on device is already used, otherwise returns False.
112 base_urls = ('127.0.0.1:%d' % device_port, 'localhost:%d' % device_port)
113 netstat_results = device.RunShellCommand(
114 ['netstat', '-a'], check_return=True, large_output=True)
115 for single_connect in netstat_results:
116 # Column 3 is the local address which we want to check with.
117 connect_results = single_connect.split()
118 if connect_results[0] != 'tcp':
119 continue
120 if len(connect_results) < 6:
121 raise Exception('Unexpected format while parsing netstat line: ' +
122 single_connect)
123 is_state_match = connect_results[5] == state if state else True
124 if connect_results[3] in base_urls and is_state_match:
125 return True
126 return False
129 def IsHttpServerConnectable(host, port, tries=3, command='GET', path='/',
130 expected_read='', timeout=2):
131 """Checks whether the specified http server is ready to serve request or not.
133 Args:
134 host: Host name of the HTTP server.
135 port: Port number of the HTTP server.
136 tries: How many times we want to test the connection. The default value is
138 command: The http command we use to connect to HTTP server. The default
139 command is 'GET'.
140 path: The path we use when connecting to HTTP server. The default path is
141 '/'.
142 expected_read: The content we expect to read from the response. The default
143 value is ''.
144 timeout: Timeout (in seconds) for each http connection. The default is 2s.
146 Returns:
147 Tuple of (connect status, client error). connect status is a boolean value
148 to indicate whether the server is connectable. client_error is the error
149 message the server returns when connect status is false.
151 assert tries >= 1
152 for i in xrange(0, tries):
153 client_error = None
154 try:
155 with contextlib.closing(httplib.HTTPConnection(
156 host, port, timeout=timeout)) as http:
157 # Output some debug information when we have tried more than 2 times.
158 http.set_debuglevel(i >= 2)
159 http.request(command, path)
160 r = http.getresponse()
161 content = r.read()
162 if r.status == 200 and r.reason == 'OK' and content == expected_read:
163 return (True, '')
164 client_error = ('Bad response: %s %s version %s\n ' %
165 (r.status, r.reason, r.version) +
166 '\n '.join([': '.join(h) for h in r.getheaders()]))
167 except (httplib.HTTPException, socket.error) as e:
168 # Probably too quick connecting: try again.
169 exception_error_msgs = traceback.format_exception_only(type(e), e)
170 if exception_error_msgs:
171 client_error = ''.join(exception_error_msgs)
172 # Only returns last client_error.
173 return (False, client_error or 'Timeout')