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

Side by Side Diff: mojo/devtools/common/pylib/http_server.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, 7 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 | « mojo/devtools/common/pylib/android.py ('k') | 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
(Empty)
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
3 # found in the LICENSE file.
4
5 import atexit
6 import datetime
7 import email.utils
8 import hashlib
9 import logging
10 import math
11 import os.path
12 import threading
13
14 import SimpleHTTPServer
15 import SocketServer
16
17
18 ZERO = datetime.timedelta(0)
19
20
21 class UTC_TZINFO(datetime.tzinfo):
22 """UTC time zone representation."""
23
24 def utcoffset(self, _):
25 return ZERO
26
27 def tzname(self, _):
28 return "UTC"
29
30 def dst(self, _):
31 return ZERO
32
33 UTC = UTC_TZINFO()
34
35
36 class _SilentTCPServer(SocketServer.TCPServer):
37 """
38 A TCPServer that won't display any error, unless debugging is enabled. This is
39 useful because the client might stop while it is fetching an URL, which causes
40 spurious error messages.
41 """
42 def handle_error(self, request, client_address):
43 """
44 Override the base class method to have conditional logging.
45 """
46 if logging.getLogger().isEnabledFor(logging.DEBUG):
47 SocketServer.TCPServer.handle_error(self, request, client_address)
48
49
50 def _GetHandlerClassForPath(base_path):
51 class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
52 """
53 Handler for SocketServer.TCPServer that will serve the files from
54 |base_path| directory over http.
55 """
56
57 def __init__(self, *args, **kwargs):
58 self.etag = None
59 SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, *args, **kwargs)
60
61 def get_etag(self):
62 if self.etag:
63 return self.etag
64
65 path = self.translate_path(self.path)
66 if not os.path.isfile(path):
67 return None
68
69 sha256 = hashlib.sha256()
70 BLOCKSIZE = 65536
71 with open(path, 'rb') as hashed:
72 buf = hashed.read(BLOCKSIZE)
73 while len(buf) > 0:
74 sha256.update(buf)
75 buf = hashed.read(BLOCKSIZE)
76 self.etag = '"%s"' % sha256.hexdigest()
77 return self.etag
78
79 def send_head(self):
80 # Always close the connection after each request, as the keep alive
81 # support from SimpleHTTPServer doesn't like when the client requests to
82 # close the connection before downloading the full response content.
83 # pylint: disable=W0201
84 self.close_connection = 1
85
86 path = self.translate_path(self.path)
87 if os.path.isfile(path):
88 # Handle If-None-Match
89 etag = self.get_etag()
90 if ('If-None-Match' in self.headers and
91 etag == self.headers['If-None-Match']):
92 self.send_response(304)
93 return None
94
95 # Handle If-Modified-Since
96 if ('If-None-Match' not in self.headers and
97 'If-Modified-Since' in self.headers):
98 last_modified = datetime.datetime.fromtimestamp(
99 math.floor(os.stat(path).st_mtime), tz=UTC)
100 ims = datetime.datetime(
101 *email.utils.parsedate(self.headers['If-Modified-Since'])[:6],
102 tzinfo=UTC)
103 if last_modified <= ims:
104 self.send_response(304)
105 return None
106
107 return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self)
108
109 def end_headers(self):
110 path = self.translate_path(self.path)
111
112 if os.path.isfile(path):
113 etag = self.get_etag()
114 if etag:
115 self.send_header('ETag', etag)
116 self.send_header('Cache-Control', 'must-revalidate')
117
118 return SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self)
119
120 def translate_path(self, path):
121 path_from_current = (
122 SimpleHTTPServer.SimpleHTTPRequestHandler.translate_path(self, path))
123 return os.path.join(base_path, os.path.relpath(path_from_current))
124
125 def log_message(self, *_):
126 """
127 Override the base class method to disable logging.
128 """
129 pass
130
131 RequestHandler.protocol_version = 'HTTP/1.1'
132 return RequestHandler
133
134
135 def StartHttpServer(path):
136 """Starts an http server serving files from |path| on random
137 (system-allocated) port. Returns the server address."""
138 assert path
139 httpd = _SilentTCPServer(('127.0.0.1', 0), _GetHandlerClassForPath(path))
140 atexit.register(httpd.shutdown)
141
142 http_thread = threading.Thread(target=httpd.serve_forever)
143 http_thread.daemon = True
144 http_thread.start()
145 return httpd.server_address
OLDNEW
« no previous file with comments | « mojo/devtools/common/pylib/android.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698