source: ceda_http_fileserver/trunk/ceda_http_fileserver/ceda/server/wsgi/fileserver/app.py @ 6996

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/ceda_http_fileserver/trunk/ceda_http_fileserver/ceda/server/wsgi/fileserver/app.py@6996
Revision 6996, 6.9 KB checked in by pjkersha, 10 years ago (diff)

Incomplete - task 9: Data Browser Replacement

  • Property svn:keywords set to Id
Line 
1"""CEDA (Centre for Environmental Data Archival) File Server WSGI Application
2module
3"""
4__author__ = "P J Kershaw"
5__date__ = "11/06/10"
6__copyright__ = "(C) 2010 Science and Technology Facilities Council"
7__license__ = """http://www.apache.org/licenses/LICENSE-2.0"""
8__contact__ = "Philip.Kershaw@stfc.ac.uk"
9__revision__ = '$Id$'
10#   Copyright (c) 2006-2007 Open Source Applications Foundation
11#
12#   Licensed under the Apache License, Version 2.0 (the "License");
13#   you may not use this file except in compliance with the License.
14#   You may obtain a copy of the License at
15#
16#       http://www.apache.org/licenses/LICENSE-2.0
17#
18#   Unless required by applicable law or agreed to in writing, software
19#   distributed under the License is distributed on an "AS IS" BASIS,
20#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21#   See the License for the specific language governing permissions and
22#   limitations under the License.
23
24from urlparse import urlparse
25import os, sys
26import logging
27
28logger = logging.getLogger(__name__)
29
30# Content type sources taken from http://en.wikipedia.org/wiki/MIME_type
31content_type_table = {
32    'js': 'application/x-javascript', 
33    'html': 'text/html; charset=utf-8',
34    'fallback':'text/plain; charset=utf-8', 
35    'ogg': 'application/ogg', 
36    'xhtml':'text/html; charset=utf-8', 
37    'rm':'audio/vnd.rn-realaudio', 
38    'swf':'application/x-shockwave-flash', 
39    'mp3': 'audio/mpeg', 
40    'wma':'audio/x-ms-wma', 
41    'ra':'audio/vnd.rn-realaudio', 
42    'wav':'audio/x-wav', 
43    'gif':'image/gif', 
44    'jpeg':'image/jpeg',
45    'jpg':'image/jpeg', 
46    'png':'image/png', 
47    'tiff':'image/tiff', 
48    'css':'text/css; charset=utf-8',
49    'mpeg':'video/mpeg', 
50    'mp4':'video/mp4', 
51    'qt':'video/quicktime', 
52    'mov':'video/quicktime',
53    'wmv':'video/x-ms-wmv', 
54    'atom':'application/atom+xml; charset=utf-8',
55    'xslt':'application/xslt+xml', 
56    'svg':'image/svg+xml', 'mathml':'application/mathml+xml', 
57    'rss':'application/rss+xml; charset=utf-8',
58    'ics':'text/calendar; charset=utf-8 '
59}
60
61
62def reconstruct_url(environ):
63    # From WSGI spec, PEP 333
64    from urllib import quote
65    url = environ['wsgi.url_scheme']+'://'
66    if environ.get('HTTP_HOST'): url += environ['HTTP_HOST']
67    else:
68        url += environ['SERVER_NAME']
69        if environ['wsgi.url_scheme'] == 'https':
70            if environ['SERVER_PORT'] != '443':
71               url += ':' + environ['SERVER_PORT']
72        else:
73            if environ['SERVER_PORT'] != '80':
74               url += ':' + environ['SERVER_PORT']
75    url += quote(environ.get('SCRIPT_NAME',''))
76    url += quote(environ.get('PATH_INFO','')).replace(url.replace(':', '%3A'), '')
77    if environ.get('QUERY_STRING'):
78        url += '?' + environ['QUERY_STRING']
79    environ['reconstructed_url'] = url
80    return url
81   
82   
83class FileResponse(object):
84    readsize = 1024
85   
86    def __init__(self, f, filename):
87        self.size = os.path.getsize(filename)
88        self.f = f
89       
90    def __iter__(self):
91        output = '\n'
92        while len(output) is not 0:
93            output = self.f.read(self.readsize)
94            yield output
95
96
97class FileServerApp(object):
98    """Application to serve static content"""
99   
100    def __init__(self, root_path, mount_point=None):
101        self.path = os.path.abspath(os.path.expanduser(root_path))
102        self.mount_point = mount_point
103       
104    def handler(self, environ, start_response):
105        """Application to serve out windmill provided"""
106        url = urlparse(reconstruct_url(environ))
107       
108        if self.mount_point is not None:
109            #split_url = url.path.split(self.mount_point, 1)
110            split_url = url[2].split(self.mount_point, 1)
111            serve_file = split_url[1]
112        else:
113            #serve_file = url.path
114            serve_file = url[2]
115       
116        serve_file = serve_file.replace('%20', ' ')
117       
118        def do_get():
119            if serve_file.endswith('/') or os.path.isdir(os.path.join(self.path, serve_file)):
120                if os.path.isdir(os.path.join(self.path, serve_file)):
121                    start_response('200 OK', [('Cache-Control','no-cache'), ('Pragma','no-cache'),
122                                              ('Content-Type', 'text/html; charset=utf-8')])
123                    return [ '<html>' + 
124                              '<br>'.join( ['<a href="%s/%s">%s</a>' % (serve_file.replace(filename, ''), filename, filename) 
125                                          for filename in os.listdir(os.path.join(self.path, serve_file))])
126                             + '</html>'   ]
127                else:
128                    logger.error('failed to list directory %s/%s' % (self.path, serve_file))
129                    start_response('404 Not found', [('Content-Type', 'text/plain')])
130                    return ['404 Not Found']
131           
132            try:
133                if os.name == 'nt' or sys.platform == 'cygwin':
134                    f = open(os.path.join(self.path, serve_file), 'rb')
135                else:
136                    f = open(os.path.join(self.path, serve_file), 'r')
137                logger.debug('opened file %s' % serve_file)
138            except IOError:
139                logger.error('failed to open file %s/%s' % (self.path, serve_file))
140                start_response('404 Not found', [('Content-Type', 'text/plain')])
141                return ['404 Not Found']
142           
143            response = FileResponse(f, os.path.join(self.path, serve_file))
144            start_response('200 OK', [('Cache-Control','no-cache'), ('Pragma','no-cache'), 
145                                      ('Content-Length', str(response.size),),
146                                      ('Content-Type', self.guess_content_type(environ['PATH_INFO']))])
147            return response
148           
149        def do_put():
150            #Write file
151            try:
152                f = open(os.path.join(self.path, serve_file), 'w')
153                logger.debug('opened file for writing %s' % serve_file)
154            except:
155                logger.error('failed to open file for writiing %s/%s' % (self.path, serve_file))
156                start_response('403 Forbidden', [('Content-Type', 'text/plain')])
157                return ['403 Forbidden']
158           
159            f.write(environ['wsgi.input'].read())
160           
161        def do_mkcollection():
162            pass
163           
164        http_method_map = {'GET':do_get, 'PUT':do_put, 'MKCOLLECTION':do_mkcollection}
165        return http_method_map[environ['REQUEST_METHOD']]()
166           
167
168    def guess_content_type(self, path_info):
169        """Make a best guess at the content type"""
170        extention_split = path_info.split('.')
171
172        if content_type_table.has_key(extention_split[-1]):
173            return content_type_table[extention_split[-1]]
174        else:
175            return content_type_table['fallback']
176           
177    def __call__(self, environ, start_response):
178        return self.handler(environ, start_response)
Note: See TracBrowser for help on using the repository browser.