Changeset 7032 for ceda_http_fileserver


Ignore:
Timestamp:
17/06/10 11:29:22 (9 years ago)
Author:
pjkersha
Message:

Incomplete - task 9: Data Browser Replacement

  • adding support byte ranges in header requests
Location:
ceda_http_fileserver/trunk/ceda_http_fileserver
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • ceda_http_fileserver/trunk/ceda_http_fileserver/ceda/server/wsgi/fileserver/app.py

    r7031 r7032  
    3434log = logging.getLogger(__name__) 
    3535     
     36 
     37class FileResponseError(Exception): 
     38    """Base exception type for exceptions raised from FileResponse class 
     39    instances""" 
     40     
     41class InvalidRangeRequest(FileResponseError): 
     42    """Raise for an invalid byte range requested""" 
     43     
    3644     
    3745class FileResponse(object): 
     
    4351        self.fileSize = None 
    4452        self.readSize = readSize 
    45  
    46     def __call__(self, fileObj, fileName): 
     53        self.start = None 
     54        self.end = None 
     55         
     56    def __call__(self, fileObj, fileName, rangeRequest=None): 
    4757        self.fileSize = os.path.getsize(fileName) 
     58        if rangeRequest is not None: 
     59            start, end = [int(i) for i in requestRange.split(':')] 
     60             
     61            # Verify range bounds 
     62            if start > end: 
     63                raise InvalidRangeRequest('Range start index %r is greater ' 
     64                                          'than the end index %r' %  
     65                                          (start, end)) 
     66            elif start < 0: 
     67                raise InvalidRangeRequest('Range start index %r is less than ' 
     68                                          'zero' % start) 
     69                 
     70            elif end >= self.fileSize: 
     71                raise InvalidRangeRequest('Range end index %r is greater than ' 
     72                                          'the length of the requested ' 
     73                                          'resource %r' % (end, self.fileSize)) 
     74        else: 
     75            start = 0 
     76            self.end = self.fileSize - 1 
     77             
    4878        self.fileObj = fileObj 
    49          
    50         return self   
     79        self.fileObj.seek(start) 
    5180         
    5281    def __iter__(self): 
    5382        output = '\n' 
    5483        while len(output) is not 0: 
    55             output = self.fileObj.read(self.readSize) 
     84            output = self.fileObj.read(self.end+1) 
    5685            yield output 
    5786 
     
    74103                    httplib.responses.get(code, httplib.INTERNAL_SERVER_ERROR)) 
    75104             
    76              
     105_parseRangeRequest = lambda rangeStr: tuple( 
     106                                        [int(i) for i in rangeStr.split(':')]) 
     107 
     108 
    77109class FileServerApp(object): 
    78110    """Application to serve static content""" 
     
    86118     
    87119    statusCode2Msg = staticmethod(_statusCode2Msg) 
     120    parseRangeRequest = staticmethod(lambda rangeStr: tuple( 
     121                                     [int(i) for i in rangeStr.split(':')])) 
    88122     
    89123    # Map HTTP status string to a given file system file access error 
     
    283317                          'point %r: returning 404 Not Found response', 
    284318                          environ['PATH_INFO'], self.mountPoint) 
     319                 
     320                status = FileServerApp.statusCode2Msg(httplib.NOT_FOUND) 
     321                start_response(status, 
     322                               [('Content-length', str(len(status))), 
     323                                ('Content-type', 'text/plain')]) 
     324                return [status] 
    285325              
    286326            relativeURI = splitPath[1] 
     
    323363        else: 
    324364            _allowFile = lambda filename: True 
    325              
     365         
     366        # Check for a HTTP Range request 
     367        requestRange = environ.get('HTTP_RANGE') 
     368         
    326369        if not _allowFile(fileSysSubDir): 
    327370            status = FileServerApp.statusCode2Msg(httplib.NOT_FOUND) 
     
    364407                        '<br>'.join(lines)) 
    365408     
     409            if rangeRequest is not None: 
     410                log.warning('Requested path %r will return a HTML type ' 
     411                            'response listing directory content: ignoring HTTP ' 
     412                            'range request %r', (environ['PATH_INFO'], 
     413                                                 rangeRequest)) 
     414                 
    366415            start_response(FileServerApp.statusCode2Msg(httplib.OK),  
    367416                           [('Cache-Control','no-cache'),  
     
    387436                return [response] 
    388437             
    389             self.__fileResponse(fileObj, filePath) 
     438            try: 
     439                self.__fileResponse(fileObj, filePath,  
     440                                    rangeRequest=rangeRequest) 
     441            except InvalidRangeRequest, e: 
     442                response = str(e) 
     443                log.error(response) 
     444                status = FileServerApp.statusCode2Msg( 
     445                                        httplib.REQUESTED_RANGE_NOT_SATISFIABLE) 
     446                start_response(status,  
     447                               [('Content-Type', 'text/plain'), 
     448                                ('Content-length', str(len(response)))]) 
     449                return [response] 
     450                             
    390451            start_response(FileServerApp.statusCode2Msg(httplib.OK),  
    391452                       [('Cache-Control','no-cache'),  
  • ceda_http_fileserver/trunk/ceda_http_fileserver/ceda/server/wsgi/fileserver/test/test_fileserver.py

    r7031 r7032  
    5858    PDF_REL_URIPATH = '/' + urllib.pathname2url(PDF_REL_FILEPATH)    
    5959      
     60      
    6061class FileServerAppTestCase(FileServerAppTestCaseBase): 
    6162    """Test ceda.server.wsgi.fileserver.app.FileServerApp with Paste Fixture 
     
    155156        self.assert_(self.__class__.PNG_REL_FILEPATH not in response2.body) 
    156157         
    157       
    158158    def test11AccessForbidden(self): 
    159159         
     
    199199        self.assert_(self.__class__.PDF_FILENAME in response.body) 
    200200         
    201                 
     201    def test14CheckResponseTypeForSubDir(self): 
     202        # Check the response type for sub-directory page listing retrieval 
     203        # It must be text/html or application/xhtml+xml for WGet recursive 
     204        # retrieval. 
     205        response = self.app.get(self.__class__.HTDOCS_SUBDIR3_URIPATH,  
     206                                status=httplib.OK) 
     207        print(response.headers) 
     208        self.assert_('text/html' in response.header_dict['content-type']) 
     209         
     210    def test15RetrieveRange(self): 
     211        # Specify a range of bytes for retrieval 
     212        headers = {'Range': '0-599'} 
     213        response = self.app.get(self.__class__.PDF_REL_URIPATH, 
     214                                headers=headers,  
     215                                status=httplib.PARTIAL_CONTENT) 
     216        self.assert_('content-range' in response.header_dict) 
    202217         
    203218class FileServerAppPasterTestCase(FileServerAppTestCaseBase):   
  • ceda_http_fileserver/trunk/ceda_http_fileserver/setup.py

    r7031 r7032  
    1717 
    1818from setuptools import setup, find_packages 
     19import setuptools 
    1920 
    2021setup( 
     
    3738    packages =          find_packages(), 
    3839    package_data =      { 
    39         'ceda.server.wsgi.fileserver.test': [ 
    40             '*.ini',  
    41             'htdocs/my*.*',  
    42             'htdocs/sub.dir1/*.*',  
    43             'htdocs/sub.dir1/SubDir 3/*.*', 
    44             'htdocs/.sub-dir2/*.*', 
    45         ] 
    46     }, 
     40        'ceda.server.wsgi.fileserver.test': ['*.ini'],  
     41        'ceda.server.wsgi.fileserver.test.htdocs': ['/my*.*', 'sub.dir1/*.PNG'], 
     42        }, 
     43#    package_data =      { 
     44#        'ceda.server.wsgi.fileserver.test': [ 
     45#            '*.ini',  
     46#            'htdocs/*.*',  
     47#            'htdocs/sub.dir1/*.*',  
     48#            'htdocs/sub.dir1/SubDir 3/*.*', 
     49#            'htdocs/.sub-dir2/*.*', 
     50#        ] 
     51#    }, 
    4752    classifiers = [ 
    4853        'Development Status :: 3 - Alpha', 
Note: See TracChangeset for help on using the changeset viewer.