OLD | NEW |
1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 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 import atexit | 5 import atexit |
6 import datetime | 6 import datetime |
7 import email.utils | 7 import email.utils |
8 import errno | 8 import errno |
9 import gzip | |
10 import hashlib | 9 import hashlib |
11 import logging | 10 import logging |
12 import math | 11 import math |
13 import os.path | 12 import os.path |
14 import shutil | |
15 import socket | 13 import socket |
| 14 import subprocess |
| 15 import tempfile |
16 import threading | 16 import threading |
17 import tempfile | |
18 | 17 |
19 import SimpleHTTPServer | 18 import SimpleHTTPServer |
20 import SocketServer | 19 import SocketServer |
21 | 20 |
22 _ZERO = datetime.timedelta(0) | 21 _ZERO = datetime.timedelta(0) |
23 | 22 |
24 | 23 |
25 class UTC_TZINFO(datetime.tzinfo): | 24 class UTC_TZINFO(datetime.tzinfo): |
26 """UTC time zone representation.""" | 25 """UTC time zone representation.""" |
27 | 26 |
28 def utcoffset(self, _): | 27 def utcoffset(self, _): |
29 return _ZERO | 28 return _ZERO |
30 | 29 |
31 def tzname(self, _): | 30 def tzname(self, _): |
32 return "UTC" | 31 return "UTC" |
33 | 32 |
34 def dst(self, _): | 33 def dst(self, _): |
35 return _ZERO | 34 return _ZERO |
36 | 35 |
37 _UTC = UTC_TZINFO() | 36 _UTC = UTC_TZINFO() |
38 | 37 |
39 | 38 |
| 39 def _gzip(file_path): |
| 40 """Gzips the given file storing the result in a temporary file. |
| 41 |
| 42 Returns: |
| 43 Path to the resulting file. |
| 44 """ |
| 45 gzipped_file = tempfile.NamedTemporaryFile(delete=False) |
| 46 try: |
| 47 subprocess.check_call(['gzip', '-c', file_path], |
| 48 stdout=gzipped_file) |
| 49 except Exception: |
| 50 print ('http_server: call to gzip failed, make sure that ' |
| 51 'gzip is installed on the host.') |
| 52 raise |
| 53 gzipped_file.close() |
| 54 return gzipped_file.name |
| 55 |
| 56 |
40 class _SilentTCPServer(SocketServer.TCPServer): | 57 class _SilentTCPServer(SocketServer.TCPServer): |
41 """A TCPServer that won't display any error, unless debugging is enabled. This | 58 """A TCPServer that won't display any error, unless debugging is enabled. This |
42 is useful because the client might stop while it is fetching an URL, which | 59 is useful because the client might stop while it is fetching an URL, which |
43 causes spurious error messages. | 60 causes spurious error messages. |
44 """ | 61 """ |
45 allow_reuse_address = True | 62 allow_reuse_address = True |
46 | 63 |
47 def handle_error(self, request, client_address): | 64 def handle_error(self, request, client_address): |
48 """Override the base class method to have conditional logging.""" | 65 """Override the base class method to have conditional logging.""" |
49 if logging.getLogger().isEnabledFor(logging.DEBUG): | 66 if logging.getLogger().isEnabledFor(logging.DEBUG): |
(...skipping 16 matching lines...) Expand all Loading... |
66 | 83 |
67 class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): | 84 class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): |
68 """Handler for SocketServer.TCPServer that will serve the files from | 85 """Handler for SocketServer.TCPServer that will serve the files from |
69 local directiories over http. | 86 local directiories over http. |
70 | 87 |
71 A new instance is created for each request. | 88 A new instance is created for each request. |
72 """ | 89 """ |
73 | 90 |
74 def __init__(self, *args, **kwargs): | 91 def __init__(self, *args, **kwargs): |
75 self.etag = None | 92 self.etag = None |
76 self.gzipped_file = None | 93 self.gzipped_file_name = None |
77 self.original_file_name = None | 94 self.original_file_name = None |
78 SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, *args, **kwargs) | 95 SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, *args, **kwargs) |
79 | 96 |
80 def get_etag(self): | 97 def get_etag(self): |
81 if self.etag: | 98 if self.etag: |
82 return self.etag | 99 return self.etag |
83 | 100 |
84 path = self.translate_path(self.path, False) | 101 path = self.translate_path(self.path, False) |
85 if not os.path.isfile(path): | 102 if not os.path.isfile(path): |
86 return None | 103 return None |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
131 if os.path.isfile(path): | 148 if os.path.isfile(path): |
132 self.send_header('Content-Encoding', 'gzip') | 149 self.send_header('Content-Encoding', 'gzip') |
133 etag = self.get_etag() | 150 etag = self.get_etag() |
134 if etag: | 151 if etag: |
135 self.send_header('ETag', etag) | 152 self.send_header('ETag', etag) |
136 self.send_header('Cache-Control', 'must-revalidate') | 153 self.send_header('Cache-Control', 'must-revalidate') |
137 | 154 |
138 return SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self) | 155 return SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self) |
139 | 156 |
140 # pylint: disable=W0221 | 157 # pylint: disable=W0221 |
141 def translate_path(self, path, gzipped=True): | 158 def translate_path(self, path, gzip=True): |
142 # Parent translate_path() will strip away the query string and fragment | 159 # Parent translate_path() will strip away the query string and fragment |
143 # identifier, but also will prepend the cwd to the path. relpath() gives | 160 # identifier, but also will prepend the cwd to the path. relpath() gives |
144 # us the relative path back. | 161 # us the relative path back. |
145 normalized_path = os.path.relpath( | 162 normalized_path = os.path.relpath( |
146 SimpleHTTPServer.SimpleHTTPRequestHandler.translate_path(self, path)) | 163 SimpleHTTPServer.SimpleHTTPRequestHandler.translate_path(self, path)) |
147 | 164 |
148 for prefix, local_base_path_list in mappings: | 165 for prefix, local_base_path_list in mappings: |
149 if normalized_path.startswith(prefix): | 166 if not normalized_path.startswith(prefix): |
150 for local_base_path in local_base_path_list: | 167 continue |
151 candidate = os.path.join(local_base_path, | 168 |
152 normalized_path[len(prefix):]) | 169 for local_base_path in local_base_path_list: |
153 if os.path.isfile(candidate): | 170 candidate = os.path.join(local_base_path, |
154 if gzipped: | 171 normalized_path[len(prefix):]) |
155 if not self.gzipped_file: | 172 if os.path.isfile(candidate): |
156 self.gzipped_file = tempfile.NamedTemporaryFile(delete=False) | 173 if gzip: |
157 self.original_file_name = candidate | 174 if not self.gzipped_file_name: |
158 with open(candidate, 'rb') as source: | 175 self.original_file_name = candidate |
159 with gzip.GzipFile(fileobj=self.gzipped_file) as target: | 176 self.gzipped_file_name = _gzip(candidate) |
160 shutil.copyfileobj(source, target) | 177 return self.gzipped_file_name |
161 self.gzipped_file.close() | 178 return candidate |
162 return self.gzipped_file.name | 179 else: |
163 return candidate | 180 self.send_response(404) |
164 else: | 181 return None |
165 self.send_response(404) | |
166 return None | |
167 self.send_response(404) | 182 self.send_response(404) |
168 return None | 183 return None |
169 | 184 |
170 def guess_type(self, path): | 185 def guess_type(self, path): |
171 # This is needed so that exploded Sky apps without shebang can still run | 186 # This is needed so that exploded Sky apps without shebang can still run |
172 # thanks to content-type mappings. | 187 # thanks to content-type mappings. |
173 # TODO(ppi): drop this part once we can rely on the Sky files declaring | 188 # TODO(ppi): drop this part once we can rely on the Sky files declaring |
174 # correct shebang. | 189 # correct shebang. |
175 actual_path = self.original_file_name or path | 190 actual_path = self.original_file_name or path |
176 | 191 |
177 if actual_path.endswith('.dart'): | 192 if actual_path.endswith('.dart'): |
178 return 'application/dart' | 193 return 'application/dart' |
179 return SimpleHTTPServer.SimpleHTTPRequestHandler.guess_type(self, | 194 return SimpleHTTPServer.SimpleHTTPRequestHandler.guess_type(self, |
180 actual_path) | 195 actual_path) |
181 | 196 |
182 def log_message(self, *_): | 197 def log_message(self, *_): |
183 """Override the base class method to disable logging.""" | 198 """Override the base class method to disable logging.""" |
184 pass | 199 pass |
185 | 200 |
186 def __del__(self): | 201 def __del__(self): |
187 if self.gzipped_file: | 202 if self.gzipped_file_name: |
188 os.remove(self.gzipped_file.name) | 203 os.remove(self.gzipped_file_name) |
189 | 204 |
190 RequestHandler.protocol_version = 'HTTP/1.1' | 205 RequestHandler.protocol_version = 'HTTP/1.1' |
191 return RequestHandler | 206 return RequestHandler |
192 | 207 |
193 | 208 |
194 def start_http_server(mappings, host_port=0): | 209 def start_http_server(mappings, host_port=0): |
195 """Starts an http server serving files from |local_dir_path| on |host_port|. | 210 """Starts an http server serving files from |local_dir_path| on |host_port|. |
196 | 211 |
197 Args: | 212 Args: |
198 mappings: List of tuples (prefix, local_base_path_list) mapping URLs that | 213 mappings: List of tuples (prefix, local_base_path_list) mapping URLs that |
(...skipping 21 matching lines...) Expand all Loading... |
220 except socket.error as v: | 235 except socket.error as v: |
221 error_code = v[0] | 236 error_code = v[0] |
222 print 'Failed to start http server for %s on port %d: %s.' % ( | 237 print 'Failed to start http server for %s on port %d: %s.' % ( |
223 str(mappings), host_port, os.strerror(error_code)) | 238 str(mappings), host_port, os.strerror(error_code)) |
224 if error_code == errno.EADDRINUSE: | 239 if error_code == errno.EADDRINUSE: |
225 print (' Run `fuser %d/tcp` to find out which process is using the port;' | 240 print (' Run `fuser %d/tcp` to find out which process is using the port;' |
226 % host_port) | 241 % host_port) |
227 print (' or `fuser -k %d/tcp` terminate it.' % host_port) | 242 print (' or `fuser -k %d/tcp` terminate it.' % host_port) |
228 print '---' | 243 print '---' |
229 raise | 244 raise |
OLD | NEW |