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
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
12
8127dde8da22 fix slashing
Jeff Hammel <jhammel@mozilla.com>
parents: 2
diff changeset
52 def add_slash(self, request):
8127dde8da22 fix slashing
Jeff Hammel <jhammel@mozilla.com>
parents: 2
diff changeset
53 import pdb; pdb.set_trace()
8127dde8da22 fix slashing
Jeff Hammel <jhammel@mozilla.com>
parents: 2
diff changeset
54 location = ''
8127dde8da22 fix slashing
Jeff Hammel <jhammel@mozilla.com>
parents: 2
diff changeset
55 response = exc.HTTPMovedPermanantly()
8127dde8da22 fix slashing
Jeff Hammel <jhammel@mozilla.com>
parents: 2
diff changeset
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
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
70 def __call__(self, environ, start_response):
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
71 request = Request(environ)
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
72 # TODO method_not_allowed: Allow: GET, HEAD
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
73 path_info = request.path_info
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
74 if not path_info:
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
75 pass # self.add slash
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
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
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
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
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
81 # Out of bounds
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
82 return exc.HTTPNotFound()(environ, start_response)
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
83 if not os.path.exists(full):
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
84 return exc.HTTPNotFound()(environ, start_response)
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
85
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
86 if os.path.isdir(full):
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
87 # serve directory index
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
88 if not path_info.endswith('/'):
12
8127dde8da22 fix slashing
Jeff Hammel <jhammel@mozilla.com>
parents: 2
diff changeset
89 response = exc.HTTPMovedPermanently(add_slash=True)
8127dde8da22 fix slashing
Jeff Hammel <jhammel@mozilla.com>
parents: 2
diff changeset
90 return response(environ, start_response)
0
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
91 return self.add_slash(environ, start_response)
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
92 index = self.index(full)
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
93 response_headers = [('Content-Type', 'text/html'),
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
94 ('Content-Length', str(len(index)))]
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
95 response = Response(index, content_type='text/html')
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
96 return response(environ, start_response)
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
97
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
98 # serve file
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
99 if path_info.endswith('/'):
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
100 # we create the `full` filename above by stripping off
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
101 # '/' from both sides; so we correct here
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
102 return exc.HTTPNotFound()(environ, start_response)
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
103 response = file_response(full)
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
104 return response(environ, start_response)
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
105
1
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
106 def main(args=sys.argv[1:]):
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
107
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
108 # parse command line arguments
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
109 usage = '%prog [options] directory'
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
110 class PlainDescriptionFormatter(optparse.IndentedHelpFormatter):
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
111 """description formatter"""
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
112 def format_description(self, description):
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
113 if description:
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
114 return description + '\n'
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
115 else:
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
116 return ''
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
117 parser = optparse.OptionParser(usage=usage, description=__doc__, formatter=PlainDescriptionFormatter())
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
118 parser.add_option('-p', '--port', dest='port',
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
119 type='int', default=9999,
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
120 help='port [DEFAULT: %default]')
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
121 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
122 help='host [DEFAULT: %default]')
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
123 options, args = parser.parse_args(args)
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
124
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
125 # get the directory
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
126 if not len(args) == 1:
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
127 parser.print_help()
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
128 sys.exit(1)
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
129 directory = args[0]
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
130 if not os.path.exists(directory):
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
131 parser.error("'%s' not found" % directory)
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
132 if not os.path.isdir(directory):
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
133 parser.error("'%s' not a directory" % directory)
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
134
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
135 # serve
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
136 app = DirectoryServer(directory)
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
137 try:
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
138 print 'http://%s:%s/' % (options.host, options.port)
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
139 make_server(options.host, options.port, app).serve_forever()
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
140 except KeyboardInterrupt, ki:
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
141 print "Cio, baby!"
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
142 except BaseException, e:
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
143 sys.exit("Problem initializing server: %s" % e)
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
144
0
8d31e36f084e initial stubbing
Jeff Hammel <jhammel@mozilla.com>
parents:
diff changeset
145 if __name__ == '__main__':
1
89d4f742ed1a include cli front end
Jeff Hammel <jhammel@mozilla.com>
parents: 0
diff changeset
146 main()