source: OpenRLabs-Git/deploy/rlabs-docker/web2py-rlabs/gluon/streamer.py

main
Last change on this file was 42bd667, checked in by David Fuertes <dfuertes@…>, 4 years ago

Historial Limpio

  • Property mode set to 100755
File size: 4.6 KB
Line 
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
9Facilities to handle file streaming
10------------------------------------
11"""
12
13import os
14import stat
15import time
16import re
17import errno
18from gluon.http import HTTP
19from gluon.utils import unlocalised_http_header_date
20from gluon.contenttype import contenttype
21from gluon._compat import PY2
22
23
24regex_start_range = re.compile('\d+(?=\-)')
25regex_stop_range = re.compile('(?<=\-)\d+')
26
27DEFAULT_CHUNK_SIZE = 64 * 1024
28
29def streamer(stream, chunk_size=DEFAULT_CHUNK_SIZE, bytes=None, callback=None):
30    try:
31        offset = 0
32        while bytes is None or offset < bytes:
33            if not bytes is None and bytes - offset < chunk_size:
34                chunk_size = bytes - offset
35            data = stream.read(chunk_size)
36            length = len(data)
37            if not length:
38                break
39            else:
40                yield data
41            if length < chunk_size:
42                break
43            offset += length
44    finally:
45        stream.close()
46        if callback:
47            callback()
48
49def stream_file_or_304_or_206(
50    static_file,
51    chunk_size=DEFAULT_CHUNK_SIZE,
52    request=None,
53    headers={},
54    status=200,
55    error_message=None
56    ):
57    # FIX THIS
58    # if error_message is None:
59    #     error_message = rewrite.THREAD_LOCAL.routes.error_message % 'invalid request'
60    try:
61        if PY2:
62            open_f = file # this makes no sense but without it GAE cannot open files
63        else:
64            open_f = open
65        fp = open_f(static_file,'rb')
66    except IOError as e:
67        if e.errno == errno.EISDIR:
68            raise HTTP(403, error_message, web2py_error='file is a directory')
69        elif e.errno == errno.EACCES:
70            raise HTTP(403, error_message, web2py_error='inaccessible file')
71        else:
72            raise HTTP(404, error_message, web2py_error='invalid file')
73    else:
74        fp.close()
75    stat_file = os.stat(static_file)
76    fsize = stat_file[stat.ST_SIZE]
77    modified = stat_file[stat.ST_MTIME]
78    mtime = unlocalised_http_header_date(time.gmtime(modified))
79    headers.setdefault('Content-Type', contenttype(static_file))
80    headers.setdefault('Last-Modified', mtime)
81    headers.setdefault('Pragma', 'cache')
82    headers.setdefault('Cache-Control', 'private')
83
84    # if this is a normal response and not a respnse to an error page
85    if status == 200:
86        if request and request.env.http_if_modified_since == mtime:
87            raise HTTP(304, **{'Content-Type': headers['Content-Type']})
88
89        elif request and request.env.http_range:
90            start_items = regex_start_range.findall(request.env.http_range)
91            if not start_items:
92                start_items = [0]
93            stop_items = regex_stop_range.findall(request.env.http_range)
94            if not stop_items or int(stop_items[0]) > fsize - 1:
95                stop_items = [fsize - 1]
96            part = (int(start_items[0]), int(stop_items[0]), fsize)
97            bytes = part[1] - part[0] + 1
98            try:
99                stream = open(static_file, 'rb')
100            except IOError as e:
101                if e.errno in (errno.EISDIR, errno.EACCES):
102                    raise HTTP(403)
103                else:
104                    raise HTTP(404)
105            stream.seek(part[0])
106            headers['Content-Range'] = 'bytes %i-%i/%i' % part
107            headers['Content-Length'] = '%i' % bytes
108            status = 206
109    # in all the other cases (not 304, not 206, but 200 or error page)
110    if status != 206:
111        enc = request.env.http_accept_encoding
112        if enc and 'gzip' in enc and not 'Content-Encoding' in headers:
113            gzipped = static_file + '.gz'
114            if os.path.isfile(gzipped) and os.path.getmtime(gzipped) >= modified:
115                static_file = gzipped
116                fsize = os.path.getsize(gzipped)
117                headers['Content-Encoding'] = 'gzip'
118                headers['Vary'] = 'Accept-Encoding'
119        try:
120            stream = open(static_file, 'rb')
121        except IOError as e:
122            # this better not happen when returning an error page ;-)
123            if e.errno in (errno.EISDIR, errno.EACCES):
124                raise HTTP(403)
125            else:
126                raise HTTP(404)
127        headers['Content-Length'] = fsize
128        bytes = None
129    if request and request.env.web2py_use_wsgi_file_wrapper:
130        wrapped = request.env.wsgi_file_wrapper(stream, chunk_size)
131    else:
132        wrapped = streamer(stream, chunk_size=chunk_size, bytes=bytes)
133    raise HTTP(status, wrapped, **headers)
Note: See TracBrowser for help on using the repository browser.