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

Side by Side Diff: mojo/devtools/common/pylib/android.py

Issue 1109493005: Extract http_server.py from android.py. (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Fix a typo. Created 5 years, 8 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 | mojo/devtools/common/pylib/http_server.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 1 # Copyright 2014 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
7 import email.utils
8 import hashlib
9 import itertools 6 import itertools
10 import json 7 import json
11 import logging 8 import logging
12 import math
13 import os 9 import os
14 import os.path 10 import os.path
15 import random 11 import random
16 import subprocess 12 import subprocess
17 import sys 13 import sys
18 import threading 14 import threading
19 import time 15 import time
20 import urlparse 16 import urlparse
21 17
22 import SimpleHTTPServer 18 from pylib.http_server import StartHttpServer
23 import SocketServer
24 19
25 20
26 # Tags used by the mojo shell application logs. 21 # Tags used by the mojo shell application logs.
27 LOGCAT_TAGS = [ 22 LOGCAT_TAGS = [
28 'AndroidHandler', 23 'AndroidHandler',
29 'MojoFileHelper', 24 'MojoFileHelper',
30 'MojoMain', 25 'MojoMain',
31 'MojoShellActivity', 26 'MojoShellActivity',
32 'MojoShellApplication', 27 'MojoShellApplication',
33 'chromium', 28 'chromium',
34 ] 29 ]
35 30
36 MOJO_SHELL_PACKAGE_NAME = 'org.chromium.mojo.shell' 31 MOJO_SHELL_PACKAGE_NAME = 'org.chromium.mojo.shell'
37 32
38 MAPPING_PREFIX = '--map-origin=' 33 MAPPING_PREFIX = '--map-origin='
39 34
40 DEFAULT_BASE_PORT = 31337 35 DEFAULT_BASE_PORT = 31337
41 36
42 ZERO = datetime.timedelta(0)
43
44 class UTC_TZINFO(datetime.tzinfo):
45 """UTC time zone representation."""
46
47 def utcoffset(self, _):
48 return ZERO
49
50 def tzname(self, _):
51 return "UTC"
52
53 def dst(self, _):
54 return ZERO
55
56 UTC = UTC_TZINFO()
57
58 _logger = logging.getLogger() 37 _logger = logging.getLogger()
59 38
60 39
61 class _SilentTCPServer(SocketServer.TCPServer):
62 """
63 A TCPServer that won't display any error, unless debugging is enabled. This is
64 useful because the client might stop while it is fetching an URL, which causes
65 spurious error messages.
66 """
67 def handle_error(self, request, client_address):
68 """
69 Override the base class method to have conditional logging.
70 """
71 if logging.getLogger().isEnabledFor(logging.DEBUG):
72 SocketServer.TCPServer.handle_error(self, request, client_address)
73
74
75 def _GetHandlerClassForPath(base_path):
76 class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
77 """
78 Handler for SocketServer.TCPServer that will serve the files from
79 |base_path| directory over http.
80 """
81
82 def __init__(self, *args, **kwargs):
83 self.etag = None
84 SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, *args, **kwargs)
85
86 def get_etag(self):
87 if self.etag:
88 return self.etag
89
90 path = self.translate_path(self.path)
91 if not os.path.isfile(path):
92 return None
93
94 sha256 = hashlib.sha256()
95 BLOCKSIZE = 65536
96 with open(path, 'rb') as hashed:
97 buf = hashed.read(BLOCKSIZE)
98 while len(buf) > 0:
99 sha256.update(buf)
100 buf = hashed.read(BLOCKSIZE)
101 self.etag = '"%s"' % sha256.hexdigest()
102 return self.etag
103
104 def send_head(self):
105 # Always close the connection after each request, as the keep alive
106 # support from SimpleHTTPServer doesn't like when the client requests to
107 # close the connection before downloading the full response content.
108 # pylint: disable=W0201
109 self.close_connection = 1
110
111 path = self.translate_path(self.path)
112 if os.path.isfile(path):
113 # Handle If-None-Match
114 etag = self.get_etag()
115 if ('If-None-Match' in self.headers and
116 etag == self.headers['If-None-Match']):
117 self.send_response(304)
118 return None
119
120 # Handle If-Modified-Since
121 if ('If-None-Match' not in self.headers and
122 'If-Modified-Since' in self.headers):
123 last_modified = datetime.datetime.fromtimestamp(
124 math.floor(os.stat(path).st_mtime), tz=UTC)
125 ims = datetime.datetime(
126 *email.utils.parsedate(self.headers['If-Modified-Since'])[:6],
127 tzinfo=UTC)
128 if last_modified <= ims:
129 self.send_response(304)
130 return None
131
132 return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self)
133
134 def end_headers(self):
135 path = self.translate_path(self.path)
136
137 if os.path.isfile(path):
138 etag = self.get_etag()
139 if etag:
140 self.send_header('ETag', etag)
141 self.send_header('Cache-Control', 'must-revalidate')
142
143 return SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self)
144
145 def translate_path(self, path):
146 path_from_current = (
147 SimpleHTTPServer.SimpleHTTPRequestHandler.translate_path(self, path))
148 return os.path.join(base_path, os.path.relpath(path_from_current))
149
150 def log_message(self, *_):
151 """
152 Override the base class method to disable logging.
153 """
154 pass
155
156 RequestHandler.protocol_version = 'HTTP/1.1'
157 return RequestHandler
158
159
160 def _IsMapOrigin(arg): 40 def _IsMapOrigin(arg):
161 """Returns whether arg is a --map-origin argument.""" 41 """Returns whether arg is a --map-origin argument."""
162 return arg.startswith(MAPPING_PREFIX) 42 return arg.startswith(MAPPING_PREFIX)
163 43
164 44
165 def _Split(l, pred): 45 def _Split(l, pred):
166 positive = [] 46 positive = []
167 negative = [] 47 negative = []
168 for v in l: 48 for v in l:
169 if pred(v): 49 if pred(v):
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
264 def _UnmapPort(): 144 def _UnmapPort():
265 subprocess.Popen(unmap_command) 145 subprocess.Popen(unmap_command)
266 atexit.register(_UnmapPort) 146 atexit.register(_UnmapPort)
267 return device_port 147 return device_port
268 148
269 def _StartHttpServerForDirectory(self, path, port=0): 149 def _StartHttpServerForDirectory(self, path, port=0):
270 """Starts an http server serving files from |path|. Returns the local 150 """Starts an http server serving files from |path|. Returns the local
271 url.""" 151 url."""
272 assert path 152 assert path
273 print 'starting http for', path 153 print 'starting http for', path
274 httpd = _SilentTCPServer(('127.0.0.1', 0), _GetHandlerClassForPath(path)) 154 server_address = StartHttpServer(path)
275 atexit.register(httpd.shutdown)
276 155
277 http_thread = threading.Thread(target=httpd.serve_forever) 156 print 'local port=%d' % server_address[1]
278 http_thread.daemon = True 157 return 'http://127.0.0.1:%d/' % self._MapPort(port, server_address[1])
279 http_thread.start()
280
281 print 'local port=%d' % httpd.server_address[1]
282 return 'http://127.0.0.1:%d/' % self._MapPort(port, httpd.server_address[1])
283 158
284 def _StartHttpServerForOriginMapping(self, mapping, port): 159 def _StartHttpServerForOriginMapping(self, mapping, port):
285 """If |mapping| points at a local file starts an http server to serve files 160 """If |mapping| points at a local file starts an http server to serve files
286 from the directory and returns the new mapping. 161 from the directory and returns the new mapping.
287 162
288 This is intended to be called for every --map-origin value.""" 163 This is intended to be called for every --map-origin value."""
289 parts = mapping.split('=') 164 parts = mapping.split('=')
290 if len(parts) != 2: 165 if len(parts) != 2:
291 return mapping 166 return mapping
292 dest = parts[1] 167 dest = parts[1]
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
399 274
400 Returns the process responsible for reading the logs. 275 Returns the process responsible for reading the logs.
401 """ 276 """
402 logcat = subprocess.Popen(self._CreateADBCommand([ 277 logcat = subprocess.Popen(self._CreateADBCommand([
403 'logcat', 278 'logcat',
404 '-s', 279 '-s',
405 ' '.join(LOGCAT_TAGS)]), 280 ' '.join(LOGCAT_TAGS)]),
406 stdout=sys.stdout) 281 stdout=sys.stdout)
407 atexit.register(_ExitIfNeeded, logcat) 282 atexit.register(_ExitIfNeeded, logcat)
408 return logcat 283 return logcat
OLDNEW
« no previous file with comments | « no previous file | mojo/devtools/common/pylib/http_server.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698