| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can be | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 """A tiny web server. | |
| 7 | |
| 8 This is intended to be used for testing, and only run from within the examples | |
| 9 directory. | |
| 10 """ | |
| 11 | |
| 12 import BaseHTTPServer | |
| 13 import logging | |
| 14 import optparse | |
| 15 import os | |
| 16 import SimpleHTTPServer | |
| 17 import SocketServer | |
| 18 import sys | |
| 19 import urlparse | |
| 20 | |
| 21 | |
| 22 EXAMPLE_PATH=os.path.dirname(os.path.abspath(__file__)) | |
| 23 NACL_SDK_ROOT = os.getenv('NACL_SDK_ROOT', os.path.dirname(EXAMPLE_PATH)) | |
| 24 | |
| 25 | |
| 26 if os.path.exists(NACL_SDK_ROOT): | |
| 27 sys.path.append(os.path.join(NACL_SDK_ROOT, 'tools')) | |
| 28 import decode_dump | |
| 29 import getos | |
| 30 else: | |
| 31 NACL_SDK_ROOT=None | |
| 32 | |
| 33 last_nexe = None | |
| 34 last_nmf = None | |
| 35 | |
| 36 logging.getLogger().setLevel(logging.INFO) | |
| 37 | |
| 38 # Using 'localhost' means that we only accept connections | |
| 39 # via the loop back interface. | |
| 40 SERVER_PORT = 5103 | |
| 41 SERVER_HOST = '' | |
| 42 | |
| 43 # We only run from the examples directory so that not too much is exposed | |
| 44 # via this HTTP server. Everything in the directory is served, so there should | |
| 45 # never be anything potentially sensitive in the serving directory, especially | |
| 46 # if the machine might be a multi-user machine and not all users are trusted. | |
| 47 # We only serve via the loopback interface. | |
| 48 def SanityCheckDirectory(): | |
| 49 httpd_path = os.path.abspath(os.path.dirname(__file__)) | |
| 50 serve_path = os.path.abspath(os.getcwd()) | |
| 51 | |
| 52 # Verify we are serving from the directory this script came from, or bellow | |
| 53 if serve_path[:len(httpd_path)] == httpd_path: | |
| 54 return | |
| 55 logging.error('For security, httpd.py should only be run from within the') | |
| 56 logging.error('example directory tree.') | |
| 57 logging.error('We are currently in %s.' % serve_path) | |
| 58 sys.exit(1) | |
| 59 | |
| 60 | |
| 61 # An HTTP server that will quit when |is_running| is set to False. We also use | |
| 62 # SocketServer.ThreadingMixIn in order to handle requests asynchronously for | |
| 63 # faster responses. | |
| 64 class QuittableHTTPServer(SocketServer.ThreadingMixIn, | |
| 65 BaseHTTPServer.HTTPServer): | |
| 66 def serve_forever(self, timeout=0.5): | |
| 67 self.is_running = True | |
| 68 self.timeout = timeout | |
| 69 while self.is_running: | |
| 70 self.handle_request() | |
| 71 | |
| 72 def shutdown(self): | |
| 73 self.is_running = False | |
| 74 return 1 | |
| 75 | |
| 76 | |
| 77 # "Safely" split a string at |sep| into a [key, value] pair. If |sep| does not | |
| 78 # exist in |str|, then the entire |str| is the key and the value is set to an | |
| 79 # empty string. | |
| 80 def KeyValuePair(str, sep='='): | |
| 81 if sep in str: | |
| 82 return str.split(sep) | |
| 83 else: | |
| 84 return [str, ''] | |
| 85 | |
| 86 | |
| 87 # A small handler that looks for '?quit=1' query in the path and shuts itself | |
| 88 # down if it finds that parameter. | |
| 89 class QuittableHTTPHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): | |
| 90 def send_head(self): | |
| 91 """Common code for GET and HEAD commands. | |
| 92 | |
| 93 This sends the response code and MIME headers. | |
| 94 | |
| 95 Return value is either a file object (which has to be copied | |
| 96 to the outputfile by the caller unless the command was HEAD, | |
| 97 and must be closed by the caller under all circumstances), or | |
| 98 None, in which case the caller has nothing further to do. | |
| 99 | |
| 100 """ | |
| 101 path = self.translate_path(self.path) | |
| 102 f = None | |
| 103 if os.path.isdir(path): | |
| 104 if not self.path.endswith('/'): | |
| 105 # redirect browser - doing basically what apache does | |
| 106 self.send_response(301) | |
| 107 self.send_header("Location", self.path + "/") | |
| 108 self.end_headers() | |
| 109 return None | |
| 110 for index in "index.html", "index.htm": | |
| 111 index = os.path.join(path, index) | |
| 112 if os.path.exists(index): | |
| 113 path = index | |
| 114 break | |
| 115 else: | |
| 116 return self.list_directory(path) | |
| 117 ctype = self.guess_type(path) | |
| 118 try: | |
| 119 # Always read in binary mode. Opening files in text mode may cause | |
| 120 # newline translations, making the actual size of the content | |
| 121 # transmitted *less* than the content-length! | |
| 122 f = open(path, 'rb') | |
| 123 except IOError: | |
| 124 self.send_error(404, "File not found") | |
| 125 return None | |
| 126 self.send_response(200) | |
| 127 self.send_header("Content-type", ctype) | |
| 128 fs = os.fstat(f.fileno()) | |
| 129 self.send_header("Content-Length", str(fs[6])) | |
| 130 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) | |
| 131 self.send_header('Cache-Control','no-cache, must-revalidate') | |
| 132 self.send_header('Expires','-1') | |
| 133 self.end_headers() | |
| 134 return f | |
| 135 | |
| 136 def do_GET(self): | |
| 137 global last_nexe, last_nmf | |
| 138 (_, _, path, query, _) = urlparse.urlsplit(self.path) | |
| 139 url_params = dict([KeyValuePair(key_value) | |
| 140 for key_value in query.split('&')]) | |
| 141 if 'quit' in url_params and '1' in url_params['quit']: | |
| 142 self.send_response(200, 'OK') | |
| 143 self.send_header('Content-type', 'text/html') | |
| 144 self.send_header('Content-length', '0') | |
| 145 self.end_headers() | |
| 146 self.server.shutdown() | |
| 147 return | |
| 148 | |
| 149 if path.endswith('.nexe'): | |
| 150 last_nexe = path | |
| 151 if path.endswith('.nmf'): | |
| 152 last_nmf = path | |
| 153 | |
| 154 SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) | |
| 155 | |
| 156 def do_POST(self): | |
| 157 (_, _,path, query, _) = urlparse.urlsplit(self.path) | |
| 158 if 'Content-Length' in self.headers: | |
| 159 if not NACL_SDK_ROOT: | |
| 160 self.wfile('Could not find NACL_SDK_ROOT to decode trace.') | |
| 161 return | |
| 162 data = self.rfile.read(int(self.headers['Content-Length'])) | |
| 163 nexe = '.' + last_nexe | |
| 164 nmf = '.' + last_nmf | |
| 165 addr = os.path.join(NACL_SDK_ROOT, 'toolchain', | |
| 166 getos.GetPlatform() + '_x86_newlib', | |
| 167 'bin', 'x86_64-nacl-addr2line') | |
| 168 decoder = decode_dump.CoreDecoder(nexe, nmf, addr, None, None) | |
| 169 info = decoder.Decode(data) | |
| 170 trace = decoder.StackTrace(info) | |
| 171 decoder.PrintTrace(trace, sys.stdout) | |
| 172 decoder.PrintTrace(trace, self.wfile) | |
| 173 | |
| 174 | |
| 175 def Run(server_address, | |
| 176 server_class=QuittableHTTPServer, | |
| 177 handler_class=QuittableHTTPHandler): | |
| 178 httpd = server_class(server_address, handler_class) | |
| 179 logging.info("Starting local server on port %d", server_address[1]) | |
| 180 logging.info("To shut down send http://localhost:%d?quit=1", | |
| 181 server_address[1]) | |
| 182 try: | |
| 183 httpd.serve_forever() | |
| 184 except KeyboardInterrupt: | |
| 185 logging.info("Received keyboard interrupt.") | |
| 186 httpd.server_close() | |
| 187 | |
| 188 logging.info("Shutting down local server on port %d", server_address[1]) | |
| 189 | |
| 190 | |
| 191 def main(): | |
| 192 usage_str = "usage: %prog [options] [optional_portnum]" | |
| 193 parser = optparse.OptionParser(usage=usage_str) | |
| 194 parser.add_option( | |
| 195 '--no_dir_check', dest='do_safe_check', | |
| 196 action='store_false', default=True, | |
| 197 help='Do not ensure that httpd.py is being run from a safe directory.') | |
| 198 (options, args) = parser.parse_args(sys.argv) | |
| 199 if options.do_safe_check: | |
| 200 SanityCheckDirectory() | |
| 201 if len(args) > 2: | |
| 202 print 'Too many arguments specified.' | |
| 203 parser.print_help() | |
| 204 elif len(args) == 2: | |
| 205 Run((SERVER_HOST, int(args[1]))) | |
| 206 else: | |
| 207 Run((SERVER_HOST, SERVER_PORT)) | |
| 208 return 0 | |
| 209 | |
| 210 | |
| 211 if __name__ == '__main__': | |
| 212 sys.exit(main()) | |
| OLD | NEW |