Package web2py :: Package gluon :: Module streamer
[hide private]
[frames] | no frames]

Source Code for Module web2py.gluon.streamer

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3   
  4  """ 
  5  This file is part of the web2py Web Framework 
  6  Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu> 
  7  License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) 
  8  """ 
  9   
 10  import os 
 11  import stat 
 12  import time 
 13  import re 
 14  import errno 
 15  import rewrite 
 16  from http import HTTP 
 17  from contenttype import contenttype 
 18   
 19   
 20  regex_start_range = re.compile('\d+(?=\-)') 
 21  regex_stop_range = re.compile('(?<=\-)\d+') 
 22   
 23  DEFAULT_CHUNK_SIZE = 64*1024 
 24   
25 -def streamer(stream, chunk_size = DEFAULT_CHUNK_SIZE, bytes = None):
26 offset = 0 27 while bytes == None or offset < bytes: 28 if bytes != None and bytes - offset < chunk_size: 29 chunk_size = bytes - offset 30 data = stream.read(chunk_size) 31 length = len(data) 32 if not length: 33 break 34 else: 35 yield data 36 if length < chunk_size: 37 break 38 offset += length 39 stream.close()
40
41 -def stream_file_or_304_or_206( 42 static_file, 43 chunk_size = DEFAULT_CHUNK_SIZE, 44 request = None, 45 headers = {}, 46 error_message = None, 47 ):
48 if error_message is None: 49 error_message = rewrite.thread.routes.error_message % 'invalid request' 50 try: 51 fp = open(static_file) 52 except IOError, e: 53 if e[0] == errno.EISDIR: 54 raise HTTP(403, error_message, web2py_error='file is a directory') 55 elif e[0] == errno.EACCES: 56 raise HTTP(403, error_message, web2py_error='inaccessible file') 57 else: 58 raise HTTP(404, error_message, web2py_error='invalid file') 59 else: 60 fp.close() 61 stat_file = os.stat(static_file) 62 fsize = stat_file[stat.ST_SIZE] 63 mtime = time.strftime('%a, %d %b %Y %H:%M:%S GMT', 64 time.gmtime(stat_file[stat.ST_MTIME])) 65 headers['Content-Type'] = contenttype(static_file) 66 headers['Last-Modified'] = mtime 67 headers['Pragma'] = 'cache' 68 headers['Cache-Control'] = 'private' 69 70 if request and request.env.http_if_modified_since == mtime: 71 raise HTTP(304, **{'Content-Type': headers['Content-Type']}) 72 73 elif request and request.env.http_range: 74 start_items = regex_start_range.findall(request.env.http_range) 75 if not start_items: 76 start_items = [0] 77 stop_items = regex_stop_range.findall(request.env.http_range) 78 if not stop_items or int(stop_items[0]) > fsize - 1: 79 stop_items = [fsize - 1] 80 part = (int(start_items[0]), int(stop_items[0]), fsize) 81 bytes = part[1] - part[0] + 1 82 try: 83 stream = open(static_file, 'rb') 84 except IOError, e: 85 if e[0] in (errno.EISDIR, errno.EACCES): 86 raise HTTP(403) 87 else: 88 raise HTTP(404) 89 stream.seek(part[0]) 90 headers['Content-Range'] = 'bytes %i-%i/%i' % part 91 headers['Content-Length'] = '%i' % bytes 92 status = 206 93 else: 94 try: 95 stream = open(static_file, 'rb') 96 except IOError, e: 97 if e[0] in (errno.EISDIR, errno.EACCES): 98 raise HTTP(403) 99 else: 100 raise HTTP(404) 101 headers['Content-Length'] = fsize 102 bytes = None 103 status = 200 104 if request and request.env.web2py_use_wsgi_file_wrapper: 105 wrapped = request.env.wsgi_file_wrapper(stream, chunk_size) 106 else: 107 wrapped = streamer(stream, chunk_size=chunk_size, bytes=bytes) 108 raise HTTP(status, wrapped, **headers)
109