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

Side by Side Diff: mojo/devtools/common/devtoolslib/http_server.py

Issue 1279543003: Devtools: gzip the file being served using a temporary file. (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Address Ben's comments. Created 5 years, 4 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 9 import gzip
10 import hashlib 10 import hashlib
11 import logging 11 import logging
12 import math 12 import math
13 import os.path 13 import os.path
14 import shutil 14 import shutil
15 import socket 15 import socket
16 import threading 16 import threading
17 import tempfile
17 18
18 import SimpleHTTPServer 19 import SimpleHTTPServer
19 import SocketServer 20 import SocketServer
20 21
21 _ZERO = datetime.timedelta(0) 22 _ZERO = datetime.timedelta(0)
22 23
23 24
24 class UTC_TZINFO(datetime.tzinfo): 25 class UTC_TZINFO(datetime.tzinfo):
25 """UTC time zone representation.""" 26 """UTC time zone representation."""
26 27
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
59 The first matching prefix and the first location that contains the 60 The first matching prefix and the first location that contains the
60 requested file will be used each time. 61 requested file will be used each time.
61 """ 62 """
62 for prefix, _ in mappings: 63 for prefix, _ in mappings:
63 assert not prefix.startswith('/'), ('Prefixes for the http server mappings ' 64 assert not prefix.startswith('/'), ('Prefixes for the http server mappings '
64 'should skip the leading slash.') 65 'should skip the leading slash.')
65 66
66 class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 67 class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
67 """Handler for SocketServer.TCPServer that will serve the files from 68 """Handler for SocketServer.TCPServer that will serve the files from
68 local directiories over http. 69 local directiories over http.
70
71 A new instance is created for each request.
69 """ 72 """
70 73
71 def __init__(self, *args, **kwargs): 74 def __init__(self, *args, **kwargs):
72 self.etag = None 75 self.etag = None
76 self.gzipped_file = None
73 SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, *args, **kwargs) 77 SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, *args, **kwargs)
74 78
75 def get_etag(self): 79 def get_etag(self):
76 if self.etag: 80 if self.etag:
77 return self.etag 81 return self.etag
78 82
79 path = self.translate_path(self.path, False) 83 path = self.translate_path(self.path, False)
80 if not os.path.isfile(path): 84 if not os.path.isfile(path):
81 return None 85 return None
82 86
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
140 normalized_path = os.path.relpath( 144 normalized_path = os.path.relpath(
141 SimpleHTTPServer.SimpleHTTPRequestHandler.translate_path(self, path)) 145 SimpleHTTPServer.SimpleHTTPRequestHandler.translate_path(self, path))
142 146
143 for prefix, local_base_path_list in mappings: 147 for prefix, local_base_path_list in mappings:
144 if normalized_path.startswith(prefix): 148 if normalized_path.startswith(prefix):
145 for local_base_path in local_base_path_list: 149 for local_base_path in local_base_path_list:
146 candidate = os.path.join(local_base_path, 150 candidate = os.path.join(local_base_path,
147 normalized_path[len(prefix):]) 151 normalized_path[len(prefix):])
148 if os.path.isfile(candidate): 152 if os.path.isfile(candidate):
149 if gzipped: 153 if gzipped:
150 gz_result = candidate + '.gz' 154 if not self.gzipped_file:
151 if (not os.path.isfile(gz_result) or 155 self.gzipped_file = tempfile.NamedTemporaryFile(delete=False)
152 os.path.getmtime(gz_result) <= os.path.getmtime(candidate)): 156 with open(candidate, 'rb') as source:
153 with open(candidate, 'rb') as f: 157 with gzip.GzipFile(fileobj=self.gzipped_file) as target:
154 with gzip.open(gz_result, 'wb') as zf: 158 shutil.copyfileobj(source, target)
155 shutil.copyfileobj(f, zf) 159 self.gzipped_file.close()
156 return gz_result 160 return self.gzipped_file.name
157 return candidate 161 return candidate
158 else: 162 else:
159 self.send_response(404) 163 self.send_response(404)
160 return None 164 return None
161 self.send_response(404) 165 self.send_response(404)
162 return None 166 return None
163 167
164 def guess_type(self, path): 168 def guess_type(self, path):
165 # This is needed so that exploded Sky apps without shebang can still run 169 # This is needed so that exploded Sky apps without shebang can still run
166 # thanks to content-type mappings. 170 # thanks to content-type mappings.
167 # TODO(ppi): drop this part once we can rely on the Sky files declaring 171 # TODO(ppi): drop this part once we can rely on the Sky files declaring
168 # correct shebang. 172 # correct shebang.
169 if path.endswith('.dart') or path.endswith('.dart.gz'): 173 if path.endswith('.dart') or path.endswith('.dart.gz'):
170 return 'application/dart' 174 return 'application/dart'
171 return SimpleHTTPServer.SimpleHTTPRequestHandler.guess_type(self, path) 175 return SimpleHTTPServer.SimpleHTTPRequestHandler.guess_type(self, path)
172 176
173 def log_message(self, *_): 177 def log_message(self, *_):
174 """Override the base class method to disable logging.""" 178 """Override the base class method to disable logging."""
175 pass 179 pass
176 180
181 def __del__(self):
182 if self.gzipped_file:
183 os.remove(self.gzipped_file.name)
184
177 RequestHandler.protocol_version = 'HTTP/1.1' 185 RequestHandler.protocol_version = 'HTTP/1.1'
178 return RequestHandler 186 return RequestHandler
179 187
180 188
181 def start_http_server(mappings, host_port=0): 189 def start_http_server(mappings, host_port=0):
182 """Starts an http server serving files from |local_dir_path| on |host_port|. 190 """Starts an http server serving files from |local_dir_path| on |host_port|.
183 191
184 Args: 192 Args:
185 mappings: List of tuples (prefix, local_base_path_list) mapping URLs that 193 mappings: List of tuples (prefix, local_base_path_list) mapping URLs that
186 start with |prefix| to one or more local directories enumerated in 194 start with |prefix| to one or more local directories enumerated in
(...skipping 19 matching lines...) Expand all
206 return httpd.server_address 214 return httpd.server_address
207 except socket.error as v: 215 except socket.error as v:
208 error_code = v[0] 216 error_code = v[0]
209 print 'Failed to start http server for %s on port %d: %s.' % ( 217 print 'Failed to start http server for %s on port %d: %s.' % (
210 str(mappings), host_port, os.strerror(error_code)) 218 str(mappings), host_port, os.strerror(error_code))
211 if error_code == errno.EADDRINUSE: 219 if error_code == errno.EADDRINUSE:
212 print (' Run `fuser %d/tcp` to find out which process is using the port.' 220 print (' Run `fuser %d/tcp` to find out which process is using the port.'
213 % host_port) 221 % host_port)
214 print '---' 222 print '---'
215 raise 223 raise
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698