source: ceda_http_fileserver/trunk/ceda_http_fileserver/wsgi_fileserver/__init__.py @ 6996

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/ceda_http_fileserver/trunk/ceda_http_fileserver/wsgi_fileserver/__init__.py@6996
Revision 6996, 6.6 KB checked in by pjkersha, 10 years ago (diff)

Incomplete - task 9: Data Browser Replacement

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