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

Side by Side Diff: build/android/lighttpd_server.py

Issue 8364020: Upstream: Test scripts for Android (phase 2) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed comments Created 9 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 #!/usr/bin/python
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Provides a convenient wrapper for spawning a test lighttpd instance.
7
8 Usage:
9 lighttpd_server PATH_TO_DOC_ROOT
10 """
11
12 import codecs
13 import contextlib
14 import httplib
15 import os
16 import pexpect
17 import random
18 import shutil
19 import socket
20 import sys
21 import tempfile
22
23
24 class LighttpdServer(object):
25 """Wraps lighttpd server, providing robust startup.
26
27 Args:
28 document_root: Path to root of this server's hosted files.
29 port: TCP port on the _host_ machine that the server will listen on. If
30 ommitted it will attempt to use 9000, or if unavailable it will find
31 a free port from 8001 - 8999.
32 lighttpd_path, lighttpd_module_path: Optional paths to lighttpd binaries.
33 base_config_path: If supplied this file will replace the built-in default
34 lighttpd config file.
35 extra_config_contents: If specified, this string will be appended to the
36 base config (default built-in, or from base_config_path).
37 config_path, error_log, access_log: Optional paths where the class should
38 place temprary files for this session.
39 """
40
41 def __init__(self, document_root, port=None,
42 lighttpd_path=None, lighttpd_module_path=None,
43 base_config_path=None, extra_config_contents=None,
44 config_path=None, error_log=None, access_log=None):
45 self.temp_dir = tempfile.mkdtemp(prefix='lighttpd_for_chrome_android')
46 self.document_root = os.path.abspath(document_root)
47 self.fixed_port = port
48 self.port = port or 9000
49 self.server_tag = 'LightTPD ' + str(random.randint(111111, 999999))
50 self.lighttpd_path = lighttpd_path or '/usr/sbin/lighttpd'
51 self.lighttpd_module_path = lighttpd_module_path or '/usr/lib/lighttpd'
52 self.base_config_path = base_config_path
53 self.extra_config_contents = extra_config_contents
54 self.config_path = config_path or self._Mktmp('config')
55 self.error_log = error_log or self._Mktmp('error_log')
56 self.access_log = access_log or self._Mktmp('access_log')
57 self.pid_file = self._Mktmp('pid_file')
58 self.process = None
59
60 def _Mktmp(self, name):
61 return os.path.join(self.temp_dir, name)
62
63 def _GetRandomPort(self):
64 # Ports 8001-8004 are reserved for other test servers. Ensure we don't
65 # collide with them.
66 return random.randint(8005, 8999)
67
68 def StartupHttpServer(self):
69 """Starts up a http server with specified document root and port."""
70 # Currently we use lighttpd as http sever in test.
71 while True:
72 if self.base_config_path:
73 # Read the config
74 with codecs.open(self.base_config_path, 'r', 'utf-8') as f:
75 config_contents = f.read()
76 else:
77 config_contents = self._GetDefaultBaseConfig()
78 if self.extra_config_contents:
79 config_contents += self.extra_config_contents
80 # Write out the config, filling in placeholders from the members of |self|
81 with codecs.open(self.config_path, 'w', 'utf-8') as f:
82 f.write(config_contents % self.__dict__)
83 if (not os.path.exists(self.lighttpd_path) or
84 not os.access(self.lighttpd_path, os.X_OK)):
85 raise EnvironmentError(
86 'Could not find lighttpd at %s.\n'
87 'It may need to be installed (e.g. sudo apt-get install lighttpd)'
88 % self.lighttpd_path)
89 self.process = pexpect.spawn(self.lighttpd_path,
90 ['-D', '-f', self.config_path,
91 '-m', self.lighttpd_module_path],
92 cwd=self.temp_dir)
93 client_error, server_error = self._TestServerConnection()
94 if not client_error:
95 assert int(open(self.pid_file, 'r').read()) == self.process.pid
96 break
97 self.process.close()
98
99 if self.fixed_port or not 'in use' in server_error:
100 print 'Client error:', client_error
101 print 'Server error:', server_error
102 return False
103 self.port = self._GetRandomPort()
104 return True
105
106 def ShutdownHttpServer(self):
107 """Shuts down our lighttpd processes."""
108 if self.process:
109 self.process.terminate()
110 shutil.rmtree(self.temp_dir, ignore_errors=True)
111
112 def _TestServerConnection(self):
113 # Wait for server to start
114 # TODO(michaelbai): From Pawel Hajdan Jr. This seems super-flaky. We maybe
115 # use the net/test/test_server that has a more reliable way to wait.
116 # http://codereview.chromium.org/8364020/diff/2001/build/android/lighttpd_se rver.py
joth 2011/10/24 10:10:27 This server has never shown any flakiness to date
michaelbai 2011/10/24 18:33:51 Great! Remove the TODO.
117 server_msg = ''
118 for timeout in xrange(1, 5):
119 client_error = None
120 try:
121 with contextlib.closing(httplib.HTTPConnection(
122 '127.0.0.1', self.port, timeout=timeout)) as http:
123 http.set_debuglevel(timeout > 3)
124 http.request('HEAD', '/')
125 r = http.getresponse()
126 r.read()
127 if (r.status == 200 and r.reason == 'OK' and
128 r.getheader('Server') == self.server_tag):
129 return (None, server_msg)
130 client_error = ('Bad response: %s %s version %s\n ' %
131 (r.status, r.reason, r.version) +
132 '\n '.join([': '.join(h) for h in r.getheaders()]))
133 except (httplib.HTTPException, socket.error) as client_error:
134 pass # Probably too quick connecting: try again
135 # Check for server startup error messages
136 ix = self.process.expect([pexpect.TIMEOUT, pexpect.EOF, '.+'],
137 timeout=timeout)
138 if ix == 2: # stdout spew from the server
139 server_msg += self.process.match.group(0)
140 elif ix == 1: # EOF -- server has quit so giveup.
141 client_error = client_error or 'Server exited'
142 break
143 return (client_error or 'Timeout', server_msg)
144
145 def _GetDefaultBaseConfig(self):
146 return """server.tag = "%(server_tag)s"
147 server.modules = ( "mod_access",
148 "mod_accesslog",
149 "mod_alias",
150 "mod_cgi",
151 "mod_rewrite" )
152
153 # default document root required
154 #server.document-root = "."
155
156 # files to check for if .../ is requested
157 index-file.names = ( "index.php", "index.pl", "index.cgi",
158 "index.html", "index.htm", "default.htm" )
159 # mimetype mapping
160 mimetype.assign = (
161 ".gif" => "image/gif",
162 ".jpg" => "image/jpeg",
163 ".jpeg" => "image/jpeg",
164 ".png" => "image/png",
165 ".svg" => "image/svg+xml",
166 ".css" => "text/css",
167 ".html" => "text/html",
168 ".htm" => "text/html",
169 ".xhtml" => "application/xhtml+xml",
170 ".xhtmlmp" => "application/vnd.wap.xhtml+xml",
171 ".js" => "application/x-javascript",
172 ".log" => "text/plain",
173 ".conf" => "text/plain",
174 ".text" => "text/plain",
175 ".txt" => "text/plain",
176 ".dtd" => "text/xml",
177 ".xml" => "text/xml",
178 ".manifest" => "text/cache-manifest",
179 )
180
181 # Use the "Content-Type" extended attribute to obtain mime type if possible
182 mimetype.use-xattr = "enable"
183
184 ##
185 # which extensions should not be handle via static-file transfer
186 #
187 # .php, .pl, .fcgi are most often handled by mod_fastcgi or mod_cgi
188 static-file.exclude-extensions = ( ".php", ".pl", ".cgi" )
189
190 server.bind = "127.0.0.1"
191 server.port = %(port)s
192
193 ## virtual directory listings
194 dir-listing.activate = "enable"
195 #dir-listing.encoding = "iso-8859-2"
196 #dir-listing.external-css = "style/oldstyle.css"
197
198 ## enable debugging
199 #debug.log-request-header = "enable"
200 #debug.log-response-header = "enable"
201 #debug.log-request-handling = "enable"
202 #debug.log-file-not-found = "enable"
203
204 #### SSL engine
205 #ssl.engine = "enable"
206 #ssl.pemfile = "server.pem"
207
208 # Autogenerated test-specific config follows.
209
210 cgi.assign = ( ".cgi" => "/usr/bin/env",
211 ".pl" => "/usr/bin/env",
212 ".asis" => "/bin/cat",
213 ".php" => "/usr/bin/php-cgi" )
214
215 server.errorlog = "%(error_log)s"
216 accesslog.filename = "%(access_log)s"
217 server.upload-dirs = ( "/tmp" )
218 server.pid-file = "%(pid_file)s"
219 server.document-root = "%(document_root)s"
220
221 """
222
223
224 def main(argv):
225 server = LighttpdServer(*argv[1:])
226 try:
227 if server.StartupHttpServer():
228 raw_input('Server running at http://127.0.0.1:%s -'
229 ' press Enter to exit it.' % server.port)
230 else:
231 print 'Server exit code:', server.process.exitstatus
232 finally:
233 server.ShutdownHttpServer()
234
235
236 if __name__ == '__main__':
237 sys.exit(main(sys.argv))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698