1# Copyright 2013 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.
4
5import atexit
6import os
7import socket
8import subprocess
9import time
10import urllib2
11
12
13class Server(object):
14  """A running ChromeDriver server."""
15
16  def __init__(self, exe_path, log_path=None):
17    """Starts the ChromeDriver server and waits for it to be ready.
18
19    Args:
20      exe_path: path to the ChromeDriver executable
21      log_path: path to the log file
22    Raises:
23      RuntimeError if ChromeDriver fails to start
24    """
25    if not os.path.exists(exe_path):
26      raise RuntimeError('ChromeDriver exe not found at: ' + exe_path)
27
28    port = self._FindOpenPort()
29    chromedriver_args = [exe_path, '--port=%d' % port]
30    if log_path:
31      chromedriver_args.extend(['--verbose', '--log-path=%s' % log_path])
32    self._process = subprocess.Popen(chromedriver_args)
33    self._url = 'http://127.0.0.1:%d' % port
34    if self._process is None:
35      raise RuntimeError('ChromeDriver server cannot be started')
36
37    max_time = time.time() + 10
38    while not self.IsRunning():
39      if time.time() > max_time:
40        self._process.terminate()
41        raise RuntimeError('ChromeDriver server did not start')
42      time.sleep(0.1)
43
44    atexit.register(self.Kill)
45
46  def _FindOpenPort(self):
47    for port in range(9500, 10000):
48      try:
49        socket.create_connection(('127.0.0.1', port), 0.2).close()
50      except socket.error:
51        return port
52    raise RuntimeError('Cannot find open port to launch ChromeDriver')
53
54  def GetUrl(self):
55    return self._url
56
57  def IsRunning(self):
58    """Returns whether the server is up and running."""
59    try:
60      urllib2.urlopen(self.GetUrl() + '/status')
61      return True
62    except urllib2.URLError:
63      return False
64
65  def Kill(self):
66    """Kills the ChromeDriver server, if it is running."""
67    if self._process is None:
68      return
69
70    try:
71      urllib2.urlopen(self.GetUrl() + '/shutdown', timeout=10).close()
72    except:
73      self._process.terminate()
74    self._process.wait()
75    self._process = None
76