OLD | NEW |
| (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 """Simple Markdown browser for a Git checkout.""" | |
6 from __future__ import print_function | |
7 | |
8 import SimpleHTTPServer | |
9 import SocketServer | |
10 import argparse | |
11 import codecs | |
12 import os | |
13 import socket | |
14 import sys | |
15 | |
16 | |
17 THIS_DIR = os.path.abspath(os.path.dirname(__file__)) | |
18 SRC_DIR = os.path.dirname(os.path.dirname(THIS_DIR)) | |
19 sys.path.append(os.path.join(SRC_DIR, 'third_party', 'Python-Markdown')) | |
20 import markdown | |
21 | |
22 | |
23 def main(argv): | |
24 parser = argparse.ArgumentParser(prog='md_browser') | |
25 parser.add_argument('-p', '--port', type=int, default=8080, | |
26 help='port to run on (default = %(default)s)') | |
27 args = parser.parse_args(argv) | |
28 | |
29 try: | |
30 s = Server(args.port, SRC_DIR) | |
31 print("Listening on http://localhost:%s/" % args.port) | |
32 s.serve_forever() | |
33 s.shutdown() | |
34 return 0 | |
35 except KeyboardInterrupt: | |
36 return 130 | |
37 | |
38 | |
39 class Server(SocketServer.TCPServer): | |
40 def __init__(self, port, top_level): | |
41 SocketServer.TCPServer.__init__(self, ('0.0.0.0', port), Handler) | |
42 self.port = port | |
43 self.top_level = top_level | |
44 | |
45 def server_bind(self): | |
46 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
47 self.socket.bind(self.server_address) | |
48 | |
49 | |
50 class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler): | |
51 def do_GET(self): | |
52 full_path = os.path.abspath(os.path.join(self.server.top_level, | |
53 self.path[1:])) | |
54 if not full_path.startswith(SRC_DIR): | |
55 self._DoUnknown() | |
56 elif self.path == '/doc.css': | |
57 self._WriteTemplate('doc.css') | |
58 elif not os.path.exists(full_path): | |
59 self._DoNotFound() | |
60 elif self.path.lower().endswith('.md'): | |
61 self._DoMD() | |
62 else: | |
63 self._DoUnknown() | |
64 | |
65 def _DoMD(self): | |
66 extensions = [ | |
67 'markdown.extensions.fenced_code', | |
68 'markdown.extensions.tables', | |
69 'markdown.extensions.toc', | |
70 ] | |
71 | |
72 contents = self._Read(self.path[1:]) | |
73 md_fragment = markdown.markdown(contents, | |
74 extensions=extensions, | |
75 output_format='html4').encode('utf-8') | |
76 try: | |
77 self._WriteTemplate('header.html') | |
78 self.wfile.write(md_fragment) | |
79 self._WriteTemplate('footer.html') | |
80 except: | |
81 raise | |
82 | |
83 def _DoNotFound(self): | |
84 self.wfile.write('<html><body>%s not found</body></html>' % self.path) | |
85 | |
86 def _DoUnknown(self): | |
87 self.wfile.write('<html><body>I do not know how to serve %s.</body>' | |
88 '</html>' % self.path) | |
89 | |
90 def _Read(self, relpath): | |
91 assert not relpath.startswith(os.sep) | |
92 path = os.path.join(self.server.top_level, relpath) | |
93 with codecs.open(path, encoding='utf-8') as fp: | |
94 return fp.read() | |
95 | |
96 def _WriteTemplate(self, template): | |
97 contents = self._Read(os.path.join('tools', 'md_browser', template)) | |
98 self.wfile.write(contents.encode('utf-8')) | |
99 | |
100 | |
101 if __name__ == '__main__': | |
102 sys.exit(main(sys.argv[1:])) | |
OLD | NEW |