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 |