source: ceda_http_fileserver/trunk/ceda_http_fileserver/ceda/server/wsgi/fileserver/__init__.py @ 6993

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

Incomplete - task 9: Data Browser Replacement

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