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

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

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

Powered by Google App Engine
This is Rietveld 408576698