Mercurial > hg > FileServer
annotate fileserver/web.py @ 18:76c939271534
introduce a test that i dont know what it means
| author | Jeff Hammel <jhammel@mozilla.com> |
|---|---|
| date | Wed, 29 Feb 2012 16:05:08 -0800 |
| parents | 27bd18f0a359 |
| children | 1eb5e82605a5 |
| rev | line source |
|---|---|
| 0 | 1 #!/usr/bin/env python |
| 2 | |
| 3 """ | |
| 4 WSGI app for FileServer | |
| 5 | |
| 6 Reference: | |
| 7 - http://docs.webob.org/en/latest/file-example.html | |
| 8 """ | |
| 9 | |
| 10 import mimetypes | |
| 1 | 11 import optparse |
| 0 | 12 import os |
| 1 | 13 import sys |
| 0 | 14 from webob import Request, Response, exc |
| 1 | 15 from wsgiref.simple_server import make_server |
| 16 | |
| 17 __all__ = ['get_mimetype', 'file_response', 'FileApp', 'DirectoryServer'] | |
| 0 | 18 |
| 19 def get_mimetype(filename): | |
| 20 type, encoding = mimetypes.guess_type(filename) | |
| 21 # We'll ignore encoding, even though we shouldn't really | |
| 22 return type or 'application/octet-stream' | |
| 23 | |
| 24 def file_response(filename): | |
| 25 res = Response(content_type=get_mimetype(filename)) | |
| 26 res.body = open(filename, 'rb').read() | |
| 27 return res | |
| 28 | |
| 29 class FileApp(object): | |
| 30 """ | |
| 31 serve static files | |
| 32 """ | |
| 33 | |
| 34 def __init__(self, filename): | |
| 35 self.filename = filename | |
| 36 | |
| 37 def __call__(self, environ, start_response): | |
| 38 res = file_response(self.filename) | |
| 39 return res(environ, start_response) | |
| 40 | |
| 41 class DirectoryServer(object): | |
| 42 | |
| 43 def __init__(self, directory): | |
| 44 assert os.path.exists(directory), "'%s' does not exist" % directory | |
| 45 assert os.path.isdir(directory), "'%s' is not a directory" % directory | |
|
2
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
46 self.directory = self.normpath(directory) |
| 0 | 47 |
| 48 @staticmethod | |
| 49 def normpath(path): | |
| 50 return os.path.normcase(os.path.abspath(path)) | |
| 51 | |
| 17 | 52 def check_path(self, path): |
| 53 """ | |
| 54 if under the root directory, returns the full path | |
| 55 otherwise, returns None | |
| 56 """ | |
| 57 path = self.normpath(path) | |
| 58 if path == self.directory or path.startswith(self.directory + os.path.sep): | |
| 59 return path | |
| 60 | |
|
2
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
61 def index(self, directory): |
|
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
62 """ |
|
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
63 generate a directory listing for a given directory |
|
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
64 """ |
|
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
65 parts = ['<html><head><title>Simple Index</title></head><body>'] |
|
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
66 listings = os.listdir(directory) |
|
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
67 listings = [(os.path.isdir(os.path.join(directory, entry)) and entry + '/' or entry, entry) |
|
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
68 for entry in listings] |
|
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
69 for link, entry in listings: |
|
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
70 parts.append('<a href="%s">%s</a><br/>' % (link, entry)) |
|
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
71 parts.append('</body></html>') |
|
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
72 return '\n'.join(parts) |
|
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
73 |
| 0 | 74 def __call__(self, environ, start_response): |
| 75 request = Request(environ) | |
| 76 # TODO method_not_allowed: Allow: GET, HEAD | |
| 77 path_info = request.path_info | |
| 78 if not path_info: | |
| 13 | 79 response = exc.HTTPMovedPermanently(add_slash=True) |
| 80 return response(environ, start_response) | |
| 17 | 81 full = self.check_path(os.path.join(self.directory, path_info.strip('/'))) |
|
2
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
82 |
| 17 | 83 if full is None: |
| 0 | 84 # Out of bounds |
| 85 return exc.HTTPNotFound()(environ, start_response) | |
| 86 if not os.path.exists(full): | |
| 87 return exc.HTTPNotFound()(environ, start_response) | |
| 88 | |
| 89 if os.path.isdir(full): | |
| 90 # serve directory index | |
| 91 if not path_info.endswith('/'): | |
| 12 | 92 response = exc.HTTPMovedPermanently(add_slash=True) |
| 93 return response(environ, start_response) | |
| 0 | 94 index = self.index(full) |
| 95 response = Response(index, content_type='text/html') | |
| 96 return response(environ, start_response) | |
| 97 | |
| 98 # serve file | |
| 99 if path_info.endswith('/'): | |
| 100 # we create the `full` filename above by stripping off | |
| 101 # '/' from both sides; so we correct here | |
| 102 return exc.HTTPNotFound()(environ, start_response) | |
| 103 response = file_response(full) | |
| 104 return response(environ, start_response) | |
| 105 | |
| 1 | 106 def main(args=sys.argv[1:]): |
| 107 | |
| 108 # parse command line arguments | |
| 109 usage = '%prog [options] directory' | |
| 110 class PlainDescriptionFormatter(optparse.IndentedHelpFormatter): | |
| 111 """description formatter""" | |
| 112 def format_description(self, description): | |
| 113 if description: | |
| 114 return description + '\n' | |
| 115 else: | |
| 116 return '' | |
| 117 parser = optparse.OptionParser(usage=usage, description=__doc__, formatter=PlainDescriptionFormatter()) | |
| 118 parser.add_option('-p', '--port', dest='port', | |
| 119 type='int', default=9999, | |
| 120 help='port [DEFAULT: %default]') | |
| 121 parser.add_option('-H', '--host', dest='host', default='0.0.0.0', | |
| 122 help='host [DEFAULT: %default]') | |
| 123 options, args = parser.parse_args(args) | |
| 124 | |
| 125 # get the directory | |
| 126 if not len(args) == 1: | |
| 127 parser.print_help() | |
| 128 sys.exit(1) | |
| 129 directory = args[0] | |
| 130 if not os.path.exists(directory): | |
| 131 parser.error("'%s' not found" % directory) | |
| 132 if not os.path.isdir(directory): | |
| 133 parser.error("'%s' not a directory" % directory) | |
| 134 | |
| 135 # serve | |
| 136 app = DirectoryServer(directory) | |
| 137 try: | |
| 138 print 'http://%s:%s/' % (options.host, options.port) | |
| 139 make_server(options.host, options.port, app).serve_forever() | |
| 140 except KeyboardInterrupt, ki: | |
| 141 print "Cio, baby!" | |
| 142 except BaseException, e: | |
| 143 sys.exit("Problem initializing server: %s" % e) | |
| 144 | |
| 0 | 145 if __name__ == '__main__': |
| 1 | 146 main() |
