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

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

Incomplete - task 9: Data Browser Replacement

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