def http_response_with_file(request, response, data_file, content_type, file_size, file_mtime): # HTTP If-Modified-Since header handling. header=request.get_header('If-Modified-Since', None) if header is not None: header=string.split(header, ';')[0] # Some proxies seem to send invalid date strings for this # header. If the date string is not valid, we ignore it # rather than raise an error to be generally consistent # with common servers such as Apache (which can usually # understand the screwy date string as a lucky side effect # of the way they parse it). # This happens to be what RFC2616 tells us to do in the face of an # invalid date. try: mod_since=long(DateTime(header).timeTime()) except: mod_since=None if mod_since is not None: last_mod = long(file_mtime) if last_mod > 0 and last_mod <= mod_since: # Set header values since apache caching will return Content-Length # of 0 in response if size is not set here response.setHeader('Last-Modified', rfc1123_date(file_mtime)) response.setHeader('Content-Type', content_type) response.setHeader('Content-Length', file_size) response.setStatus(304) return response.setHeader('Last-Modified', rfc1123_date(file_mtime)) response.setHeader('Content-Type', content_type) response.setHeader('Content-Length', file_size) for chunk in iter_file_data(data_file): response.write(chunk)
def test_handle_if_modified_since_requests(self): self.layer['request'].environ.update({ 'HTTP_IF_MODIFIED_SINCE': rfc1123_date(self.context._p_mtime+1) }) self.index_html() response = self.layer['request'].RESPONSE self.assertEqual(304, response.getStatus())
def render_attachment_preview(self, attachment): sm = getSecurityManager() if not sm.checkPermission(permissions.View, self.context): raise Unauthorized r = self.request.response settings = Settings(attachment) if self.preview_type not in ("large", "normal", "small"): self.preview_type = "small" if self.page is None: self.page = 1 filepath = u"%s/dump_%s.%s" % (self.preview_type, self.page, settings.pdf_image_format) blob = settings.blob_files[filepath] blobfi = openBlob(blob) length = os.fstat(blobfi.fileno()).st_size blobfi.close() ext = os.path.splitext(os.path.normcase(filepath))[1][1:] if ext == "txt": ct = "text/plain" else: ct = "image/%s" % ext r.setHeader("Content-Type", ct) r.setHeader("Last-Modified", rfc1123_date(self.context._p_mtime)) r.setHeader("Accept-Ranges", "bytes") r.setHeader("Content-Length", length) request_range = handleRequestRange(self.context, length, self.request, self.request.response) return BlobStreamIterator(blob, **request_range)
def HEAD(self, REQUEST, RESPONSE): """Retrieve resource information without a response body.""" self.dav__init(REQUEST, RESPONSE) content_type=None if hasattr(self, 'content_type'): content_type=absattr(self.content_type) if content_type is None: url=urlfix(REQUEST['URL'], 'HEAD') name=unquote(filter(None, url.split( '/')[-1])) content_type, encoding=mimetypes.guess_type(name) if content_type is None: if hasattr(self, 'default_content_type'): content_type=absattr(self.default_content_type) if content_type is None: content_type = 'application/octet-stream' RESPONSE.setHeader('Content-Type', content_type.lower()) if hasattr(aq_base(self), 'get_size'): RESPONSE.setHeader('Content-Length', absattr(self.get_size)) if hasattr(self, '_p_mtime'): mtime=rfc1123_date(self._p_mtime) RESPONSE.setHeader('Last-Modified', mtime) if hasattr(aq_base(self), 'http__etag'): etag = self.http__etag(readonly=1) if etag: RESPONSE.setHeader('Etag', etag) RESPONSE.setStatus(200) return RESPONSE
def test_index_html( self ): path, ref = self._extractFile() import os from webdav.common import rfc1123_date mod_time = os.stat( path )[ 8 ] file = self._makeOne( 'test_file', 'test_file.swf' ) file = file.__of__( self.root ) data = file.index_html( self.REQUEST, self.RESPONSE ) self.assertEqual( len( data ), len( ref ) ) self.assertEqual( data, ref ) # ICK! 'HTTPResponse.getHeader' doesn't case-flatten the key! # for Zope versions before 2.6.1: Content-Length has to be stringified self.assertEqual( str( self.RESPONSE.getHeader( 'Content-Length'.lower() ) ) , str(len(ref)) ) self.assertEqual( self.RESPONSE.getHeader( 'Content-Type'.lower() ) , 'application/octet-stream' ) self.assertEqual( self.RESPONSE.getHeader( 'Last-Modified'.lower() ) , rfc1123_date( mod_time ) )
def index_html(self, REQUEST=None, RESPONSE=None, charset='utf-8', disposition='inline'): """ make it directly viewable when entering the objects URL """ if REQUEST is None: REQUEST = self.REQUEST if RESPONSE is None: RESPONSE = REQUEST.RESPONSE RESPONSE.setHeader('Last-Modified', rfc1123_date(self._p_mtime)) RESPONSE.setHeader('Content-Type', self.getContentType()) RESPONSE.setHeader('Accept-Ranges', 'bytes') if handleIfModifiedSince(self, REQUEST, RESPONSE): return '' length = self.get_size() RESPONSE.setHeader('Content-Length', length) filename = self.getFilename() if filename is not None: if not isinstance(filename, unicode): filename = unicode(filename, charset) filename = IUserPreferredFileNameNormalizer(REQUEST).normalize( filename) header_value = contentDispositionHeader( disposition=disposition, filename=filename) RESPONSE.setHeader("Content-disposition", header_value) request_range = handleRequestRange(self, length, REQUEST, RESPONSE) return self.getIterator(**request_range)
def get_logo(self, disposition='inline', headers=True): portal = getToolByName(self.context, 'portal_url').getPortalObject() customstyles_util = CustomStylesUtility(portal) customstyles = customstyles_util.annotations.get( 'customstyles', OOBTree(DEFAULT_STYLES)) REQUEST = self.request RESPONSE = REQUEST.RESPONSE blob = LOGO_KEY in customstyles and customstyles[LOGO_RIGHT_KEY] or None if not blob: return '' length = blob.get_size() if headers: RESPONSE.setHeader('Last-Modified', rfc1123_date(self.context._p_mtime)) RESPONSE.setHeader('Content-Type', blob.getContentType()) RESPONSE.setHeader('Accept-Ranges', 'bytes') if handleIfModifiedSince(self.context, REQUEST, RESPONSE): return '' RESPONSE.setHeader('Content-Length', length) filename = blob.getFilename() if filename is not None: filename = IUserPreferredFileNameNormalizer(REQUEST).normalize( unicode(filename, self.context.getCharset())) header_value = contentDispositionHeader( disposition=disposition, filename=filename) RESPONSE.setHeader("Content-disposition", header_value) range = handleRequestRange(self.context, length, REQUEST, RESPONSE) return blob.getIterator(**range)
def index_html(self, instance, REQUEST=None, RESPONSE=None, disposition='inline'): """ make it directly viewable when entering the objects URL """ if REQUEST is None: REQUEST = instance.REQUEST if RESPONSE is None: RESPONSE = REQUEST.RESPONSE blob = self.getUnwrapped(instance, raw=True) # TODO: why 'raw'? RESPONSE.setHeader('Last-Modified', rfc1123_date(instance._p_mtime)) RESPONSE.setHeader('Content-Type', self.getContentType(instance)) # The only change is to comment out this header: #RESPONSE.setHeader('Accept-Ranges', 'bytes') if handleIfModifiedSince(instance, REQUEST, RESPONSE): return '' length = blob.get_size() RESPONSE.setHeader('Content-Length', length) filename = self.getFilename(instance) if filename is not None: filename = IUserPreferredFileNameNormalizer(REQUEST).normalize( unicode(filename, instance.getCharset())) header_value = contentDispositionHeader( disposition=disposition, filename=filename) RESPONSE.setHeader("Content-disposition", header_value) range = handleRequestRange(instance, length, REQUEST, RESPONSE) return blob.getIterator(**range)
def test_index_html( self ): path, ref = self._extractFile() import os from webdav.common import rfc1123_date mod_time = os.stat( path )[ 8 ] image = self._makeOne( 'test_image', 'test_image.gif' ) image = image.__of__( self.root ) data = image.index_html( self.REQUEST, self.RESPONSE ) self.assertEqual( len( data ), len( ref ) ) self.assertEqual( data, ref ) # # ICK! 'HTTPResponse.getHeader' doesn't case-flatten the key! # self.assertEqual( self.RESPONSE.getHeader( 'Content-Length'.lower() ) , str( len( ref ) ) ) self.assertEqual( self.RESPONSE.getHeader( 'Content-Type'.lower() ) , 'image/gif' ) self.assertEqual( self.RESPONSE.getHeader( 'Last-Modified'.lower() ) , rfc1123_date( mod_time ) )
def test304ResponseSetsCache(self): response = self.publish(self.file_path, env={'HTTP_IF_MODIFIED_SINCE': rfc1123_date(DateTime()+7)}, basic=user_auth) self.assertEqual(response.getStatus(), 304) self.assertEqual(self.cache.data, None) self.assertEqual(self.cache.called, ['set'])
def test_index_html_200_with_cpm( self ): # should behave the same as without cpm installed from Products.CMFCore.tests.base.dummy import DummyCachingManager self.root.caching_policy_manager = DummyCachingManager() path, ref = self._extractFile('test_file.swf') import os from webdav.common import rfc1123_date file = self._makeOne( 'test_file', 'test_file.swf' ) file = file.__of__( self.root ) mod_time = os.stat( path )[ 8 ] data = file.index_html( self.REQUEST, self.RESPONSE ) self.assertEqual( len( data ), len( ref ) ) self.assertEqual( data, ref ) # ICK! 'HTTPResponse.getHeader' doesn't case-flatten the key! self.assertEqual( self.RESPONSE.getHeader( 'Content-Length'.lower() ) , str(len(ref)) ) self.assertEqual( self.RESPONSE.getHeader( 'Content-Type'.lower() ) , 'application/octet-stream' ) self.assertEqual( self.RESPONSE.getHeader( 'Last-Modified'.lower() ) , rfc1123_date( mod_time ) )
def test_index_html_with_304_and_caching( self ): # See collector #355 self._setupCachingPolicyManager(DummyCachingManager()) original_len = len(self.RESPONSE.headers) path, ref = self._extractFile() from webdav.common import rfc1123_date self.root.image = self._makeOne( 'test_image', 'test_image.gif' ) image = self.root.image transaction.savepoint(optimistic=True) mod_time = image.modified() self.REQUEST.environ[ 'IF_MODIFIED_SINCE' ] = '%s;' % rfc1123_date( mod_time+1 ) data = image.index_html( self.REQUEST, self.RESPONSE ) self.assertEqual( data, '' ) self.assertEqual( self.RESPONSE.getStatus(), 304 ) headers = self.RESPONSE.headers self.failUnless(len(headers) >= original_len + 3) self.failUnless('foo' in headers.keys()) self.failUnless('bar' in headers.keys()) self.assertEqual(headers['test_path'], '/test_image')
def index_html(self, REQUEST, RESPONSE): """ The default view of the contents of a File or Image. Returns the contents of the file or image. Also, sets the Content-Type HTTP header to the objects content type. """ # HTTP If-Modified-Since header handling. data = self._readFile() header=REQUEST.get_header('If-Modified-Since', None) if header is not None: header=string.split(header, ';')[0] # Some proxies seem to send invalid date strings for this # header. If the date string is not valid, we ignore it # rather than raise an error to be generally consistent # with common servers such as Apache (which can usually # understand the screwy date string as a lucky side effect # of the way they parse it). try: mod_since=long(DateTime(header).timeTime()) except: mod_since=None if mod_since is not None: last_mod = self.file_mod_time if last_mod > 0 and last_mod <= mod_since: RESPONSE.setStatus(304) return '' RESPONSE.setHeader('Last-Modified', rfc1123_date(self.file_mod_time)) RESPONSE.setHeader('Content-Type', self.content_type) RESPONSE.setHeader('Content-Length', len(data)) return data
def _if_modified_since_request_handler(self, REQUEST): """ HTTP If-Modified-Since header handling: return True if we can handle this request by returning a 304 response. """ header = REQUEST.get_header("If-Modified-Since", None) if header is not None: header = string.split(header, ";")[0] try: mod_since = long(DateTime(header).timeTime()) except: mod_since = None if mod_since is not None: if self._p_mtime: last_mod = long(self._p_mtime) else: last_mod = long(0) if last_mod > 0 and last_mod < mod_since: # Set headers for Apache caching last_mod = rfc1123_date(self._p_mtime) REQUEST.RESPONSE.setHeader("Last-Modified", last_mod) REQUEST.RESPONSE.setHeader("Content-Type", self.content_type) # RFC violation. See http://collector.zope.org/Zope/544 # REQUEST.RESPONSE.setHeader('Content-Length', self.get_size()) REQUEST.RESPONSE.setStatus(304) return 1
def _FSCacheHeaders(obj): REQUEST = getattr(obj, 'REQUEST', None) if REQUEST is None: return False RESPONSE = REQUEST.RESPONSE header = REQUEST.get_header('If-Modified-Since', None) last_mod = obj._file_mod_time if header is not None: header = header.split(';')[0] # Some proxies seem to send invalid date strings for this # header. If the date string is not valid, we ignore it # rather than raise an error to be generally consistent # with common servers such as Apache (which can usually # understand the screwy date string as a lucky side effect # of the way they parse it). try: mod_since=DateTime(header) mod_since=long(mod_since.timeTime()) except TypeError: mod_since=None if mod_since is not None: if last_mod > 0 and last_mod <= mod_since: RESPONSE.setStatus(304) return True #Last-Modified will get stomped on by a cache policy if there is #one set.... RESPONSE.setHeader('Last-Modified', rfc1123_date(last_mod))
def _if_modified_since_request_handler(self, REQUEST, RESPONSE): # HTTP If-Modified-Since header handling: return True if # we can handle this request by returning a 304 response header = REQUEST.get_header("If-Modified-Since", None) if header is not None: header = header.split(";")[0] # Some proxies seem to send invalid date strings for this # header. If the date string is not valid, we ignore it # rather than raise an error to be generally consistent # with common servers such as Apache (which can usually # understand the screwy date string as a lucky side effect # of the way they parse it). # This happens to be what RFC2616 tells us to do in the face of an # invalid date. try: mod_since = long(DateTime(header).timeTime()) except: mod_since = None if mod_since is not None: if self._p_mtime: last_mod = long(self._p_mtime) else: last_mod = long(0) if last_mod > 0 and last_mod <= mod_since: RESPONSE.setHeader("Last-Modified", rfc1123_date(self._p_mtime)) RESPONSE.setHeader("Content-Type", self.content_type) RESPONSE.setHeader("Accept-Ranges", "bytes") RESPONSE.setStatus(304) return True
def test_index_html_with_304_and_caching( self ): # See collector #355 self.root.caching_policy_manager = DummyCachingManager() original_len = len(self.RESPONSE.headers) path, ref = self._extractFile() import os from webdav.common import rfc1123_date mod_time = os.stat( path )[ 8 ] image = self._makeOne( 'test_image', 'test_image.gif' ) image = image.__of__( self.root ) self.REQUEST.environ[ 'IF_MODIFIED_SINCE' ] = '%s;' % rfc1123_date( mod_time+3600 ) data = image.index_html( self.REQUEST, self.RESPONSE ) self.assertEqual( data, '' ) self.assertEqual( self.RESPONSE.getStatus(), 304 ) headers = self.RESPONSE.headers self.failUnless(len(headers) >= original_len + 3) self.failUnless('foo' in headers.keys()) self.failUnless('bar' in headers.keys()) self.assertEqual(headers['test_path'], '/test_image')
def __call__(self): sm = getSecurityManager() if not sm.checkPermission(permissions.View, self.context.context): raise Unauthorized settings = self.context.settings filepath = self.context.filepath blob = settings.blob_files[filepath] blobfi = openBlob(blob) length = os.fstat(blobfi.fileno()).st_size blobfi.close() ext = os.path.splitext(os.path.normcase(filepath))[1][1:] if ext == 'txt': ct = 'text/plain' else: ct = 'image/%s' % ext self.request.response.setHeader('Last-Modified', rfc1123_date(self.context._p_mtime)) self.request.response.setHeader('Accept-Ranges', 'bytes') self.request.response.setHeader("Content-Length", length) self.request.response.setHeader('Content-Type', ct) request_range = handleRequestRange( self.context, length, self.request, self.request.response) return BlobStreamIterator(blob, **request_range)
def index_html (self, icon=0, preview=0, width=None, height=None, REQUEST=None): """ Return the file with it's corresponding MIME-type """ if REQUEST is not None: if self._if_modified_since_request_handler(REQUEST): self.ZCacheable_set(None) return '' if self._redirect_default_view_request_handler(icon, preview, REQUEST): return '' filename, content_type, icon, preview = self._get_file_to_serve(icon, preview) filename = self._get_fsname(filename) if _debug > 1: logger.info('serving %s, %s, %s, %s' %(filename, content_type, icon, preview)) if filename: size = os.stat(filename)[6] else: filename = self._get_icon_file(broken=True) size = os.stat(filename)[6] content_type = 'image/gif' icon = 1 if icon==0 and width is not None and height is not None: data = TemporaryFile() # hold resized image try: from PIL import Image im = Image.open(filename) if im.mode!='RGB': im = im.convert('RGB') filter = Image.BICUBIC if hasattr(Image, 'ANTIALIAS'): # PIL 1.1.3 filter = Image.ANTIALIAS im = im.resize((int(width),int(height)), filter) im.save(data, 'JPEG', quality=85) except: data = open(filename, 'rb') else: data.seek(0,2) size = data.tell() data.seek(0) content_type = 'image/jpeg' else: data = open(filename, 'rb') if REQUEST is not None: last_mod = rfc1123_date(self._p_mtime) REQUEST.RESPONSE.setHeader('Last-Modified', last_mod) REQUEST.RESPONSE.setHeader('Content-Type', content_type) REQUEST.RESPONSE.setHeader('Content-Length', size) self.ZCacheable_set(None) return stream_iterator(data) try: return data.read() finally: data.close()
def testRedirectDefaultViewReturns304(self): self.file.manage_file_upload(gifImage) self.file.redirect_default_view = 1 response = self.publish(self.file_path, env={'HTTP_IF_MODIFIED_SINCE': rfc1123_date(DateTime()+7)}, basic=user_auth) self.assertEqual(response.getStatus(), 304) self.assertEqual(response.getHeader('Location'), None)
def index_html(self, REQUEST, RESPONSE): """ The default view of the contents of a File or Image. Returns the contents of the file or image. Also, sets the Content-Type HTTP header to the objects content type. """ if self._if_modified_since_request_handler(REQUEST, RESPONSE): # we were able to handle this by returning a 304 # unfortunately, because the HTTP cache manager uses the cache # API, and because 304 responses are required to carry the Expires # header for HTTP/1.1, we need to call ZCacheable_set here. # This is nonsensical for caches other than the HTTP cache manager # unfortunately. self.ZCacheable_set(None) return "" if self.precondition and hasattr(self, str(self.precondition)): # Grab whatever precondition was defined and then # execute it. The precondition will raise an exception # if something violates its terms. c = getattr(self, str(self.precondition)) if hasattr(c, "isDocTemp") and c.isDocTemp: c(REQUEST["PARENTS"][1], REQUEST) else: c() if self._range_request_handler(REQUEST, RESPONSE): # we served a chunk of content in response to a range request. return "" RESPONSE.setHeader("Last-Modified", rfc1123_date(self._p_mtime)) RESPONSE.setHeader("Content-Type", self.content_type) RESPONSE.setHeader("Content-Length", self.size) RESPONSE.setHeader("Accept-Ranges", "bytes") if self.ZCacheable_isCachingEnabled(): result = self.ZCacheable_get(default=None) if result is not None: # We will always get None from RAMCacheManager and HTTP # Accelerated Cache Manager but we will get # something implementing the IStreamIterator interface # from a "FileCacheManager" return result self.ZCacheable_set(None) data = self.data if isinstance(data, str): RESPONSE.setBase(None) return data while data is not None: RESPONSE.write(data.data) data = data.next return ""
def view_image(self, REQUEST, RESPONSE): """ The default view of the contents of an Image. Returns the contents of the file or image. Also, sets the Content-Type HTTP header to the objects content type. """ # HTTP If-Modified-Since header handling. header=REQUEST.get_header('If-Modified-Since', None) if header is not None: header=string.split(header, ';')[0] # Some proxies seem to send invalid date strings for this # header. If the date string is not valid, we ignore it # rather than raise an error to be generally consistent # with common servers such as Apache (which can usually # understand the screwy date string as a lucky side effect # of the way they parse it). try: mod_since=long(DateTime(header).timeTime()) except: mod_since=None last_mod = long(0) if mod_since is not None: if self._p_mtime: last_mod = long(self._p_mtime) else: last_mod = long(0) if last_mod > 0 and last_mod <= mod_since: # Set header values since apache caching will return Content-Length # of 0 in response if size is not set here RESPONSE.setHeader('Last-Modified', rfc1123_date(self._p_mtime)) RESPONSE.setHeader('Content-Type', self._v_image['content_type']) RESPONSE.setHeader('Content-Length', self._v_image['size']) RESPONSE.setStatus(304) return '' RESPONSE.setHeader('Last-Modified', rfc1123_date(self._p_mtime)) RESPONSE.setHeader('Content-Type', self._v_image['content_type']) RESPONSE.setHeader('Content-Length', self._v_image['size']) data=self._v_image['data'] if type(data) is type(''): return data while data is not None: RESPONSE.write(data.data) data=data.next return ''
def other_headers(self, headers): if self._include_last_modified and 'Last-Modified' not in headers: # If missing, add a last-modified header with the modification time. modification = self.context.get_modification_datetime() if modification is not None: self.response.setHeader( 'Last-Modified', rfc1123_date(modification)) super(HTTPResponseHeaders, self).other_headers(headers)
def index_html(self, REQUEST, RESPONSE): """ The default view of the contents of a File or Image. Returns the contents of the file or image. Also, sets the Content-Type HTTP header to the objects content type. """ self._updateFromFS() data = self._data # HTTP If-Modified-Since header handling. header=REQUEST.get_header('If-Modified-Since', None) if header is not None: header = header.split(';')[0] # Some proxies seem to send invalid date strings for this # header. If the date string is not valid, we ignore it # rather than raise an error to be generally consistent # with common servers such as Apache (which can usually # understand the screwy date string as a lucky side effect # of the way they parse it). try: mod_since=long(DateTime(header).timeTime()) except: mod_since=None if mod_since is not None: last_mod = self._file_mod_time if last_mod > 0 and last_mod <= mod_since: # Set header values since apache caching will return # Content-Length of 0 in response if size is not set here RESPONSE.setHeader('Last-Modified', rfc1123_date(last_mod)) RESPONSE.setHeader('Content-Type', self.content_type) RESPONSE.setHeader('Content-Length', len(data)) RESPONSE.setStatus(304) return '' #Last-Modified will get stomped on by a cache policy it there is one set.... RESPONSE.setHeader('Last-Modified', rfc1123_date(self._file_mod_time)) RESPONSE.setHeader('Content-Type', self.content_type) RESPONSE.setHeader('Content-Length', len(data)) #There are 2 Cache Managers which can be in play....need to decide which to use #to determine where the cache headers are decided on. if self.ZCacheable_getManager() is not None: self.ZCacheable_set(None) else: _setCacheHeaders(self, extra_context={}) return data
def index_html(self, REQUEST, RESPONSE=None): """Default view for VirtualBinary file""" ranges = None if RESPONSE is None: RESPONSE = REQUEST.RESPONSE if self.__content_class__ is None: # Build your own headers RESPONSE.setHeader('Content-Type', self.content_type) RESPONSE.setHeader('Content-Length', self.size) else: # Call index_html method of content class with a fake # self.data attribute which will return empty value # Use this artifice to make sure content class is not loading # all data in memory since it is better to return a stream iterator # There is an exception with multiple range if REQUEST.environ.has_key('HTTP_RANGE'): ranges = parseRange(REQUEST.environ.get('HTTP_RANGE')) ## in case of mutiple range we don't know do with an iterator if ranges is not None and len(ranges) > 1: ## call normally OFS.image with data return self.__content_class__.index_html(self, REQUEST, RESPONSE) else: ### now we deal correctly with 304 header if self._if_modified_since_request_handler(REQUEST, RESPONSE): self.ZCacheable_set(None) return '' ### set correctly header RESPONSE.setHeader('Last-Modified', rfc1123_date(self._p_mtime)) RESPONSE.setHeader('Content-Type', self.content_type) RESPONSE.setHeader('Content-Length', self.size) RESPONSE.setHeader('Accept-Ranges', 'bytes') self.ZCacheable_set(None) # This is a default header that can be bypassed by other products # such as attachment field. if RESPONSE.getHeader('content-disposition') is None: # headers are in lower case in HTTPResponse RESPONSE.setHeader( 'Content-Disposition', 'inline; filename="%s"' % self.filename ) if ranges and len(ranges) == 1: ## is an range request with one range , ## return an iterator with this range [(start,end)] = ranges if end is None: end = self.size iterator = range_filestream_iterator(self.path, start, end, mode='rb') return iterator else: return filestream_iterator(self.path, mode='rb')
def dav__init(self, request, response): # We are allowed to accept a url w/o a trailing slash # for a collection, but are supposed to provide a # hint to the client that it should be using one. # [WebDAV, 5.2] pathinfo=request.get('PATH_INFO','') if pathinfo and pathinfo[-1] != '/': location='%s/' % request['URL1'] response.setHeader('Content-Location', location) response.setHeader('Connection', 'close', 1) response.setHeader('Date', rfc1123_date(), 1)
def index_html(self, REQUEST, RESPONSE): """ The default view of the contents of a File or Image. Returns the contents of the file or image. Also, sets the Content-Type HTTP header to the objects content type. """ self._updateFromFS() data = self._readFile(0) data_len = len(data) last_mod = self._file_mod_time status = 200 # HTTP If-Modified-Since header handling. header = REQUEST.get_header('If-Modified-Since', None) if header is not None: header = header.split(';')[0] # Some proxies seem to send invalid date strings for this # header. If the date string is not valid, we ignore it # rather than raise an error to be generally consistent # with common servers such as Apache (which can usually # understand the screwy date string as a lucky side effect # of the way they parse it). try: mod_since = long(DateTime(header).timeTime()) except: mod_since = None if mod_since is not None: if last_mod > 0 and last_mod <= mod_since: status = 304 data = '' #Last-Modified will get stomped on by a cache policy it there is #one set.... RESPONSE.setStatus(status) RESPONSE.setHeader('Last-Modified', rfc1123_date(last_mod)) RESPONSE.setHeader('Content-Type', self.content_type) if status != 304: # Avoid setting content-length for a 304. See RFC 2616. # Zope might still, for better or for worse, set a # content-length header with value "0". RESPONSE.setHeader('Content-Length', data_len) #There are 2 Cache Managers which can be in play.... #need to decide which to use to determine where the cache headers #are decided on. if self.ZCacheable_getManager() is not None: self.ZCacheable_set(None) else: _setCacheHeaders(_ViewEmulator().__of__(self), extra_context={}) return data
def index_html(self, instance, REQUEST=None, RESPONSE=None, charset='utf-8', disposition='inline'): """Kicks download. Writes data including file name and content type to RESPONSE """ if REQUEST is None: REQUEST = instance.REQUEST if RESPONSE is None: RESPONSE = REQUEST.RESPONSE RESPONSE.setHeader('Last-Modified', rfc1123_date(instance._p_mtime)) RESPONSE.setHeader('Content-Type', self.getContentType(instance)) RESPONSE.setHeader('Accept-Ranges', 'bytes') if handleIfModifiedSince(instance, REQUEST, RESPONSE): return '' length = self.get_size(instance) RESPONSE.setHeader('Content-Length', length) filename = self.getFilename(instance) if filename is not None: if isinstance(filename, unicode): filename = filename.encode(charset, errors="ignore") # Create a user agent specific disposition header # IE needs an url quoted filename # Other browsers need an unquoted filename user_agent = REQUEST.get('HTTP_USER_AGENT', '') if 'MSIE' in user_agent: header_value = '%s; filename=%s' % (disposition, quote(filename)) else: header_value = '%s; filename="%s"' % (disposition, filename) RESPONSE.setHeader("Content-disposition", header_value) request_range = handleRequestRange(instance, length, REQUEST, RESPONSE) # Notify file downloads, but do not notify range requests if not ('start' in request_range and request_range['start'] > 0): portal_state = getMultiAdapter((instance, instance.REQUEST), name='plone_portal_state') if not portal_state.anonymous(): registry = getUtility(IRegistry) user_ids = registry['ftw.file.filesettings.user_ids'] if portal_state.member().id \ and not portal_state.member().id in user_ids: notify(FileDownloadedEvent(instance, filename)) return self.get(instance).getIterator(**request_range)
def index_html(self, REQUEST, RESPONSE): """ The default view of the contents of a File or Image. Returns the contents of the file or image. Also, sets the Content-Type HTTP header to the objects content type. """ self._updateFromFS() data = self._readFile(0) data_len = len(data) last_mod = self._file_mod_time status = 200 # HTTP If-Modified-Since header handling. header=REQUEST.get_header('If-Modified-Since', None) if header is not None: header = header.split(';')[0] # Some proxies seem to send invalid date strings for this # header. If the date string is not valid, we ignore it # rather than raise an error to be generally consistent # with common servers such as Apache (which can usually # understand the screwy date string as a lucky side effect # of the way they parse it). try: mod_since=long(DateTime(header).timeTime()) except: mod_since=None if mod_since is not None: if last_mod > 0 and last_mod <= mod_since: status = 304 data = '' #Last-Modified will get stomped on by a cache policy it there is #one set.... RESPONSE.setStatus(status) RESPONSE.setHeader('Last-Modified', rfc1123_date(last_mod)) RESPONSE.setHeader('Content-Type', self.content_type) if status != 304: # Avoid setting content-length for a 304. See RFC 2616. # Zope might still, for better or for worse, set a # content-length header with value "0". RESPONSE.setHeader('Content-Length', data_len) #There are 2 Cache Managers which can be in play.... #need to decide which to use to determine where the cache headers #are decided on. if self.ZCacheable_getManager() is not None: self.ZCacheable_set(None) else: _setCacheHeaders(_ViewEmulator().__of__(self), extra_context={}) return data
def index_html(self, REQUEST, RESPONSE): """ The default view of the contents of a File or Image. Returns the contents of the file or image. Also, sets the Content-Type HTTP header to the objects content type. """ self._updateFromFS() data = self._data # HTTP If-Modified-Since header handling. header = REQUEST.get_header('If-Modified-Since', None) if header is not None: header = string.split(header, ';')[0] # Some proxies seem to send invalid date strings for this # header. If the date string is not valid, we ignore it # rather than raise an error to be generally consistent # with common servers such as Apache (which can usually # understand the screwy date string as a lucky side effect # of the way they parse it). try: mod_since = long(DateTime(header).timeTime()) except: mod_since = None if mod_since is not None: last_mod = self._file_mod_time if last_mod > 0 and last_mod <= mod_since: # Set header values since apache caching will return # Content-Length of 0 in response if size is not set here RESPONSE.setHeader('Last-Modified', rfc1123_date(last_mod)) RESPONSE.setHeader('Content-Type', self.content_type) RESPONSE.setHeader('Content-Length', len(data)) RESPONSE.setStatus(304) return '' RESPONSE.setHeader('Last-Modified', rfc1123_date(self._file_mod_time)) RESPONSE.setHeader('Content-Type', self.content_type) RESPONSE.setHeader('Content-Length', len(data)) return data
def wrapper(self, REQUEST, RESPONSE, **kwargs): """ Look in cache: - if item exists in cache: if If-Modified-Sience in REQUEST headers: set status 304, Not Modified return '' return cached value - else compute result, cache and return it. """ RESPONSE.setHeader('Cache-Control', 'public,max-age=3600') kwargs.update(REQUEST.form) # Get page from cache if exists keyset = None if self.ZCacheable_isCachingEnabled(): # Prepare a cache key. keyset = kwargs.copy() keyset['*'] = () result = self.ZCacheable_get(keywords=keyset, default=_marker) if result is not _marker: last_mod_req = REQUEST.get_header('If-Modified-Since', None) if not last_mod_req: # Return from server cache REQUEST.RESPONSE.setHeader('Last-Modified', rfc1123_date()) return result # Return from client cache RESPONSE.setStatus(304) return '' # Compute result result = method(self, REQUEST=REQUEST, RESPONSE=RESPONSE, **kwargs) # Update cache if keyset is not None: self.ZCacheable_set(result, keywords=keyset) REQUEST.RESPONSE.setHeader('Last-Modified', rfc1123_date()) return result
def dav__init(self, request, response): # We are allowed to accept a url w/o a trailing slash # for a collection, but are supposed to provide a # hint to the client that it should be using one. # [WebDAV, 5.2] pathinfo = request.get('PATH_INFO', '') if pathinfo and pathinfo[-1] != '/': location = '%s/' % request['URL1'] response.setHeader('Content-Location', location) # We sniff for a ZServer response object, because we don't # want to write duplicate headers (since ZS writes Date # and Connection itself). if not hasattr(response, '_server_version'): response.setHeader('Connection', 'close', 1) response.setHeader('Date', rfc1123_date(), 1)
def dav__init(self, request, response): # Init expected HTTP 1.1 / WebDAV headers which are not # currently set by the base response object automagically. # # We sniff for a ZServer response object, because we don't # want to write duplicate headers (since ZS writes Date # and Connection itself). if not hasattr(response, '_server_version'): response.setHeader('Connection', 'close') response.setHeader('Date', rfc1123_date(), 1) # HTTP Range support if HTTPRangeInterface.providedBy(self): response.setHeader('Accept-Ranges', 'bytes') else: response.setHeader('Accept-Ranges', 'none')
def index_html(self, REQUEST=None, RESPONSE=None, charset='utf-8', disposition='inline'): """ make it directly viewable when entering the objects URL """ if REQUEST is None: REQUEST = self.REQUEST if RESPONSE is None: RESPONSE = REQUEST.RESPONSE RESPONSE.setHeader('Last-Modified', rfc1123_date(self._p_mtime)) RESPONSE.setHeader('Content-Type', self.getContentType()) RESPONSE.setHeader('Accept-Ranges', 'bytes') if handleIfModifiedSince(self, REQUEST, RESPONSE): return '' length = self.get_size() RESPONSE.setHeader('Content-Length', length) filename = self.getFilename() if filename is not None: if REQUEST.HTTP_USER_AGENT.find('MSIE') != -1: if isinstance(filename, unicode): filename = filename.encode('gb18030') else: filename = unicode(filename, charset, errors="ignore") filename = filename.encode('gb18030') header_value = contentDispositionHeader(disposition, 'gb18030', filename=filename) else: if not isinstance(filename, unicode): filename = unicode(filename, charset, errors="ignore") # filename = IUserPreferredFileNameNormalizer(REQUEST).normalize( # filename) header_value = contentDispositionHeader(disposition=disposition, filename=filename) RESPONSE.setHeader("Content-disposition", header_value) request_range = handleRequestRange(self, length, REQUEST, RESPONSE) return self.getIterator(**request_range)
def test_304_response_from_cpm(self): # test that we get a 304 response from the cpm via this template from DateTime import DateTime from webdav.common import rfc1123_date from Products.CMFCore.tests.base.dummy \ import DummyCachingManagerWithPolicy mod_time = DateTime() self.root.caching_policy_manager = DummyCachingManagerWithPolicy() script = self._makeOne('testReST', 'testReST.rst') script = script.__of__(self.root) self.REQUEST.environ['IF_MODIFIED_SINCE'] = '%s;' % rfc1123_date( mod_time + 3600) data = script(self.REQUEST, self.RESPONSE) self.assertEqual(data, '') self.assertEqual(self.RESPONSE.getStatus(), 304)
def other_headers(self, headers): if self._include_last_modified: self.response.setHeader( 'Last-Modified', rfc1123_date(self.context.get_modification_datetime())) self.response.setHeader( 'Content-Disposition', 'inline;filename=%s' % (self.context.get_filename())) self.response.setHeader('Content-Type', self.context.get_content_type()) if self.context.get_content_encoding(): self.response.setHeader('Content-Encoding', self.context.get_content_encoding()) if not self.response.getHeader('Content-Length'): self.response.setHeader('Content-Length', self.context.get_file_size()) if not self.response.getHeader('Accept-Ranges'): self.response.setHeader('Accept-Ranges', 'none')
def test_304_response_from_cpm(self): # test that we get a 304 response from the cpm via this template from webdav.common import rfc1123_date mod_time = DateTime() self._setupCachingPolicyManager(DummyCachingManagerWithPolicy()) content = DummyContent(id='content') content.modified_date = mod_time content = content.__of__(self.root) script = self._makeOne('testDTML', 'testDTML.dtml') script = script.__of__(content) self.REQUEST.environ['IF_MODIFIED_SINCE'] = '%s;' % rfc1123_date( mod_time + 3600) data = script(content, self.REQUEST, self.RESPONSE) self.assertEqual(data, '') self.assertEqual(self.RESPONSE.getStatus(), 304)
def test_index_html_without_304(self): path, ref = self._extractFile() import os from webdav.common import rfc1123_date mod_time = os.stat(path)[8] image = self._makeOne('test_image', 'test_image.gif') image = image.__of__(self.root) self.REQUEST.environ['IF_MODIFIED_SINCE'] = '%s;' % rfc1123_date( mod_time - 3600) data = image.index_html(self.REQUEST, self.RESPONSE) self.failUnless(data, '') self.assertEqual(self.RESPONSE.getStatus(), 200)
def test_index_html_with_304(self): path, ref = self._extractFile() import os from webdav.common import rfc1123_date mod_time = os.stat(path)[8] file = self._makeOne('test_file', 'test_file.swf') file = file.__of__(self.root) self.REQUEST.environ['IF_MODIFIED_SINCE'] = '%s;' % rfc1123_date( mod_time + 3600) data = file.index_html(self.REQUEST, self.RESPONSE) self.assertEqual(data, '') self.assertEqual(self.RESPONSE.getStatus(), 304)
def test_index_html_with_304_from_cpm(self): self._setupCachingPolicyManager(DummyCachingManagerWithPolicy()) path, ref = self._extractFile() from webdav.common import rfc1123_date from Products.CMFCore.tests.base.dummy import FAKE_ETAG self.root.file = self._makeOne('test_file', 'test_image.jpg', file=ref) file = self.root.file mod_time = file.modified() self.REQUEST.environ['IF_MODIFIED_SINCE'] = '%s;' % rfc1123_date( mod_time) self.REQUEST.environ['IF_NONE_MATCH'] = '%s;' % FAKE_ETAG data = file.index_html(self.REQUEST, self.RESPONSE) self.assertEqual(len(data), 0) self.assertEqual(self.RESPONSE.getStatus(), 304)
def render_attachment_preview(self, attachment): sm = getSecurityManager() if not sm.checkPermission(permissions.View, self.context): raise Unauthorized r = self.request.response # avoid long dreaded CSRF error annotations = IAnnotations(attachment) if not annotations.get('collective.documentviewer', None): safeWrite(attachment) settings = Settings(attachment) # possibly creates annotation if self.preview_type not in ('large', 'normal', 'small'): self.preview_type = 'small' if self.page is None: self.page = 1 filepath = u'%s/dump_%s.%s' % (self.preview_type, self.page, settings.pdf_image_format) try: blob = settings.blob_files[filepath] except TypeError: # 'NoneType' object has no attribute '__getitem__' # happens e.g. when missing preview for stream attachment return blobfi = openBlob(blob) length = os.fstat(blobfi.fileno()).st_size blobfi.close() ext = os.path.splitext(os.path.normcase(filepath))[1][1:] if ext == 'txt': ct = 'text/plain' else: ct = 'image/%s' % ext r.setHeader('Content-Type', ct) r.setHeader('Last-Modified', rfc1123_date(self.context._p_mtime)) r.setHeader('Accept-Ranges', 'bytes') r.setHeader("Content-Length", length) request_range = handleRequestRange(self.context, length, self.request, self.request.response) return BlobStreamIterator(blob, **request_range)
def test_index_html_with_304_from_cpm(self): self.root.caching_policy_manager = DummyCachingManagerWithPolicy() path, ref = self._extractFile() import os from webdav.common import rfc1123_date from base.dummy import FAKE_ETAG file = self._makeOne('test_file', 'test_image.gif') file = file.__of__(self.root) mod_time = os.stat(path)[8] self.REQUEST.environ['IF_MODIFIED_SINCE'] = '%s;' % rfc1123_date( mod_time) self.REQUEST.environ['IF_NONE_MATCH'] = '%s;' % FAKE_ETAG data = file.index_html(self.REQUEST, self.RESPONSE) self.assertEqual(len(data), 0) self.assertEqual(self.RESPONSE.getStatus(), 304)
def render_blob_version(self): # done much like it is done in plone.app.blob's index_html header_value = contentDispositionHeader( disposition='inline', filename=self.context.getFilename().replace('.pdf', '.swf')) blob = self.settings.data blobfi = openBlob(blob) length = fstat(blobfi.fileno()).st_size blobfi.close() self.request.response.setHeader('Last-Modified', rfc1123_date(self.context._p_mtime)) self.request.response.setHeader('Accept-Ranges', 'bytes') self.request.response.setHeader('Content-Disposition', header_value) self.request.response.setHeader("Content-Length", length) self.request.response.setHeader('Content-Type', 'application/x-shockwave-flash') range = handleRequestRange(self.context, length, self.request, self.request.response) return BlobStreamIterator(blob, **range)
def stream_avatar_data(profile, size, request): """Generate avatar at the specified size and stream it This is a utility method used by the browser views below. """ response = request.response if not profile: return default_avatar(response) imaging = plone_api.content.get_view( request=request, context=profile, name='images') if size not in AVATAR_SIZES: return default_avatar(response) width = height = AVATAR_SIZES.get(size) try: scale = imaging.scale( fieldname='portrait', width=width, height=height, direction='down', ) except TypeError: # No image found return default_avatar(response) if scale is not None: data = scale.data mtime = rfc1123_date(profile._p_mtime) response.setHeader('Last-Modified', mtime) from plone.namedfile.utils import set_headers, stream_data set_headers(data, response) return stream_data(data) else: return default_avatar(response)
def __call__(self): sm = getSecurityManager() if not sm.checkPermission(permissions.View, self.context.context): raise Unauthorized settings = self.context.settings filepath = self.context.filepath blob = settings.blob_files[filepath] filename = blob._p_blob_uncommitted or blob.committed() length = os.path.getsize(filename) ext = os.path.splitext(os.path.normcase(filepath))[1][1:] if ext == 'txt': ct = 'text/plain' else: ct = 'image/%s' % ext self.request.response.setHeader('Last-Modified', rfc1123_date(self.context._p_mtime)) self.request.response.setHeader("Content-Length", length) self.request.response.setHeader('Content-Type', ct) return filestream_iterator(filename, 'rb')
def test_index_html_with_304(self): path, ref = self._extractFile() import os from webdav.common import rfc1123_date mod_time = os.stat(path)[8] image = self._makeOne('test_image', 'test_image.gif') image = image.__of__(self.root) self.REQUEST.environ['IF_MODIFIED_SINCE'] = '%s;' % rfc1123_date( mod_time + 3600) data = image.index_html(self.REQUEST, self.RESPONSE) self.assertEqual(data, '') # test that we properly hack around apache bug noted in code self.assertEqual(self.RESPONSE.getHeader('Content-Length'.lower()), str(len(ref))) self.assertEqual(self.RESPONSE.getStatus(), 304)
def test_index_html_with_304( self ): path, ref = self._extractFile('test_file.swf') import os from webdav.common import rfc1123_date mod_time = os.stat( path )[ 8 ] file = self._makeOne( 'test_file', 'test_file.swf' ) file = file.__of__( self.root ) self.REQUEST.environ[ 'IF_MODIFIED_SINCE' ] = '%s;' % rfc1123_date( mod_time+3600 ) data = file.index_html( self.REQUEST, self.RESPONSE ) self.assertEqual( data, '' ) # test that we don't supply a content-length self.assertEqual( self.RESPONSE.getHeader('Content-Length'.lower()), None ) self.assertEqual( self.RESPONSE.getStatus(), 304 )
def index_html(self, REQUEST=None, RESPONSE=None, charset='utf-8', disposition='inline'): """ make it directly viewable when entering the objects URL """ if REQUEST is None: REQUEST = self.REQUEST if RESPONSE is None: RESPONSE = REQUEST.RESPONSE RESPONSE.setHeader('Last-Modified', rfc1123_date(self._p_mtime)) RESPONSE.setHeader('Content-Type', self.getContentType()) RESPONSE.setHeader('Accept-Ranges', 'bytes') if handleIfModifiedSince(self, REQUEST, RESPONSE): return '' length = self.get_size() RESPONSE.setHeader('Content-Length', length) filename = self.getFilename() if filename is not None: if not isinstance(filename, six.text_type): filename = six.text_type(filename, charset, errors='ignore') filename = IUserPreferredFileNameNormalizer( REQUEST, ).normalize( filename, ) header_value = contentDispositionHeader( disposition=disposition, filename=filename, ) # Add original filename in utf-8, ref to rfc2231 RESPONSE.setHeader('Content-disposition', header_value) request_range = handleRequestRange(self, length, REQUEST, RESPONSE) return self.getIterator(**request_range)
def test_index_html(self): path, ref = self._extractFile() import os from webdav.common import rfc1123_date mod_time = os.stat(path)[8] image = self._makeOne('test_image', 'test_image.gif') image = image.__of__(self.root) data = image.index_html(self.REQUEST, self.RESPONSE) self.assertEqual(len(data), len(ref)) self.assertEqual(data, ref) # ICK! 'HTTPResponse.getHeader' doesn't case-flatten the key! self.assertEqual(self.RESPONSE.getHeader('Content-Length'.lower()), str(len(ref))) self.assertEqual(self.RESPONSE.getHeader('Content-Type'.lower()), 'image/gif') self.assertEqual(self.RESPONSE.getHeader('Last-Modified'.lower()), rfc1123_date(mod_time))
def __call__(self): sm = getSecurityManager() if not sm.checkPermission(permissions.View, self.context.context): raise Unauthorized settings = self.context.settings filepath = self.context.filepath blob = settings.blob_files[filepath] blobfi = openBlob(blob) length = os.fstat(blobfi.fileno()).st_size blobfi.close() ext = os.path.splitext(os.path.normcase(filepath))[1][1:] if ext == 'txt': ct = 'text/plain' else: ct = 'image/%s' % ext self.request.response.setHeader('Last-Modified', rfc1123_date(self.context._p_mtime)) self.request.response.setHeader('Accept-Ranges', 'bytes') self.request.response.setHeader("Content-Length", length) self.request.response.setHeader('Content-Type', ct) range = handleRequestRange(self.context, length, self.request, self.request.response) return BlobStreamIterator(blob, **range)
def test_index_html_200_with_cpm(self): # should behave the same as without cpm installed self.root.caching_policy_manager = DummyCachingManager() path, ref = self._extractFile() from webdav.common import rfc1123_date file = self._makeOne('test_file', 'test_file.swf', file=ref) file = file.__of__(self.root) mod_time = file.modified().timeTime() data = file.index_html(self.REQUEST, self.RESPONSE) self.assertEqual(len(data), len(ref)) self.assertEqual(data, ref) # ICK! 'HTTPResponse.getHeader' doesn't case-flatten the key! self.assertEqual(self.RESPONSE.getHeader('Content-Length'.lower()), str(len(ref))) self.assertEqual(self.RESPONSE.getHeader('Content-Type'.lower()), 'application/octet-stream') self.assertEqual(self.RESPONSE.getHeader('Last-Modified'.lower()), rfc1123_date(mod_time))
def test_forced_content_type( self ): path, ref = self._extractFile('test_file_two.swf') import os from webdav.common import rfc1123_date mod_time = os.stat( path )[ 8 ] file = self._makeOne( 'test_file', 'test_file_two.swf' ) file = file.__of__( self.root ) data = file.index_html( self.REQUEST, self.RESPONSE ) self.assertEqual( len( data ), len( ref ) ) self.assertEqual( data, ref ) # ICK! 'HTTPResponse.getHeader' doesn't case-flatten the key! self.assertEqual( self.RESPONSE.getHeader( 'Content-Length'.lower() ) , str(len(ref)) ) self.assertEqual( self.RESPONSE.getHeader( 'Content-Type'.lower() ) , 'application/x-shockwave-flash' ) self.assertEqual( self.RESPONSE.getHeader( 'Last-Modified'.lower() ) , rfc1123_date( mod_time ) )
def test_index_html_200_with_cpm(self): self._setupCachingPolicyManager(DummyCachingManagerWithPolicy()) path, ref = self._extractFile() from webdav.common import rfc1123_date file = self._makeOne('test_file', 'test_image.jpg', file=ref) file = file.__of__(self.root) mod_time = file.modified() data = file.index_html(self.REQUEST, self.RESPONSE) # should behave the same as without cpm self.assertEqual(len(data), len(ref)) self.assertEqual(data, ref) # ICK! 'HTTPResponse.getHeader' doesn't case-flatten the key! self.assertEqual(self.RESPONSE.getHeader('Content-Length'.lower()), str(len(ref))) self.assertEqual(self.RESPONSE.getHeader('Content-Type'.lower()), 'image/jpeg') self.assertEqual(self.RESPONSE.getHeader('Last-Modified'.lower()), rfc1123_date(mod_time))
def __call__(self): try: data = self.get_data() except IOError: # can be from zeo client blob file weirdness with PIL # ocassionally logger.info('Could not get blob data', exc_info=True) raise NotFound if data: is_blob = False if isinstance(data, basestring): length = len(data) else: is_blob = True blobfi = openBlob(data) length = fstat(blobfi.fileno()).st_size blobfi.close() self.request.response.setHeader( 'Last-Modified', rfc1123_date(self.context._p_mtime)) resp = self.request.response resp.setHeader( 'Content-Disposition', 'inline; filename=%s.%s' % (self.context.getId(), self.file_ext)) resp.setHeader("Content-Length", length) resp.setHeader('Content-Type', self.content_type) if is_blob: resp.setHeader('Accept-Ranges', 'bytes') range = handleRequestRange(self.context, length, self.request, self.request.response) return BlobStreamIterator(data, **range) else: return data else: raise NotFound
def index_html(self, REQUEST, RESPONSE): """ The default view of the contents of a File or Image. Returns the contents of the file or image. Also, sets the Content-Type HTTP header to the objects content type. """ if self.precondition and hasattr(self, str(self.precondition)): # Grab whatever precondition was defined and then # execute it. The precondition will raise an exception # if something violates its terms. c = getattr(self, str(self.precondition)) if hasattr(c, 'isDocTemp') and c.isDocTemp: # DTML thingy c(REQUEST['PARENTS'][1], REQUEST) else: c() RESPONSE.setHeader('Last-Modified', rfc1123_date(self._p_mtime)) RESPONSE.setHeader('Content-Type', self.content_type) RESPONSE.setHeader('Content-Length', self.size) return self.getIterator()
def createLastModifiedDate(self, offset=0): return rfc1123_date(self.file._p_mtime + offset)
def getHTTPCachingHeaders( self, content, view_name, keywords, time=None ): # if the object has a modified method, add it as last-modified if hasattr(content, 'modified'): headers = ( ('Last-modified', rfc1123_date(content.modified()) ), ) return headers
def _range_request_handler(self, REQUEST, RESPONSE): # HTTP Range header handling: return True if we've served a range # chunk out of our data. range = REQUEST.get_header('Range', None) request_range = REQUEST.get_header('Request-Range', None) if request_range is not None: # Netscape 2 through 4 and MSIE 3 implement a draft version # Later on, we need to serve a different mime-type as well. range = request_range if_range = REQUEST.get_header('If-Range', None) if range is not None: ranges = HTTPRangeSupport.parseRange(range) if if_range is not None: # Only send ranges if the data isn't modified, otherwise send # the whole object. Support both ETags and Last-Modified dates! if len(if_range) > 1 and if_range[:2] == 'ts': # ETag: if if_range != self.http__etag(): # Modified, so send a normal response. We delete # the ranges, which causes us to skip to the 200 # response. ranges = None else: # Date date = if_range.split(';')[0] try: mod_since = long(DateTime(date).timeTime()) except: mod_since = None if mod_since is not None: if self._p_mtime: last_mod = long(self._p_mtime) else: last_mod = long(0) if last_mod > mod_since: # Modified, so send a normal response. We delete # the ranges, which causes us to skip to the 200 # response. ranges = None if ranges: # Search for satisfiable ranges. satisfiable = 0 for start, end in ranges: if start < self.size: satisfiable = 1 break if not satisfiable: RESPONSE.setHeader('Content-Range', 'bytes */%d' % self.size) RESPONSE.setHeader('Accept-Ranges', 'bytes') RESPONSE.setHeader('Last-Modified', rfc1123_date(self._p_mtime)) RESPONSE.setHeader('Content-Type', self.content_type) RESPONSE.setHeader('Content-Length', self.size) RESPONSE.setStatus(416) return True ranges = HTTPRangeSupport.expandRanges(ranges, self.size) if len(ranges) == 1: # Easy case, set extra header and return partial set. start, end = ranges[0] size = end - start RESPONSE.setHeader('Last-Modified', rfc1123_date(self._p_mtime)) RESPONSE.setHeader('Content-Type', self.content_type) RESPONSE.setHeader('Content-Length', size) RESPONSE.setHeader('Accept-Ranges', 'bytes') RESPONSE.setHeader( 'Content-Range', 'bytes %d-%d/%d' % (start, end - 1, self.size)) RESPONSE.setStatus(206) # Partial content data = self.data if isinstance(data, str): RESPONSE.write(data[start:end]) return True # Linked Pdata objects. Urgh. pos = 0 while data is not None: l = len(data.data) pos = pos + l if pos > start: # We are within the range lstart = l - (pos - start) if lstart < 0: lstart = 0 # find the endpoint if end <= pos: lend = l - (pos - end) # Send and end transmission RESPONSE.write(data[lstart:lend]) break # Not yet at the end, transmit what we have. RESPONSE.write(data[lstart:]) data = data.next return True else: boundary = choose_boundary() # Calculate the content length size = ( 8 + len(boundary) + # End marker length len(ranges) * ( # Constant lenght per set 49 + len(boundary) + len(self.content_type) + len('%d' % self.size))) for start, end in ranges: # Variable length per set size = (size + len('%d%d' % (start, end - 1)) + end - start) # Some clients implement an earlier draft of the spec, they # will only accept x-byteranges. draftprefix = (request_range is not None) and 'x-' or '' RESPONSE.setHeader('Content-Length', size) RESPONSE.setHeader('Accept-Ranges', 'bytes') RESPONSE.setHeader('Last-Modified', rfc1123_date(self._p_mtime)) RESPONSE.setHeader( 'Content-Type', 'multipart/%sbyteranges; boundary=%s' % (draftprefix, boundary)) RESPONSE.setStatus(206) # Partial content data = self.data # The Pdata map allows us to jump into the Pdata chain # arbitrarily during out-of-order range searching. pdata_map = {} pdata_map[0] = data for start, end in ranges: RESPONSE.write('\r\n--%s\r\n' % boundary) RESPONSE.write('Content-Type: %s\r\n' % self.content_type) RESPONSE.write( 'Content-Range: bytes %d-%d/%d\r\n\r\n' % (start, end - 1, self.size)) if isinstance(data, str): RESPONSE.write(data[start:end]) else: # Yippee. Linked Pdata objects. The following # calculations allow us to fast-forward through the # Pdata chain without a lot of dereferencing if we # did the work already. first_size = len(pdata_map[0].data) if start < first_size: closest_pos = 0 else: closest_pos = (( (start - first_size) >> 16 << 16) + first_size) pos = min(closest_pos, max(pdata_map.keys())) data = pdata_map[pos] while data is not None: l = len(data.data) pos = pos + l if pos > start: # We are within the range lstart = l - (pos - start) if lstart < 0: lstart = 0 # find the endpoint if end <= pos: lend = l - (pos - end) # Send and loop to next range RESPONSE.write(data[lstart:lend]) break # Not yet at the end, transmit what we have. RESPONSE.write(data[lstart:]) data = data.next # Store a reference to a Pdata chain link so we # don't have to deref during this request again. pdata_map[pos] = data # Do not keep the link references around. del pdata_map RESPONSE.write('\r\n--%s--\r\n' % boundary) return True
def index_html(self, icon=0, preview=0, width=None, height=None, REQUEST=None): """ Return the file with it's corresponding MIME-type """ if REQUEST is not None: if self._if_modified_since_request_handler(REQUEST): self.ZCacheable_set(None) return '' if self._redirect_default_view_request_handler( icon, preview, REQUEST): return '' filename, content_type, icon, preview = self._get_file_to_serve( icon, preview) filename = self._get_fsname(filename) if _debug > 1: logger.info('serving %s, %s, %s, %s' % (filename, content_type, icon, preview)) if filename: size = os.stat(filename)[6] else: filename = self._get_icon_file(broken=True) size = os.stat(filename)[6] content_type = 'image/gif' icon = 1 if icon == 0 and width is not None and height is not None: data = TemporaryFile() # hold resized image try: from PIL import Image im = Image.open(filename) if im.mode != 'RGB': im = im.convert('RGB') filter = Image.BICUBIC if hasattr(Image, 'ANTIALIAS'): # PIL 1.1.3 filter = Image.ANTIALIAS im = im.resize((int(width), int(height)), filter) im.save(data, 'JPEG', quality=85) except: data = open(filename, 'rb') else: data.seek(0, 2) size = data.tell() data.seek(0) content_type = 'image/jpeg' else: data = open(filename, 'rb') if REQUEST is not None: last_mod = rfc1123_date(self._p_mtime) REQUEST.RESPONSE.setHeader('Last-Modified', last_mod) REQUEST.RESPONSE.setHeader('Content-Type', content_type) REQUEST.RESPONSE.setHeader('Content-Length', size) self.ZCacheable_set(None) return stream_iterator(data) try: return data.read() finally: data.close()