annotate fileserver/web.py @ 14:c8520774ddc9

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