Mercurial > hg > FileServer
annotate fileserver/web.py @ 12:8127dde8da22
fix slashing
author | Jeff Hammel <jhammel@mozilla.com> |
---|---|
date | Wed, 29 Feb 2012 15:41:01 -0800 |
parents | 8fb047af207a |
children | e3993fa05b89 |
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 | |
12 | 52 def add_slash(self, request): |
53 import pdb; pdb.set_trace() | |
54 location = '' | |
55 response = exc.HTTPMovedPermanantly() | |
56 | |
2
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
57 def index(self, directory): |
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
58 """ |
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
59 generate a directory listing for a given directory |
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
60 """ |
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
61 parts = ['<html><head><title>Simple Index</title></head><body>'] |
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
62 listings = os.listdir(directory) |
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
63 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
|
64 for entry in listings] |
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
65 for link, entry in listings: |
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
66 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
|
67 parts.append('</body></html>') |
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
68 return '\n'.join(parts) |
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
69 |
0 | 70 def __call__(self, environ, start_response): |
71 request = Request(environ) | |
72 # TODO method_not_allowed: Allow: GET, HEAD | |
73 path_info = request.path_info | |
74 if not path_info: | |
75 pass # self.add slash | |
76 full = self.normpath(os.path.join(self.directory, path_info.strip('/'))) | |
2
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
77 |
0 | 78 if not full.startswith(self.directory): |
2
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
79 print 'OUT OF BOUNDS!' |
8fb047af207a
this now actually serves things
Jeff Hammel <jhammel@mozilla.com>
parents:
1
diff
changeset
|
80 import pdb; pdb.set_trace() |
0 | 81 # Out of bounds |
82 return exc.HTTPNotFound()(environ, start_response) | |
83 if not os.path.exists(full): | |
84 return exc.HTTPNotFound()(environ, start_response) | |
85 | |
86 if os.path.isdir(full): | |
87 # serve directory index | |
88 if not path_info.endswith('/'): | |
12 | 89 response = exc.HTTPMovedPermanently(add_slash=True) |
90 return response(environ, start_response) | |
0 | 91 return self.add_slash(environ, start_response) |
92 index = self.index(full) | |
93 response_headers = [('Content-Type', 'text/html'), | |
94 ('Content-Length', str(len(index)))] | |
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() |