| 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 |